Root/
1 | /* |
2 | * drivers/pci/pci-sysfs.c |
3 | * |
4 | * (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com> |
5 | * (C) Copyright 2002-2004 IBM Corp. |
6 | * (C) Copyright 2003 Matthew Wilcox |
7 | * (C) Copyright 2003 Hewlett-Packard |
8 | * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com> |
9 | * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com> |
10 | * |
11 | * File attributes for PCI devices |
12 | * |
13 | * Modeled after usb's driverfs.c |
14 | * |
15 | */ |
16 | |
17 | |
18 | #include <linux/kernel.h> |
19 | #include <linux/sched.h> |
20 | #include <linux/pci.h> |
21 | #include <linux/stat.h> |
22 | #include <linux/export.h> |
23 | #include <linux/topology.h> |
24 | #include <linux/mm.h> |
25 | #include <linux/fs.h> |
26 | #include <linux/capability.h> |
27 | #include <linux/security.h> |
28 | #include <linux/pci-aspm.h> |
29 | #include <linux/slab.h> |
30 | #include <linux/vgaarb.h> |
31 | #include <linux/pm_runtime.h> |
32 | #include "pci.h" |
33 | |
34 | static int sysfs_initialized; /* = 0 */ |
35 | |
36 | /* show configuration fields */ |
37 | #define pci_config_attr(field, format_string) \ |
38 | static ssize_t \ |
39 | field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ |
40 | { \ |
41 | struct pci_dev *pdev; \ |
42 | \ |
43 | pdev = to_pci_dev (dev); \ |
44 | return sprintf (buf, format_string, pdev->field); \ |
45 | } |
46 | |
47 | pci_config_attr(vendor, "0x%04x\n"); |
48 | pci_config_attr(device, "0x%04x\n"); |
49 | pci_config_attr(subsystem_vendor, "0x%04x\n"); |
50 | pci_config_attr(subsystem_device, "0x%04x\n"); |
51 | pci_config_attr(class, "0x%06x\n"); |
52 | pci_config_attr(irq, "%u\n"); |
53 | |
54 | static ssize_t broken_parity_status_show(struct device *dev, |
55 | struct device_attribute *attr, |
56 | char *buf) |
57 | { |
58 | struct pci_dev *pdev = to_pci_dev(dev); |
59 | return sprintf (buf, "%u\n", pdev->broken_parity_status); |
60 | } |
61 | |
62 | static ssize_t broken_parity_status_store(struct device *dev, |
63 | struct device_attribute *attr, |
64 | const char *buf, size_t count) |
65 | { |
66 | struct pci_dev *pdev = to_pci_dev(dev); |
67 | unsigned long val; |
68 | |
69 | if (strict_strtoul(buf, 0, &val) < 0) |
70 | return -EINVAL; |
71 | |
72 | pdev->broken_parity_status = !!val; |
73 | |
74 | return count; |
75 | } |
76 | |
77 | static ssize_t local_cpus_show(struct device *dev, |
78 | struct device_attribute *attr, char *buf) |
79 | { |
80 | const struct cpumask *mask; |
81 | int len; |
82 | |
83 | #ifdef CONFIG_NUMA |
84 | mask = (dev_to_node(dev) == -1) ? cpu_online_mask : |
85 | cpumask_of_node(dev_to_node(dev)); |
86 | #else |
87 | mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); |
88 | #endif |
89 | len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask); |
90 | buf[len++] = '\n'; |
91 | buf[len] = '\0'; |
92 | return len; |
93 | } |
94 | |
95 | |
96 | static ssize_t local_cpulist_show(struct device *dev, |
97 | struct device_attribute *attr, char *buf) |
98 | { |
99 | const struct cpumask *mask; |
100 | int len; |
101 | |
102 | #ifdef CONFIG_NUMA |
103 | mask = (dev_to_node(dev) == -1) ? cpu_online_mask : |
104 | cpumask_of_node(dev_to_node(dev)); |
105 | #else |
106 | mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); |
107 | #endif |
108 | len = cpulist_scnprintf(buf, PAGE_SIZE-2, mask); |
109 | buf[len++] = '\n'; |
110 | buf[len] = '\0'; |
111 | return len; |
112 | } |
113 | |
114 | /* |
115 | * PCI Bus Class Devices |
116 | */ |
117 | static ssize_t pci_bus_show_cpuaffinity(struct device *dev, |
118 | int type, |
119 | struct device_attribute *attr, |
120 | char *buf) |
121 | { |
122 | int ret; |
123 | const struct cpumask *cpumask; |
124 | |
125 | cpumask = cpumask_of_pcibus(to_pci_bus(dev)); |
126 | ret = type ? |
127 | cpulist_scnprintf(buf, PAGE_SIZE-2, cpumask) : |
128 | cpumask_scnprintf(buf, PAGE_SIZE-2, cpumask); |
129 | buf[ret++] = '\n'; |
130 | buf[ret] = '\0'; |
131 | return ret; |
132 | } |
133 | |
134 | static inline ssize_t pci_bus_show_cpumaskaffinity(struct device *dev, |
135 | struct device_attribute *attr, |
136 | char *buf) |
137 | { |
138 | return pci_bus_show_cpuaffinity(dev, 0, attr, buf); |
139 | } |
140 | |
141 | static inline ssize_t pci_bus_show_cpulistaffinity(struct device *dev, |
142 | struct device_attribute *attr, |
143 | char *buf) |
144 | { |
145 | return pci_bus_show_cpuaffinity(dev, 1, attr, buf); |
146 | } |
147 | |
148 | /* show resources */ |
149 | static ssize_t |
150 | resource_show(struct device * dev, struct device_attribute *attr, char * buf) |
151 | { |
152 | struct pci_dev * pci_dev = to_pci_dev(dev); |
153 | char * str = buf; |
154 | int i; |
155 | int max; |
156 | resource_size_t start, end; |
157 | |
158 | if (pci_dev->subordinate) |
159 | max = DEVICE_COUNT_RESOURCE; |
160 | else |
161 | max = PCI_BRIDGE_RESOURCES; |
162 | |
163 | for (i = 0; i < max; i++) { |
164 | struct resource *res = &pci_dev->resource[i]; |
165 | pci_resource_to_user(pci_dev, i, res, &start, &end); |
166 | str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx\n", |
167 | (unsigned long long)start, |
168 | (unsigned long long)end, |
169 | (unsigned long long)res->flags); |
170 | } |
171 | return (str - buf); |
172 | } |
173 | |
174 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) |
175 | { |
176 | struct pci_dev *pci_dev = to_pci_dev(dev); |
177 | |
178 | return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x\n", |
179 | pci_dev->vendor, pci_dev->device, |
180 | pci_dev->subsystem_vendor, pci_dev->subsystem_device, |
181 | (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), |
182 | (u8)(pci_dev->class)); |
183 | } |
184 | |
185 | static ssize_t is_enabled_store(struct device *dev, |
186 | struct device_attribute *attr, const char *buf, |
187 | size_t count) |
188 | { |
189 | struct pci_dev *pdev = to_pci_dev(dev); |
190 | unsigned long val; |
191 | ssize_t result = strict_strtoul(buf, 0, &val); |
192 | |
193 | if (result < 0) |
194 | return result; |
195 | |
196 | /* this can crash the machine when done on the "wrong" device */ |
197 | if (!capable(CAP_SYS_ADMIN)) |
198 | return -EPERM; |
199 | |
200 | if (!val) { |
201 | if (pci_is_enabled(pdev)) |
202 | pci_disable_device(pdev); |
203 | else |
204 | result = -EIO; |
205 | } else |
206 | result = pci_enable_device(pdev); |
207 | |
208 | return result < 0 ? result : count; |
209 | } |
210 | |
211 | static ssize_t is_enabled_show(struct device *dev, |
212 | struct device_attribute *attr, char *buf) |
213 | { |
214 | struct pci_dev *pdev; |
215 | |
216 | pdev = to_pci_dev (dev); |
217 | return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt)); |
218 | } |
219 | |
220 | #ifdef CONFIG_NUMA |
221 | static ssize_t |
222 | numa_node_show(struct device *dev, struct device_attribute *attr, char *buf) |
223 | { |
224 | return sprintf (buf, "%d\n", dev->numa_node); |
225 | } |
226 | #endif |
227 | |
228 | static ssize_t |
229 | dma_mask_bits_show(struct device *dev, struct device_attribute *attr, char *buf) |
230 | { |
231 | struct pci_dev *pdev = to_pci_dev(dev); |
232 | |
233 | return sprintf (buf, "%d\n", fls64(pdev->dma_mask)); |
234 | } |
235 | |
236 | static ssize_t |
237 | consistent_dma_mask_bits_show(struct device *dev, struct device_attribute *attr, |
238 | char *buf) |
239 | { |
240 | return sprintf (buf, "%d\n", fls64(dev->coherent_dma_mask)); |
241 | } |
242 | |
243 | static ssize_t |
244 | msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf) |
245 | { |
246 | struct pci_dev *pdev = to_pci_dev(dev); |
247 | |
248 | if (!pdev->subordinate) |
249 | return 0; |
250 | |
251 | return sprintf (buf, "%u\n", |
252 | !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI)); |
253 | } |
254 | |
255 | static ssize_t |
256 | msi_bus_store(struct device *dev, struct device_attribute *attr, |
257 | const char *buf, size_t count) |
258 | { |
259 | struct pci_dev *pdev = to_pci_dev(dev); |
260 | unsigned long val; |
261 | |
262 | if (strict_strtoul(buf, 0, &val) < 0) |
263 | return -EINVAL; |
264 | |
265 | /* bad things may happen if the no_msi flag is changed |
266 | * while some drivers are loaded */ |
267 | if (!capable(CAP_SYS_ADMIN)) |
268 | return -EPERM; |
269 | |
270 | /* Maybe pci devices without subordinate busses shouldn't even have this |
271 | * attribute in the first place? */ |
272 | if (!pdev->subordinate) |
273 | return count; |
274 | |
275 | /* Is the flag going to change, or keep the value it already had? */ |
276 | if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^ |
277 | !!val) { |
278 | pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI; |
279 | |
280 | dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI," |
281 | " bad things could happen\n", val ? "" : " not"); |
282 | } |
283 | |
284 | return count; |
285 | } |
286 | |
287 | #ifdef CONFIG_HOTPLUG |
288 | static DEFINE_MUTEX(pci_remove_rescan_mutex); |
289 | static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, |
290 | size_t count) |
291 | { |
292 | unsigned long val; |
293 | struct pci_bus *b = NULL; |
294 | |
295 | if (strict_strtoul(buf, 0, &val) < 0) |
296 | return -EINVAL; |
297 | |
298 | if (val) { |
299 | mutex_lock(&pci_remove_rescan_mutex); |
300 | while ((b = pci_find_next_bus(b)) != NULL) |
301 | pci_rescan_bus(b); |
302 | mutex_unlock(&pci_remove_rescan_mutex); |
303 | } |
304 | return count; |
305 | } |
306 | |
307 | struct bus_attribute pci_bus_attrs[] = { |
308 | __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store), |
309 | __ATTR_NULL |
310 | }; |
311 | |
312 | static ssize_t |
313 | dev_rescan_store(struct device *dev, struct device_attribute *attr, |
314 | const char *buf, size_t count) |
315 | { |
316 | unsigned long val; |
317 | struct pci_dev *pdev = to_pci_dev(dev); |
318 | |
319 | if (strict_strtoul(buf, 0, &val) < 0) |
320 | return -EINVAL; |
321 | |
322 | if (val) { |
323 | mutex_lock(&pci_remove_rescan_mutex); |
324 | pci_rescan_bus(pdev->bus); |
325 | mutex_unlock(&pci_remove_rescan_mutex); |
326 | } |
327 | return count; |
328 | } |
329 | |
330 | static void remove_callback(struct device *dev) |
331 | { |
332 | struct pci_dev *pdev = to_pci_dev(dev); |
333 | |
334 | mutex_lock(&pci_remove_rescan_mutex); |
335 | pci_stop_and_remove_bus_device(pdev); |
336 | mutex_unlock(&pci_remove_rescan_mutex); |
337 | } |
338 | |
339 | static ssize_t |
340 | remove_store(struct device *dev, struct device_attribute *dummy, |
341 | const char *buf, size_t count) |
342 | { |
343 | int ret = 0; |
344 | unsigned long val; |
345 | |
346 | if (strict_strtoul(buf, 0, &val) < 0) |
347 | return -EINVAL; |
348 | |
349 | /* An attribute cannot be unregistered by one of its own methods, |
350 | * so we have to use this roundabout approach. |
351 | */ |
352 | if (val) |
353 | ret = device_schedule_callback(dev, remove_callback); |
354 | if (ret) |
355 | count = ret; |
356 | return count; |
357 | } |
358 | |
359 | static ssize_t |
360 | dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, |
361 | const char *buf, size_t count) |
362 | { |
363 | unsigned long val; |
364 | struct pci_bus *bus = to_pci_bus(dev); |
365 | |
366 | if (strict_strtoul(buf, 0, &val) < 0) |
367 | return -EINVAL; |
368 | |
369 | if (val) { |
370 | mutex_lock(&pci_remove_rescan_mutex); |
371 | if (!pci_is_root_bus(bus) && list_empty(&bus->devices)) |
372 | pci_rescan_bus_bridge_resize(bus->self); |
373 | else |
374 | pci_rescan_bus(bus); |
375 | mutex_unlock(&pci_remove_rescan_mutex); |
376 | } |
377 | return count; |
378 | } |
379 | |
380 | #endif |
381 | |
382 | #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) |
383 | static ssize_t d3cold_allowed_store(struct device *dev, |
384 | struct device_attribute *attr, |
385 | const char *buf, size_t count) |
386 | { |
387 | struct pci_dev *pdev = to_pci_dev(dev); |
388 | unsigned long val; |
389 | |
390 | if (strict_strtoul(buf, 0, &val) < 0) |
391 | return -EINVAL; |
392 | |
393 | pdev->d3cold_allowed = !!val; |
394 | pm_runtime_resume(dev); |
395 | |
396 | return count; |
397 | } |
398 | |
399 | static ssize_t d3cold_allowed_show(struct device *dev, |
400 | struct device_attribute *attr, char *buf) |
401 | { |
402 | struct pci_dev *pdev = to_pci_dev(dev); |
403 | return sprintf (buf, "%u\n", pdev->d3cold_allowed); |
404 | } |
405 | #endif |
406 | |
407 | struct device_attribute pci_dev_attrs[] = { |
408 | __ATTR_RO(resource), |
409 | __ATTR_RO(vendor), |
410 | __ATTR_RO(device), |
411 | __ATTR_RO(subsystem_vendor), |
412 | __ATTR_RO(subsystem_device), |
413 | __ATTR_RO(class), |
414 | __ATTR_RO(irq), |
415 | __ATTR_RO(local_cpus), |
416 | __ATTR_RO(local_cpulist), |
417 | __ATTR_RO(modalias), |
418 | #ifdef CONFIG_NUMA |
419 | __ATTR_RO(numa_node), |
420 | #endif |
421 | __ATTR_RO(dma_mask_bits), |
422 | __ATTR_RO(consistent_dma_mask_bits), |
423 | __ATTR(enable, 0600, is_enabled_show, is_enabled_store), |
424 | __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), |
425 | broken_parity_status_show,broken_parity_status_store), |
426 | __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), |
427 | #ifdef CONFIG_HOTPLUG |
428 | __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), |
429 | __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), |
430 | #endif |
431 | #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) |
432 | __ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store), |
433 | #endif |
434 | __ATTR_NULL, |
435 | }; |
436 | |
437 | struct device_attribute pcibus_dev_attrs[] = { |
438 | #ifdef CONFIG_HOTPLUG |
439 | __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_bus_rescan_store), |
440 | #endif |
441 | __ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpumaskaffinity, NULL), |
442 | __ATTR(cpulistaffinity, S_IRUGO, pci_bus_show_cpulistaffinity, NULL), |
443 | __ATTR_NULL, |
444 | }; |
445 | |
446 | static ssize_t |
447 | boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf) |
448 | { |
449 | struct pci_dev *pdev = to_pci_dev(dev); |
450 | struct pci_dev *vga_dev = vga_default_device(); |
451 | |
452 | if (vga_dev) |
453 | return sprintf(buf, "%u\n", (pdev == vga_dev)); |
454 | |
455 | return sprintf(buf, "%u\n", |
456 | !!(pdev->resource[PCI_ROM_RESOURCE].flags & |
457 | IORESOURCE_ROM_SHADOW)); |
458 | } |
459 | struct device_attribute vga_attr = __ATTR_RO(boot_vga); |
460 | |
461 | static void |
462 | pci_config_pm_runtime_get(struct pci_dev *pdev) |
463 | { |
464 | struct device *dev = &pdev->dev; |
465 | struct device *parent = dev->parent; |
466 | |
467 | if (parent) |
468 | pm_runtime_get_sync(parent); |
469 | pm_runtime_get_noresume(dev); |
470 | /* |
471 | * pdev->current_state is set to PCI_D3cold during suspending, |
472 | * so wait until suspending completes |
473 | */ |
474 | pm_runtime_barrier(dev); |
475 | /* |
476 | * Only need to resume devices in D3cold, because config |
477 | * registers are still accessible for devices suspended but |
478 | * not in D3cold. |
479 | */ |
480 | if (pdev->current_state == PCI_D3cold) |
481 | pm_runtime_resume(dev); |
482 | } |
483 | |
484 | static void |
485 | pci_config_pm_runtime_put(struct pci_dev *pdev) |
486 | { |
487 | struct device *dev = &pdev->dev; |
488 | struct device *parent = dev->parent; |
489 | |
490 | pm_runtime_put(dev); |
491 | if (parent) |
492 | pm_runtime_put_sync(parent); |
493 | } |
494 | |
495 | static ssize_t |
496 | pci_read_config(struct file *filp, struct kobject *kobj, |
497 | struct bin_attribute *bin_attr, |
498 | char *buf, loff_t off, size_t count) |
499 | { |
500 | struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); |
501 | unsigned int size = 64; |
502 | loff_t init_off = off; |
503 | u8 *data = (u8*) buf; |
504 | |
505 | /* Several chips lock up trying to read undefined config space */ |
506 | if (security_capable(filp->f_cred, &init_user_ns, CAP_SYS_ADMIN) == 0) { |
507 | size = dev->cfg_size; |
508 | } else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) { |
509 | size = 128; |
510 | } |
511 | |
512 | if (off > size) |
513 | return 0; |
514 | if (off + count > size) { |
515 | size -= off; |
516 | count = size; |
517 | } else { |
518 | size = count; |
519 | } |
520 | |
521 | pci_config_pm_runtime_get(dev); |
522 | |
523 | if ((off & 1) && size) { |
524 | u8 val; |
525 | pci_user_read_config_byte(dev, off, &val); |
526 | data[off - init_off] = val; |
527 | off++; |
528 | size--; |
529 | } |
530 | |
531 | if ((off & 3) && size > 2) { |
532 | u16 val; |
533 | pci_user_read_config_word(dev, off, &val); |
534 | data[off - init_off] = val & 0xff; |
535 | data[off - init_off + 1] = (val >> 8) & 0xff; |
536 | off += 2; |
537 | size -= 2; |
538 | } |
539 | |
540 | while (size > 3) { |
541 | u32 val; |
542 | pci_user_read_config_dword(dev, off, &val); |
543 | data[off - init_off] = val & 0xff; |
544 | data[off - init_off + 1] = (val >> 8) & 0xff; |
545 | data[off - init_off + 2] = (val >> 16) & 0xff; |
546 | data[off - init_off + 3] = (val >> 24) & 0xff; |
547 | off += 4; |
548 | size -= 4; |
549 | } |
550 | |
551 | if (size >= 2) { |
552 | u16 val; |
553 | pci_user_read_config_word(dev, off, &val); |
554 | data[off - init_off] = val & 0xff; |
555 | data[off - init_off + 1] = (val >> 8) & 0xff; |
556 | off += 2; |
557 | size -= 2; |
558 | } |
559 | |
560 | if (size > 0) { |
561 | u8 val; |
562 | pci_user_read_config_byte(dev, off, &val); |
563 | data[off - init_off] = val; |
564 | off++; |
565 | --size; |
566 | } |
567 | |
568 | pci_config_pm_runtime_put(dev); |
569 | |
570 | return count; |
571 | } |
572 | |
573 | static ssize_t |
574 | pci_write_config(struct file* filp, struct kobject *kobj, |
575 | struct bin_attribute *bin_attr, |
576 | char *buf, loff_t off, size_t count) |
577 | { |
578 | struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); |
579 | unsigned int size = count; |
580 | loff_t init_off = off; |
581 | u8 *data = (u8*) buf; |
582 | |
583 | if (off > dev->cfg_size) |
584 | return 0; |
585 | if (off + count > dev->cfg_size) { |
586 | size = dev->cfg_size - off; |
587 | count = size; |
588 | } |
589 | |
590 | pci_config_pm_runtime_get(dev); |
591 | |
592 | if ((off & 1) && size) { |
593 | pci_user_write_config_byte(dev, off, data[off - init_off]); |
594 | off++; |
595 | size--; |
596 | } |
597 | |
598 | if ((off & 3) && size > 2) { |
599 | u16 val = data[off - init_off]; |
600 | val |= (u16) data[off - init_off + 1] << 8; |
601 | pci_user_write_config_word(dev, off, val); |
602 | off += 2; |
603 | size -= 2; |
604 | } |
605 | |
606 | while (size > 3) { |
607 | u32 val = data[off - init_off]; |
608 | val |= (u32) data[off - init_off + 1] << 8; |
609 | val |= (u32) data[off - init_off + 2] << 16; |
610 | val |= (u32) data[off - init_off + 3] << 24; |
611 | pci_user_write_config_dword(dev, off, val); |
612 | off += 4; |
613 | size -= 4; |
614 | } |
615 | |
616 | if (size >= 2) { |
617 | u16 val = data[off - init_off]; |
618 | val |= (u16) data[off - init_off + 1] << 8; |
619 | pci_user_write_config_word(dev, off, val); |
620 | off += 2; |
621 | size -= 2; |
622 | } |
623 | |
624 | if (size) { |
625 | pci_user_write_config_byte(dev, off, data[off - init_off]); |
626 | off++; |
627 | --size; |
628 | } |
629 | |
630 | pci_config_pm_runtime_put(dev); |
631 | |
632 | return count; |
633 | } |
634 | |
635 | static ssize_t |
636 | read_vpd_attr(struct file *filp, struct kobject *kobj, |
637 | struct bin_attribute *bin_attr, |
638 | char *buf, loff_t off, size_t count) |
639 | { |
640 | struct pci_dev *dev = |
641 | to_pci_dev(container_of(kobj, struct device, kobj)); |
642 | |
643 | if (off > bin_attr->size) |
644 | count = 0; |
645 | else if (count > bin_attr->size - off) |
646 | count = bin_attr->size - off; |
647 | |
648 | return pci_read_vpd(dev, off, count, buf); |
649 | } |
650 | |
651 | static ssize_t |
652 | write_vpd_attr(struct file *filp, struct kobject *kobj, |
653 | struct bin_attribute *bin_attr, |
654 | char *buf, loff_t off, size_t count) |
655 | { |
656 | struct pci_dev *dev = |
657 | to_pci_dev(container_of(kobj, struct device, kobj)); |
658 | |
659 | if (off > bin_attr->size) |
660 | count = 0; |
661 | else if (count > bin_attr->size - off) |
662 | count = bin_attr->size - off; |
663 | |
664 | return pci_write_vpd(dev, off, count, buf); |
665 | } |
666 | |
667 | #ifdef HAVE_PCI_LEGACY |
668 | /** |
669 | * pci_read_legacy_io - read byte(s) from legacy I/O port space |
670 | * @filp: open sysfs file |
671 | * @kobj: kobject corresponding to file to read from |
672 | * @bin_attr: struct bin_attribute for this file |
673 | * @buf: buffer to store results |
674 | * @off: offset into legacy I/O port space |
675 | * @count: number of bytes to read |
676 | * |
677 | * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific |
678 | * callback routine (pci_legacy_read). |
679 | */ |
680 | static ssize_t |
681 | pci_read_legacy_io(struct file *filp, struct kobject *kobj, |
682 | struct bin_attribute *bin_attr, |
683 | char *buf, loff_t off, size_t count) |
684 | { |
685 | struct pci_bus *bus = to_pci_bus(container_of(kobj, |
686 | struct device, |
687 | kobj)); |
688 | |
689 | /* Only support 1, 2 or 4 byte accesses */ |
690 | if (count != 1 && count != 2 && count != 4) |
691 | return -EINVAL; |
692 | |
693 | return pci_legacy_read(bus, off, (u32 *)buf, count); |
694 | } |
695 | |
696 | /** |
697 | * pci_write_legacy_io - write byte(s) to legacy I/O port space |
698 | * @filp: open sysfs file |
699 | * @kobj: kobject corresponding to file to read from |
700 | * @bin_attr: struct bin_attribute for this file |
701 | * @buf: buffer containing value to be written |
702 | * @off: offset into legacy I/O port space |
703 | * @count: number of bytes to write |
704 | * |
705 | * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific |
706 | * callback routine (pci_legacy_write). |
707 | */ |
708 | static ssize_t |
709 | pci_write_legacy_io(struct file *filp, struct kobject *kobj, |
710 | struct bin_attribute *bin_attr, |
711 | char *buf, loff_t off, size_t count) |
712 | { |
713 | struct pci_bus *bus = to_pci_bus(container_of(kobj, |
714 | struct device, |
715 | kobj)); |
716 | /* Only support 1, 2 or 4 byte accesses */ |
717 | if (count != 1 && count != 2 && count != 4) |
718 | return -EINVAL; |
719 | |
720 | return pci_legacy_write(bus, off, *(u32 *)buf, count); |
721 | } |
722 | |
723 | /** |
724 | * pci_mmap_legacy_mem - map legacy PCI memory into user memory space |
725 | * @filp: open sysfs file |
726 | * @kobj: kobject corresponding to device to be mapped |
727 | * @attr: struct bin_attribute for this file |
728 | * @vma: struct vm_area_struct passed to mmap |
729 | * |
730 | * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap |
731 | * legacy memory space (first meg of bus space) into application virtual |
732 | * memory space. |
733 | */ |
734 | static int |
735 | pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj, |
736 | struct bin_attribute *attr, |
737 | struct vm_area_struct *vma) |
738 | { |
739 | struct pci_bus *bus = to_pci_bus(container_of(kobj, |
740 | struct device, |
741 | kobj)); |
742 | |
743 | return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem); |
744 | } |
745 | |
746 | /** |
747 | * pci_mmap_legacy_io - map legacy PCI IO into user memory space |
748 | * @filp: open sysfs file |
749 | * @kobj: kobject corresponding to device to be mapped |
750 | * @attr: struct bin_attribute for this file |
751 | * @vma: struct vm_area_struct passed to mmap |
752 | * |
753 | * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap |
754 | * legacy IO space (first meg of bus space) into application virtual |
755 | * memory space. Returns -ENOSYS if the operation isn't supported |
756 | */ |
757 | static int |
758 | pci_mmap_legacy_io(struct file *filp, struct kobject *kobj, |
759 | struct bin_attribute *attr, |
760 | struct vm_area_struct *vma) |
761 | { |
762 | struct pci_bus *bus = to_pci_bus(container_of(kobj, |
763 | struct device, |
764 | kobj)); |
765 | |
766 | return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); |
767 | } |
768 | |
769 | /** |
770 | * pci_adjust_legacy_attr - adjustment of legacy file attributes |
771 | * @b: bus to create files under |
772 | * @mmap_type: I/O port or memory |
773 | * |
774 | * Stub implementation. Can be overridden by arch if necessary. |
775 | */ |
776 | void __weak |
777 | pci_adjust_legacy_attr(struct pci_bus *b, enum pci_mmap_state mmap_type) |
778 | { |
779 | return; |
780 | } |
781 | |
782 | /** |
783 | * pci_create_legacy_files - create legacy I/O port and memory files |
784 | * @b: bus to create files under |
785 | * |
786 | * Some platforms allow access to legacy I/O port and ISA memory space on |
787 | * a per-bus basis. This routine creates the files and ties them into |
788 | * their associated read, write and mmap files from pci-sysfs.c |
789 | * |
790 | * On error unwind, but don't propagate the error to the caller |
791 | * as it is ok to set up the PCI bus without these files. |
792 | */ |
793 | void pci_create_legacy_files(struct pci_bus *b) |
794 | { |
795 | int error; |
796 | |
797 | b->legacy_io = kzalloc(sizeof(struct bin_attribute) * 2, |
798 | GFP_ATOMIC); |
799 | if (!b->legacy_io) |
800 | goto kzalloc_err; |
801 | |
802 | sysfs_bin_attr_init(b->legacy_io); |
803 | b->legacy_io->attr.name = "legacy_io"; |
804 | b->legacy_io->size = 0xffff; |
805 | b->legacy_io->attr.mode = S_IRUSR | S_IWUSR; |
806 | b->legacy_io->read = pci_read_legacy_io; |
807 | b->legacy_io->write = pci_write_legacy_io; |
808 | b->legacy_io->mmap = pci_mmap_legacy_io; |
809 | pci_adjust_legacy_attr(b, pci_mmap_io); |
810 | error = device_create_bin_file(&b->dev, b->legacy_io); |
811 | if (error) |
812 | goto legacy_io_err; |
813 | |
814 | /* Allocated above after the legacy_io struct */ |
815 | b->legacy_mem = b->legacy_io + 1; |
816 | sysfs_bin_attr_init(b->legacy_mem); |
817 | b->legacy_mem->attr.name = "legacy_mem"; |
818 | b->legacy_mem->size = 1024*1024; |
819 | b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR; |
820 | b->legacy_mem->mmap = pci_mmap_legacy_mem; |
821 | pci_adjust_legacy_attr(b, pci_mmap_mem); |
822 | error = device_create_bin_file(&b->dev, b->legacy_mem); |
823 | if (error) |
824 | goto legacy_mem_err; |
825 | |
826 | return; |
827 | |
828 | legacy_mem_err: |
829 | device_remove_bin_file(&b->dev, b->legacy_io); |
830 | legacy_io_err: |
831 | kfree(b->legacy_io); |
832 | b->legacy_io = NULL; |
833 | kzalloc_err: |
834 | printk(KERN_WARNING "pci: warning: could not create legacy I/O port " |
835 | "and ISA memory resources to sysfs\n"); |
836 | return; |
837 | } |
838 | |
839 | void pci_remove_legacy_files(struct pci_bus *b) |
840 | { |
841 | if (b->legacy_io) { |
842 | device_remove_bin_file(&b->dev, b->legacy_io); |
843 | device_remove_bin_file(&b->dev, b->legacy_mem); |
844 | kfree(b->legacy_io); /* both are allocated here */ |
845 | } |
846 | } |
847 | #endif /* HAVE_PCI_LEGACY */ |
848 | |
849 | #ifdef HAVE_PCI_MMAP |
850 | |
851 | int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, |
852 | enum pci_mmap_api mmap_api) |
853 | { |
854 | unsigned long nr, start, size, pci_start; |
855 | |
856 | if (pci_resource_len(pdev, resno) == 0) |
857 | return 0; |
858 | nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; |
859 | start = vma->vm_pgoff; |
860 | size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; |
861 | pci_start = (mmap_api == PCI_MMAP_PROCFS) ? |
862 | pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0; |
863 | if (start >= pci_start && start < pci_start + size && |
864 | start + nr <= pci_start + size) |
865 | return 1; |
866 | return 0; |
867 | } |
868 | |
869 | /** |
870 | * pci_mmap_resource - map a PCI resource into user memory space |
871 | * @kobj: kobject for mapping |
872 | * @attr: struct bin_attribute for the file being mapped |
873 | * @vma: struct vm_area_struct passed into the mmap |
874 | * @write_combine: 1 for write_combine mapping |
875 | * |
876 | * Use the regular PCI mapping routines to map a PCI resource into userspace. |
877 | */ |
878 | static int |
879 | pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, |
880 | struct vm_area_struct *vma, int write_combine) |
881 | { |
882 | struct pci_dev *pdev = to_pci_dev(container_of(kobj, |
883 | struct device, kobj)); |
884 | struct resource *res = attr->private; |
885 | enum pci_mmap_state mmap_type; |
886 | resource_size_t start, end; |
887 | int i; |
888 | |
889 | for (i = 0; i < PCI_ROM_RESOURCE; i++) |
890 | if (res == &pdev->resource[i]) |
891 | break; |
892 | if (i >= PCI_ROM_RESOURCE) |
893 | return -ENODEV; |
894 | |
895 | if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) { |
896 | WARN(1, "process \"%s\" tried to map 0x%08lx bytes " |
897 | "at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n", |
898 | current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff, |
899 | pci_name(pdev), i, |
900 | (u64)pci_resource_start(pdev, i), |
901 | (u64)pci_resource_len(pdev, i)); |
902 | return -EINVAL; |
903 | } |
904 | |
905 | /* pci_mmap_page_range() expects the same kind of entry as coming |
906 | * from /proc/bus/pci/ which is a "user visible" value. If this is |
907 | * different from the resource itself, arch will do necessary fixup. |
908 | */ |
909 | pci_resource_to_user(pdev, i, res, &start, &end); |
910 | vma->vm_pgoff += start >> PAGE_SHIFT; |
911 | mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; |
912 | |
913 | if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start)) |
914 | return -EINVAL; |
915 | |
916 | return pci_mmap_page_range(pdev, vma, mmap_type, write_combine); |
917 | } |
918 | |
919 | static int |
920 | pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, |
921 | struct bin_attribute *attr, |
922 | struct vm_area_struct *vma) |
923 | { |
924 | return pci_mmap_resource(kobj, attr, vma, 0); |
925 | } |
926 | |
927 | static int |
928 | pci_mmap_resource_wc(struct file *filp, struct kobject *kobj, |
929 | struct bin_attribute *attr, |
930 | struct vm_area_struct *vma) |
931 | { |
932 | return pci_mmap_resource(kobj, attr, vma, 1); |
933 | } |
934 | |
935 | static ssize_t |
936 | pci_resource_io(struct file *filp, struct kobject *kobj, |
937 | struct bin_attribute *attr, char *buf, |
938 | loff_t off, size_t count, bool write) |
939 | { |
940 | struct pci_dev *pdev = to_pci_dev(container_of(kobj, |
941 | struct device, kobj)); |
942 | struct resource *res = attr->private; |
943 | unsigned long port = off; |
944 | int i; |
945 | |
946 | for (i = 0; i < PCI_ROM_RESOURCE; i++) |
947 | if (res == &pdev->resource[i]) |
948 | break; |
949 | if (i >= PCI_ROM_RESOURCE) |
950 | return -ENODEV; |
951 | |
952 | port += pci_resource_start(pdev, i); |
953 | |
954 | if (port > pci_resource_end(pdev, i)) |
955 | return 0; |
956 | |
957 | if (port + count - 1 > pci_resource_end(pdev, i)) |
958 | return -EINVAL; |
959 | |
960 | switch (count) { |
961 | case 1: |
962 | if (write) |
963 | outb(*(u8 *)buf, port); |
964 | else |
965 | *(u8 *)buf = inb(port); |
966 | return 1; |
967 | case 2: |
968 | if (write) |
969 | outw(*(u16 *)buf, port); |
970 | else |
971 | *(u16 *)buf = inw(port); |
972 | return 2; |
973 | case 4: |
974 | if (write) |
975 | outl(*(u32 *)buf, port); |
976 | else |
977 | *(u32 *)buf = inl(port); |
978 | return 4; |
979 | } |
980 | return -EINVAL; |
981 | } |
982 | |
983 | static ssize_t |
984 | pci_read_resource_io(struct file *filp, struct kobject *kobj, |
985 | struct bin_attribute *attr, char *buf, |
986 | loff_t off, size_t count) |
987 | { |
988 | return pci_resource_io(filp, kobj, attr, buf, off, count, false); |
989 | } |
990 | |
991 | static ssize_t |
992 | pci_write_resource_io(struct file *filp, struct kobject *kobj, |
993 | struct bin_attribute *attr, char *buf, |
994 | loff_t off, size_t count) |
995 | { |
996 | return pci_resource_io(filp, kobj, attr, buf, off, count, true); |
997 | } |
998 | |
999 | /** |
1000 | * pci_remove_resource_files - cleanup resource files |
1001 | * @pdev: dev to cleanup |
1002 | * |
1003 | * If we created resource files for @pdev, remove them from sysfs and |
1004 | * free their resources. |
1005 | */ |
1006 | static void |
1007 | pci_remove_resource_files(struct pci_dev *pdev) |
1008 | { |
1009 | int i; |
1010 | |
1011 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { |
1012 | struct bin_attribute *res_attr; |
1013 | |
1014 | res_attr = pdev->res_attr[i]; |
1015 | if (res_attr) { |
1016 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); |
1017 | kfree(res_attr); |
1018 | } |
1019 | |
1020 | res_attr = pdev->res_attr_wc[i]; |
1021 | if (res_attr) { |
1022 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); |
1023 | kfree(res_attr); |
1024 | } |
1025 | } |
1026 | } |
1027 | |
1028 | static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) |
1029 | { |
1030 | /* allocate attribute structure, piggyback attribute name */ |
1031 | int name_len = write_combine ? 13 : 10; |
1032 | struct bin_attribute *res_attr; |
1033 | int retval; |
1034 | |
1035 | res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC); |
1036 | if (res_attr) { |
1037 | char *res_attr_name = (char *)(res_attr + 1); |
1038 | |
1039 | sysfs_bin_attr_init(res_attr); |
1040 | if (write_combine) { |
1041 | pdev->res_attr_wc[num] = res_attr; |
1042 | sprintf(res_attr_name, "resource%d_wc", num); |
1043 | res_attr->mmap = pci_mmap_resource_wc; |
1044 | } else { |
1045 | pdev->res_attr[num] = res_attr; |
1046 | sprintf(res_attr_name, "resource%d", num); |
1047 | res_attr->mmap = pci_mmap_resource_uc; |
1048 | } |
1049 | if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { |
1050 | res_attr->read = pci_read_resource_io; |
1051 | res_attr->write = pci_write_resource_io; |
1052 | } |
1053 | res_attr->attr.name = res_attr_name; |
1054 | res_attr->attr.mode = S_IRUSR | S_IWUSR; |
1055 | res_attr->size = pci_resource_len(pdev, num); |
1056 | res_attr->private = &pdev->resource[num]; |
1057 | retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); |
1058 | } else |
1059 | retval = -ENOMEM; |
1060 | |
1061 | return retval; |
1062 | } |
1063 | |
1064 | /** |
1065 | * pci_create_resource_files - create resource files in sysfs for @dev |
1066 | * @pdev: dev in question |
1067 | * |
1068 | * Walk the resources in @pdev creating files for each resource available. |
1069 | */ |
1070 | static int pci_create_resource_files(struct pci_dev *pdev) |
1071 | { |
1072 | int i; |
1073 | int retval; |
1074 | |
1075 | /* Expose the PCI resources from this device as files */ |
1076 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { |
1077 | |
1078 | /* skip empty resources */ |
1079 | if (!pci_resource_len(pdev, i)) |
1080 | continue; |
1081 | |
1082 | retval = pci_create_attr(pdev, i, 0); |
1083 | /* for prefetchable resources, create a WC mappable file */ |
1084 | if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH) |
1085 | retval = pci_create_attr(pdev, i, 1); |
1086 | |
1087 | if (retval) { |
1088 | pci_remove_resource_files(pdev); |
1089 | return retval; |
1090 | } |
1091 | } |
1092 | return 0; |
1093 | } |
1094 | #else /* !HAVE_PCI_MMAP */ |
1095 | int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } |
1096 | void __weak pci_remove_resource_files(struct pci_dev *dev) { return; } |
1097 | #endif /* HAVE_PCI_MMAP */ |
1098 | |
1099 | /** |
1100 | * pci_write_rom - used to enable access to the PCI ROM display |
1101 | * @filp: sysfs file |
1102 | * @kobj: kernel object handle |
1103 | * @bin_attr: struct bin_attribute for this file |
1104 | * @buf: user input |
1105 | * @off: file offset |
1106 | * @count: number of byte in input |
1107 | * |
1108 | * writing anything except 0 enables it |
1109 | */ |
1110 | static ssize_t |
1111 | pci_write_rom(struct file *filp, struct kobject *kobj, |
1112 | struct bin_attribute *bin_attr, |
1113 | char *buf, loff_t off, size_t count) |
1114 | { |
1115 | struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj)); |
1116 | |
1117 | if ((off == 0) && (*buf == '0') && (count == 2)) |
1118 | pdev->rom_attr_enabled = 0; |
1119 | else |
1120 | pdev->rom_attr_enabled = 1; |
1121 | |
1122 | return count; |
1123 | } |
1124 | |
1125 | /** |
1126 | * pci_read_rom - read a PCI ROM |
1127 | * @filp: sysfs file |
1128 | * @kobj: kernel object handle |
1129 | * @bin_attr: struct bin_attribute for this file |
1130 | * @buf: where to put the data we read from the ROM |
1131 | * @off: file offset |
1132 | * @count: number of bytes to read |
1133 | * |
1134 | * Put @count bytes starting at @off into @buf from the ROM in the PCI |
1135 | * device corresponding to @kobj. |
1136 | */ |
1137 | static ssize_t |
1138 | pci_read_rom(struct file *filp, struct kobject *kobj, |
1139 | struct bin_attribute *bin_attr, |
1140 | char *buf, loff_t off, size_t count) |
1141 | { |
1142 | struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj)); |
1143 | void __iomem *rom; |
1144 | size_t size; |
1145 | |
1146 | if (!pdev->rom_attr_enabled) |
1147 | return -EINVAL; |
1148 | |
1149 | rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */ |
1150 | if (!rom || !size) |
1151 | return -EIO; |
1152 | |
1153 | if (off >= size) |
1154 | count = 0; |
1155 | else { |
1156 | if (off + count > size) |
1157 | count = size - off; |
1158 | |
1159 | memcpy_fromio(buf, rom + off, count); |
1160 | } |
1161 | pci_unmap_rom(pdev, rom); |
1162 | |
1163 | return count; |
1164 | } |
1165 | |
1166 | static struct bin_attribute pci_config_attr = { |
1167 | .attr = { |
1168 | .name = "config", |
1169 | .mode = S_IRUGO | S_IWUSR, |
1170 | }, |
1171 | .size = PCI_CFG_SPACE_SIZE, |
1172 | .read = pci_read_config, |
1173 | .write = pci_write_config, |
1174 | }; |
1175 | |
1176 | static struct bin_attribute pcie_config_attr = { |
1177 | .attr = { |
1178 | .name = "config", |
1179 | .mode = S_IRUGO | S_IWUSR, |
1180 | }, |
1181 | .size = PCI_CFG_SPACE_EXP_SIZE, |
1182 | .read = pci_read_config, |
1183 | .write = pci_write_config, |
1184 | }; |
1185 | |
1186 | int __weak pcibios_add_platform_entries(struct pci_dev *dev) |
1187 | { |
1188 | return 0; |
1189 | } |
1190 | |
1191 | static ssize_t reset_store(struct device *dev, |
1192 | struct device_attribute *attr, const char *buf, |
1193 | size_t count) |
1194 | { |
1195 | struct pci_dev *pdev = to_pci_dev(dev); |
1196 | unsigned long val; |
1197 | ssize_t result = strict_strtoul(buf, 0, &val); |
1198 | |
1199 | if (result < 0) |
1200 | return result; |
1201 | |
1202 | if (val != 1) |
1203 | return -EINVAL; |
1204 | |
1205 | result = pci_reset_function(pdev); |
1206 | if (result < 0) |
1207 | return result; |
1208 | |
1209 | return count; |
1210 | } |
1211 | |
1212 | static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store); |
1213 | |
1214 | static int pci_create_capabilities_sysfs(struct pci_dev *dev) |
1215 | { |
1216 | int retval; |
1217 | struct bin_attribute *attr; |
1218 | |
1219 | /* If the device has VPD, try to expose it in sysfs. */ |
1220 | if (dev->vpd) { |
1221 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); |
1222 | if (!attr) |
1223 | return -ENOMEM; |
1224 | |
1225 | sysfs_bin_attr_init(attr); |
1226 | attr->size = dev->vpd->len; |
1227 | attr->attr.name = "vpd"; |
1228 | attr->attr.mode = S_IRUSR | S_IWUSR; |
1229 | attr->read = read_vpd_attr; |
1230 | attr->write = write_vpd_attr; |
1231 | retval = sysfs_create_bin_file(&dev->dev.kobj, attr); |
1232 | if (retval) { |
1233 | kfree(attr); |
1234 | return retval; |
1235 | } |
1236 | dev->vpd->attr = attr; |
1237 | } |
1238 | |
1239 | /* Active State Power Management */ |
1240 | pcie_aspm_create_sysfs_dev_files(dev); |
1241 | |
1242 | if (!pci_probe_reset_function(dev)) { |
1243 | retval = device_create_file(&dev->dev, &reset_attr); |
1244 | if (retval) |
1245 | goto error; |
1246 | dev->reset_fn = 1; |
1247 | } |
1248 | return 0; |
1249 | |
1250 | error: |
1251 | pcie_aspm_remove_sysfs_dev_files(dev); |
1252 | if (dev->vpd && dev->vpd->attr) { |
1253 | sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); |
1254 | kfree(dev->vpd->attr); |
1255 | } |
1256 | |
1257 | return retval; |
1258 | } |
1259 | |
1260 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) |
1261 | { |
1262 | int retval; |
1263 | int rom_size = 0; |
1264 | struct bin_attribute *attr; |
1265 | |
1266 | if (!sysfs_initialized) |
1267 | return -EACCES; |
1268 | |
1269 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
1270 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); |
1271 | else |
1272 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
1273 | if (retval) |
1274 | goto err; |
1275 | |
1276 | retval = pci_create_resource_files(pdev); |
1277 | if (retval) |
1278 | goto err_config_file; |
1279 | |
1280 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) |
1281 | rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); |
1282 | else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) |
1283 | rom_size = 0x20000; |
1284 | |
1285 | /* If the device has a ROM, try to expose it in sysfs. */ |
1286 | if (rom_size) { |
1287 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); |
1288 | if (!attr) { |
1289 | retval = -ENOMEM; |
1290 | goto err_resource_files; |
1291 | } |
1292 | sysfs_bin_attr_init(attr); |
1293 | attr->size = rom_size; |
1294 | attr->attr.name = "rom"; |
1295 | attr->attr.mode = S_IRUSR | S_IWUSR; |
1296 | attr->read = pci_read_rom; |
1297 | attr->write = pci_write_rom; |
1298 | retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); |
1299 | if (retval) { |
1300 | kfree(attr); |
1301 | goto err_resource_files; |
1302 | } |
1303 | pdev->rom_attr = attr; |
1304 | } |
1305 | |
1306 | if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { |
1307 | retval = device_create_file(&pdev->dev, &vga_attr); |
1308 | if (retval) |
1309 | goto err_rom_file; |
1310 | } |
1311 | |
1312 | /* add platform-specific attributes */ |
1313 | retval = pcibios_add_platform_entries(pdev); |
1314 | if (retval) |
1315 | goto err_vga_file; |
1316 | |
1317 | /* add sysfs entries for various capabilities */ |
1318 | retval = pci_create_capabilities_sysfs(pdev); |
1319 | if (retval) |
1320 | goto err_vga_file; |
1321 | |
1322 | pci_create_firmware_label_files(pdev); |
1323 | |
1324 | return 0; |
1325 | |
1326 | err_vga_file: |
1327 | if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) |
1328 | device_remove_file(&pdev->dev, &vga_attr); |
1329 | err_rom_file: |
1330 | if (rom_size) { |
1331 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); |
1332 | kfree(pdev->rom_attr); |
1333 | pdev->rom_attr = NULL; |
1334 | } |
1335 | err_resource_files: |
1336 | pci_remove_resource_files(pdev); |
1337 | err_config_file: |
1338 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
1339 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); |
1340 | else |
1341 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
1342 | err: |
1343 | return retval; |
1344 | } |
1345 | |
1346 | static void pci_remove_capabilities_sysfs(struct pci_dev *dev) |
1347 | { |
1348 | if (dev->vpd && dev->vpd->attr) { |
1349 | sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); |
1350 | kfree(dev->vpd->attr); |
1351 | } |
1352 | |
1353 | pcie_aspm_remove_sysfs_dev_files(dev); |
1354 | if (dev->reset_fn) { |
1355 | device_remove_file(&dev->dev, &reset_attr); |
1356 | dev->reset_fn = 0; |
1357 | } |
1358 | } |
1359 | |
1360 | /** |
1361 | * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files |
1362 | * @pdev: device whose entries we should free |
1363 | * |
1364 | * Cleanup when @pdev is removed from sysfs. |
1365 | */ |
1366 | void pci_remove_sysfs_dev_files(struct pci_dev *pdev) |
1367 | { |
1368 | int rom_size = 0; |
1369 | |
1370 | if (!sysfs_initialized) |
1371 | return; |
1372 | |
1373 | pci_remove_capabilities_sysfs(pdev); |
1374 | |
1375 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
1376 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); |
1377 | else |
1378 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
1379 | |
1380 | pci_remove_resource_files(pdev); |
1381 | |
1382 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) |
1383 | rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); |
1384 | else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) |
1385 | rom_size = 0x20000; |
1386 | |
1387 | if (rom_size && pdev->rom_attr) { |
1388 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); |
1389 | kfree(pdev->rom_attr); |
1390 | } |
1391 | |
1392 | pci_remove_firmware_label_files(pdev); |
1393 | |
1394 | } |
1395 | |
1396 | static int __init pci_sysfs_init(void) |
1397 | { |
1398 | struct pci_dev *pdev = NULL; |
1399 | int retval; |
1400 | |
1401 | sysfs_initialized = 1; |
1402 | for_each_pci_dev(pdev) { |
1403 | retval = pci_create_sysfs_dev_files(pdev); |
1404 | if (retval) { |
1405 | pci_dev_put(pdev); |
1406 | return retval; |
1407 | } |
1408 | } |
1409 | |
1410 | return 0; |
1411 | } |
1412 | |
1413 | late_initcall(pci_sysfs_init); |
1414 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9