aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/access.c156
-rw-r--r--drivers/pci/pci-sysfs.c38
-rw-r--r--drivers/pci/pci.h6
-rw-r--r--include/linux/pci.h4
4 files changed, 114 insertions, 90 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 98ddba94b5b9..86ec4ad44bcd 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -66,6 +66,39 @@ EXPORT_SYMBOL(pci_bus_write_config_byte);
66EXPORT_SYMBOL(pci_bus_write_config_word); 66EXPORT_SYMBOL(pci_bus_write_config_word);
67EXPORT_SYMBOL(pci_bus_write_config_dword); 67EXPORT_SYMBOL(pci_bus_write_config_dword);
68 68
69
70/**
71 * pci_read_vpd - Read one entry from Vital Product Data
72 * @dev: pci device struct
73 * @pos: offset in vpd space
74 * @count: number of bytes to read
75 * @buf: pointer to where to store result
76 *
77 */
78ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
79{
80 if (!dev->vpd || !dev->vpd->ops)
81 return -ENODEV;
82 return dev->vpd->ops->read(dev, pos, count, buf);
83}
84EXPORT_SYMBOL(pci_read_vpd);
85
86/**
87 * pci_write_vpd - Write entry to Vital Product Data
88 * @dev: pci device struct
89 * @pos: offset in vpd space
90 * @count: number of bytes to read
91 * @val: value to write
92 *
93 */
94ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
95{
96 if (!dev->vpd || !dev->vpd->ops)
97 return -ENODEV;
98 return dev->vpd->ops->write(dev, pos, count, buf);
99}
100EXPORT_SYMBOL(pci_write_vpd);
101
69/* 102/*
70 * The following routines are to prevent the user from accessing PCI config 103 * The following routines are to prevent the user from accessing PCI config
71 * space when it's unsafe to do so. Some devices require this during BIST and 104 * space when it's unsafe to do so. Some devices require this during BIST and
@@ -176,19 +209,17 @@ static int pci_vpd_pci22_wait(struct pci_dev *dev)
176 } 209 }
177} 210}
178 211
179static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, 212static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count,
180 char *buf) 213 void *arg)
181{ 214{
182 struct pci_vpd_pci22 *vpd = 215 struct pci_vpd_pci22 *vpd =
183 container_of(dev->vpd, struct pci_vpd_pci22, base); 216 container_of(dev->vpd, struct pci_vpd_pci22, base);
184 u32 val; 217 int ret;
185 int ret = 0; 218 loff_t end = pos + count;
186 int begin, end, i; 219 u8 *buf = arg;
187 220
188 if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos) 221 if (pos < 0 || pos > vpd->base.len || end > vpd->base.len)
189 return -EINVAL; 222 return -EINVAL;
190 if (size == 0)
191 return 0;
192 223
193 if (mutex_lock_killable(&vpd->lock)) 224 if (mutex_lock_killable(&vpd->lock))
194 return -EINTR; 225 return -EINTR;
@@ -196,73 +227,84 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size,
196 ret = pci_vpd_pci22_wait(dev); 227 ret = pci_vpd_pci22_wait(dev);
197 if (ret < 0) 228 if (ret < 0)
198 goto out; 229 goto out;
199 ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
200 pos & ~3);
201 if (ret < 0)
202 goto out;
203 230
204 vpd->busy = true; 231 while (pos < end) {
205 vpd->flag = PCI_VPD_ADDR_F; 232 u32 val;
206 ret = pci_vpd_pci22_wait(dev); 233 unsigned int i, skip;
207 if (ret < 0) 234
208 goto out; 235 ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
209 ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, 236 pos & ~3);
210 &val); 237 if (ret < 0)
238 break;
239 vpd->busy = true;
240 vpd->flag = PCI_VPD_ADDR_F;
241 ret = pci_vpd_pci22_wait(dev);
242 if (ret < 0)
243 break;
244
245 ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val);
246 if (ret < 0)
247 break;
248
249 skip = pos & 3;
250 for (i = 0; i < sizeof(u32); i++) {
251 if (i >= skip) {
252 *buf++ = val;
253 if (++pos == end)
254 break;
255 }
256 val >>= 8;
257 }
258 }
211out: 259out:
212 mutex_unlock(&vpd->lock); 260 mutex_unlock(&vpd->lock);
213 if (ret < 0) 261 return ret ? ret : count;
214 return ret;
215
216 /* Convert to bytes */
217 begin = pos & 3;
218 end = min(4, begin + size);
219 for (i = 0; i < end; ++i) {
220 if (i >= begin)
221 *buf++ = val;
222 val >>= 8;
223 }
224 return end - begin;
225} 262}
226 263
227static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, 264static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count,
228 const char *buf) 265 const void *arg)
229{ 266{
230 struct pci_vpd_pci22 *vpd = 267 struct pci_vpd_pci22 *vpd =
231 container_of(dev->vpd, struct pci_vpd_pci22, base); 268 container_of(dev->vpd, struct pci_vpd_pci22, base);
232 u32 val; 269 const u8 *buf = arg;
270 loff_t end = pos + count;
233 int ret = 0; 271 int ret = 0;
234 272
235 if (pos < 0 || pos > vpd->base.len || pos & 3 || 273 if (pos < 0 || (pos & 3) || (count & 3) || end > vpd->base.len)
236 size > vpd->base.len - pos || size < 4)
237 return -EINVAL; 274 return -EINVAL;
238 275
239 val = (u8) *buf++;
240 val |= ((u8) *buf++) << 8;
241 val |= ((u8) *buf++) << 16;
242 val |= ((u32)(u8) *buf++) << 24;
243
244 if (mutex_lock_killable(&vpd->lock)) 276 if (mutex_lock_killable(&vpd->lock))
245 return -EINTR; 277 return -EINTR;
278
246 ret = pci_vpd_pci22_wait(dev); 279 ret = pci_vpd_pci22_wait(dev);
247 if (ret < 0) 280 if (ret < 0)
248 goto out; 281 goto out;
249 ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, 282
250 val); 283 while (pos < end) {
251 if (ret < 0) 284 u32 val;
252 goto out; 285
253 ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, 286 val = *buf++;
254 pos | PCI_VPD_ADDR_F); 287 val |= *buf++ << 8;
255 if (ret < 0) 288 val |= *buf++ << 16;
256 goto out; 289 val |= *buf++ << 24;
257 vpd->busy = true; 290
258 vpd->flag = 0; 291 ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val);
259 ret = pci_vpd_pci22_wait(dev); 292 if (ret < 0)
293 break;
294 ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
295 pos | PCI_VPD_ADDR_F);
296 if (ret < 0)
297 break;
298
299 vpd->busy = true;
300 vpd->flag = 0;
301 ret = pci_vpd_pci22_wait(dev);
302
303 pos += sizeof(u32);
304 }
260out: 305out:
261 mutex_unlock(&vpd->lock); 306 mutex_unlock(&vpd->lock);
262 if (ret < 0) 307 return ret ? ret : count;
263 return ret;
264
265 return 4;
266} 308}
267 309
268static void pci_vpd_pci22_release(struct pci_dev *dev) 310static void pci_vpd_pci22_release(struct pci_dev *dev)
@@ -270,7 +312,7 @@ static void pci_vpd_pci22_release(struct pci_dev *dev)
270 kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); 312 kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
271} 313}
272 314
273static struct pci_vpd_ops pci_vpd_pci22_ops = { 315static const struct pci_vpd_ops pci_vpd_pci22_ops = {
274 .read = pci_vpd_pci22_read, 316 .read = pci_vpd_pci22_read,
275 .write = pci_vpd_pci22_write, 317 .write = pci_vpd_pci22_write,
276 .release = pci_vpd_pci22_release, 318 .release = pci_vpd_pci22_release,
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index ea54cedcdfc6..c23619fb6c4b 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -371,55 +371,33 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr,
371} 371}
372 372
373static ssize_t 373static ssize_t
374pci_read_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, 374read_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr,
375 char *buf, loff_t off, size_t count) 375 char *buf, loff_t off, size_t count)
376{ 376{
377 struct pci_dev *dev = 377 struct pci_dev *dev =
378 to_pci_dev(container_of(kobj, struct device, kobj)); 378 to_pci_dev(container_of(kobj, struct device, kobj));
379 int end;
380 int ret;
381 379
382 if (off > bin_attr->size) 380 if (off > bin_attr->size)
383 count = 0; 381 count = 0;
384 else if (count > bin_attr->size - off) 382 else if (count > bin_attr->size - off)
385 count = bin_attr->size - off; 383 count = bin_attr->size - off;
386 end = off + count;
387
388 while (off < end) {
389 ret = dev->vpd->ops->read(dev, off, end - off, buf);
390 if (ret < 0)
391 return ret;
392 buf += ret;
393 off += ret;
394 }
395 384
396 return count; 385 return pci_read_vpd(dev, off, count, buf);
397} 386}
398 387
399static ssize_t 388static ssize_t
400pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, 389write_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr,
401 char *buf, loff_t off, size_t count) 390 char *buf, loff_t off, size_t count)
402{ 391{
403 struct pci_dev *dev = 392 struct pci_dev *dev =
404 to_pci_dev(container_of(kobj, struct device, kobj)); 393 to_pci_dev(container_of(kobj, struct device, kobj));
405 int end;
406 int ret;
407 394
408 if (off > bin_attr->size) 395 if (off > bin_attr->size)
409 count = 0; 396 count = 0;
410 else if (count > bin_attr->size - off) 397 else if (count > bin_attr->size - off)
411 count = bin_attr->size - off; 398 count = bin_attr->size - off;
412 end = off + count;
413
414 while (off < end) {
415 ret = dev->vpd->ops->write(dev, off, end - off, buf);
416 if (ret < 0)
417 return ret;
418 buf += ret;
419 off += ret;
420 }
421 399
422 return count; 400 return pci_write_vpd(dev, off, count, buf);
423} 401}
424 402
425#ifdef HAVE_PCI_LEGACY 403#ifdef HAVE_PCI_LEGACY
@@ -845,8 +823,8 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev)
845 attr->size = dev->vpd->len; 823 attr->size = dev->vpd->len;
846 attr->attr.name = "vpd"; 824 attr->attr.name = "vpd";
847 attr->attr.mode = S_IRUSR | S_IWUSR; 825 attr->attr.mode = S_IRUSR | S_IWUSR;
848 attr->read = pci_read_vpd; 826 attr->read = read_vpd_attr;
849 attr->write = pci_write_vpd; 827 attr->write = write_vpd_attr;
850 retval = sysfs_create_bin_file(&dev->dev.kobj, attr); 828 retval = sysfs_create_bin_file(&dev->dev.kobj, attr);
851 if (retval) { 829 if (retval) {
852 kfree(dev->vpd->attr); 830 kfree(dev->vpd->attr);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 65deed8bfc06..211fd418f48f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -56,14 +56,14 @@ extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val);
56extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); 56extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
57 57
58struct pci_vpd_ops { 58struct pci_vpd_ops {
59 int (*read)(struct pci_dev *dev, int pos, int size, char *buf); 59 ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
60 int (*write)(struct pci_dev *dev, int pos, int size, const char *buf); 60 ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
61 void (*release)(struct pci_dev *dev); 61 void (*release)(struct pci_dev *dev);
62}; 62};
63 63
64struct pci_vpd { 64struct pci_vpd {
65 unsigned int len; 65 unsigned int len;
66 struct pci_vpd_ops *ops; 66 const struct pci_vpd_ops *ops;
67 struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ 67 struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
68}; 68};
69 69
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 170f9ae2d8a0..76079e106895 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -687,6 +687,10 @@ int pci_back_from_sleep(struct pci_dev *dev);
687/* Functions for PCI Hotplug drivers to use */ 687/* Functions for PCI Hotplug drivers to use */
688int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); 688int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);
689 689
690/* Vital product data routines */
691ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
692ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
693
690/* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ 694/* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */
691void pci_bus_assign_resources(struct pci_bus *bus); 695void pci_bus_assign_resources(struct pci_bus *bus);
692void pci_bus_size_bridges(struct pci_bus *bus); 696void pci_bus_size_bridges(struct pci_bus *bus);