diff options
-rw-r--r-- | drivers/pci/access.c | 156 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 38 | ||||
-rw-r--r-- | drivers/pci/pci.h | 6 | ||||
-rw-r--r-- | include/linux/pci.h | 4 |
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); | |||
66 | EXPORT_SYMBOL(pci_bus_write_config_word); | 66 | EXPORT_SYMBOL(pci_bus_write_config_word); |
67 | EXPORT_SYMBOL(pci_bus_write_config_dword); | 67 | EXPORT_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 | */ | ||
78 | ssize_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 | } | ||
84 | EXPORT_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 | */ | ||
94 | ssize_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 | } | ||
100 | EXPORT_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 | ||
179 | static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, | 212 | static 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 | } | ||
211 | out: | 259 | out: |
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 | ||
227 | static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, | 264 | static 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 | } | ||
260 | out: | 305 | out: |
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 | ||
268 | static void pci_vpd_pci22_release(struct pci_dev *dev) | 310 | static 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 | ||
273 | static struct pci_vpd_ops pci_vpd_pci22_ops = { | 315 | static 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 | ||
373 | static ssize_t | 373 | static ssize_t |
374 | pci_read_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, | 374 | read_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 | ||
399 | static ssize_t | 388 | static ssize_t |
400 | pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, | 389 | write_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); | |||
56 | extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); | 56 | extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); |
57 | 57 | ||
58 | struct pci_vpd_ops { | 58 | struct 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 | ||
64 | struct pci_vpd { | 64 | struct 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 */ |
688 | int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); | 688 | int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); |
689 | 689 | ||
690 | /* Vital product data routines */ | ||
691 | ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); | ||
692 | ssize_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) */ |
691 | void pci_bus_assign_resources(struct pci_bus *bus); | 695 | void pci_bus_assign_resources(struct pci_bus *bus); |
692 | void pci_bus_size_bridges(struct pci_bus *bus); | 696 | void pci_bus_size_bridges(struct pci_bus *bus); |