aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pci-sysfs.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pci/pci-sysfs.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/pci/pci-sysfs.c')
-rw-r--r--drivers/pci/pci-sysfs.c490
1 files changed, 490 insertions, 0 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
new file mode 100644
index 000000000000..d57ae71d32b1
--- /dev/null
+++ b/drivers/pci/pci-sysfs.c
@@ -0,0 +1,490 @@
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/config.h>
19#include <linux/kernel.h>
20#include <linux/pci.h>
21#include <linux/stat.h>
22#include <linux/topology.h>
23#include <linux/mm.h>
24
25#include "pci.h"
26
27static int sysfs_initialized; /* = 0 */
28
29/* show configuration fields */
30#define pci_config_attr(field, format_string) \
31static ssize_t \
32field##_show(struct device *dev, char *buf) \
33{ \
34 struct pci_dev *pdev; \
35 \
36 pdev = to_pci_dev (dev); \
37 return sprintf (buf, format_string, pdev->field); \
38}
39
40pci_config_attr(vendor, "0x%04x\n");
41pci_config_attr(device, "0x%04x\n");
42pci_config_attr(subsystem_vendor, "0x%04x\n");
43pci_config_attr(subsystem_device, "0x%04x\n");
44pci_config_attr(class, "0x%06x\n");
45pci_config_attr(irq, "%u\n");
46
47static ssize_t local_cpus_show(struct device *dev, char *buf)
48{
49 cpumask_t mask = pcibus_to_cpumask(to_pci_dev(dev)->bus);
50 int len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask);
51 strcat(buf,"\n");
52 return 1+len;
53}
54
55/* show resources */
56static ssize_t
57resource_show(struct device * dev, char * buf)
58{
59 struct pci_dev * pci_dev = to_pci_dev(dev);
60 char * str = buf;
61 int i;
62 int max = 7;
63
64 if (pci_dev->subordinate)
65 max = DEVICE_COUNT_RESOURCE;
66
67 for (i = 0; i < max; i++) {
68 str += sprintf(str,"0x%016lx 0x%016lx 0x%016lx\n",
69 pci_resource_start(pci_dev,i),
70 pci_resource_end(pci_dev,i),
71 pci_resource_flags(pci_dev,i));
72 }
73 return (str - buf);
74}
75
76struct device_attribute pci_dev_attrs[] = {
77 __ATTR_RO(resource),
78 __ATTR_RO(vendor),
79 __ATTR_RO(device),
80 __ATTR_RO(subsystem_vendor),
81 __ATTR_RO(subsystem_device),
82 __ATTR_RO(class),
83 __ATTR_RO(irq),
84 __ATTR_RO(local_cpus),
85 __ATTR_NULL,
86};
87
88static ssize_t
89pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
90{
91 struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
92 unsigned int size = 64;
93 loff_t init_off = off;
94
95 /* Several chips lock up trying to read undefined config space */
96 if (capable(CAP_SYS_ADMIN)) {
97 size = dev->cfg_size;
98 } else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
99 size = 128;
100 }
101
102 if (off > size)
103 return 0;
104 if (off + count > size) {
105 size -= off;
106 count = size;
107 } else {
108 size = count;
109 }
110
111 while (off & 3) {
112 unsigned char val;
113 pci_read_config_byte(dev, off, &val);
114 buf[off - init_off] = val;
115 off++;
116 if (--size == 0)
117 break;
118 }
119
120 while (size > 3) {
121 unsigned int val;
122 pci_read_config_dword(dev, off, &val);
123 buf[off - init_off] = val & 0xff;
124 buf[off - init_off + 1] = (val >> 8) & 0xff;
125 buf[off - init_off + 2] = (val >> 16) & 0xff;
126 buf[off - init_off + 3] = (val >> 24) & 0xff;
127 off += 4;
128 size -= 4;
129 }
130
131 while (size > 0) {
132 unsigned char val;
133 pci_read_config_byte(dev, off, &val);
134 buf[off - init_off] = val;
135 off++;
136 --size;
137 }
138
139 return count;
140}
141
142static ssize_t
143pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
144{
145 struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
146 unsigned int size = count;
147 loff_t init_off = off;
148
149 if (off > dev->cfg_size)
150 return 0;
151 if (off + count > dev->cfg_size) {
152 size = dev->cfg_size - off;
153 count = size;
154 }
155
156 while (off & 3) {
157 pci_write_config_byte(dev, off, buf[off - init_off]);
158 off++;
159 if (--size == 0)
160 break;
161 }
162
163 while (size > 3) {
164 unsigned int val = buf[off - init_off];
165 val |= (unsigned int) buf[off - init_off + 1] << 8;
166 val |= (unsigned int) buf[off - init_off + 2] << 16;
167 val |= (unsigned int) buf[off - init_off + 3] << 24;
168 pci_write_config_dword(dev, off, val);
169 off += 4;
170 size -= 4;
171 }
172
173 while (size > 0) {
174 pci_write_config_byte(dev, off, buf[off - init_off]);
175 off++;
176 --size;
177 }
178
179 return count;
180}
181
182#ifdef HAVE_PCI_LEGACY
183/**
184 * pci_read_legacy_io - read byte(s) from legacy I/O port space
185 * @kobj: kobject corresponding to file to read from
186 * @buf: buffer to store results
187 * @off: offset into legacy I/O port space
188 * @count: number of bytes to read
189 *
190 * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific
191 * callback routine (pci_legacy_read).
192 */
193ssize_t
194pci_read_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count)
195{
196 struct pci_bus *bus = to_pci_bus(container_of(kobj,
197 struct class_device,
198 kobj));
199
200 /* Only support 1, 2 or 4 byte accesses */
201 if (count != 1 && count != 2 && count != 4)
202 return -EINVAL;
203
204 return pci_legacy_read(bus, off, (u32 *)buf, count);
205}
206
207/**
208 * pci_write_legacy_io - write byte(s) to legacy I/O port space
209 * @kobj: kobject corresponding to file to read from
210 * @buf: buffer containing value to be written
211 * @off: offset into legacy I/O port space
212 * @count: number of bytes to write
213 *
214 * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific
215 * callback routine (pci_legacy_write).
216 */
217ssize_t
218pci_write_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count)
219{
220 struct pci_bus *bus = to_pci_bus(container_of(kobj,
221 struct class_device,
222 kobj));
223 /* Only support 1, 2 or 4 byte accesses */
224 if (count != 1 && count != 2 && count != 4)
225 return -EINVAL;
226
227 return pci_legacy_write(bus, off, *(u32 *)buf, count);
228}
229
230/**
231 * pci_mmap_legacy_mem - map legacy PCI memory into user memory space
232 * @kobj: kobject corresponding to device to be mapped
233 * @attr: struct bin_attribute for this file
234 * @vma: struct vm_area_struct passed to mmap
235 *
236 * Uses an arch specific callback, pci_mmap_legacy_page_range, to mmap
237 * legacy memory space (first meg of bus space) into application virtual
238 * memory space.
239 */
240int
241pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr,
242 struct vm_area_struct *vma)
243{
244 struct pci_bus *bus = to_pci_bus(container_of(kobj,
245 struct class_device,
246 kobj));
247
248 return pci_mmap_legacy_page_range(bus, vma);
249}
250#endif /* HAVE_PCI_LEGACY */
251
252#ifdef HAVE_PCI_MMAP
253/**
254 * pci_mmap_resource - map a PCI resource into user memory space
255 * @kobj: kobject for mapping
256 * @attr: struct bin_attribute for the file being mapped
257 * @vma: struct vm_area_struct passed into the mmap
258 *
259 * Use the regular PCI mapping routines to map a PCI resource into userspace.
260 * FIXME: write combining? maybe automatic for prefetchable regions?
261 */
262static int
263pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
264 struct vm_area_struct *vma)
265{
266 struct pci_dev *pdev = to_pci_dev(container_of(kobj,
267 struct device, kobj));
268 struct resource *res = (struct resource *)attr->private;
269 enum pci_mmap_state mmap_type;
270
271 vma->vm_pgoff += res->start >> PAGE_SHIFT;
272 mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
273
274 return pci_mmap_page_range(pdev, vma, mmap_type, 0);
275}
276
277/**
278 * pci_create_resource_files - create resource files in sysfs for @dev
279 * @dev: dev in question
280 *
281 * Walk the resources in @dev creating files for each resource available.
282 */
283static void
284pci_create_resource_files(struct pci_dev *pdev)
285{
286 int i;
287
288 /* Expose the PCI resources from this device as files */
289 for (i = 0; i < PCI_ROM_RESOURCE; i++) {
290 struct bin_attribute *res_attr;
291
292 /* skip empty resources */
293 if (!pci_resource_len(pdev, i))
294 continue;
295
296 res_attr = kmalloc(sizeof(*res_attr) + 10, GFP_ATOMIC);
297 if (res_attr) {
298 memset(res_attr, 0, sizeof(*res_attr) + 10);
299 pdev->res_attr[i] = res_attr;
300 /* Allocated above after the res_attr struct */
301 res_attr->attr.name = (char *)(res_attr + 1);
302 sprintf(res_attr->attr.name, "resource%d", i);
303 res_attr->size = pci_resource_len(pdev, i);
304 res_attr->attr.mode = S_IRUSR | S_IWUSR;
305 res_attr->attr.owner = THIS_MODULE;
306 res_attr->mmap = pci_mmap_resource;
307 res_attr->private = &pdev->resource[i];
308 sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
309 }
310 }
311}
312
313/**
314 * pci_remove_resource_files - cleanup resource files
315 * @dev: dev to cleanup
316 *
317 * If we created resource files for @dev, remove them from sysfs and
318 * free their resources.
319 */
320static void
321pci_remove_resource_files(struct pci_dev *pdev)
322{
323 int i;
324
325 for (i = 0; i < PCI_ROM_RESOURCE; i++) {
326 struct bin_attribute *res_attr;
327
328 res_attr = pdev->res_attr[i];
329 if (res_attr) {
330 sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
331 kfree(res_attr);
332 }
333 }
334}
335#else /* !HAVE_PCI_MMAP */
336static inline void pci_create_resource_files(struct pci_dev *dev) { return; }
337static inline void pci_remove_resource_files(struct pci_dev *dev) { return; }
338#endif /* HAVE_PCI_MMAP */
339
340/**
341 * pci_write_rom - used to enable access to the PCI ROM display
342 * @kobj: kernel object handle
343 * @buf: user input
344 * @off: file offset
345 * @count: number of byte in input
346 *
347 * writing anything except 0 enables it
348 */
349static ssize_t
350pci_write_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
351{
352 struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
353
354 if ((off == 0) && (*buf == '0') && (count == 2))
355 pdev->rom_attr_enabled = 0;
356 else
357 pdev->rom_attr_enabled = 1;
358
359 return count;
360}
361
362/**
363 * pci_read_rom - read a PCI ROM
364 * @kobj: kernel object handle
365 * @buf: where to put the data we read from the ROM
366 * @off: file offset
367 * @count: number of bytes to read
368 *
369 * Put @count bytes starting at @off into @buf from the ROM in the PCI
370 * device corresponding to @kobj.
371 */
372static ssize_t
373pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
374{
375 struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
376 void __iomem *rom;
377 size_t size;
378
379 if (!pdev->rom_attr_enabled)
380 return -EINVAL;
381
382 rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */
383 if (!rom)
384 return 0;
385
386 if (off >= size)
387 count = 0;
388 else {
389 if (off + count > size)
390 count = size - off;
391
392 memcpy_fromio(buf, rom + off, count);
393 }
394 pci_unmap_rom(pdev, rom);
395
396 return count;
397}
398
399static struct bin_attribute pci_config_attr = {
400 .attr = {
401 .name = "config",
402 .mode = S_IRUGO | S_IWUSR,
403 .owner = THIS_MODULE,
404 },
405 .size = 256,
406 .read = pci_read_config,
407 .write = pci_write_config,
408};
409
410static struct bin_attribute pcie_config_attr = {
411 .attr = {
412 .name = "config",
413 .mode = S_IRUGO | S_IWUSR,
414 .owner = THIS_MODULE,
415 },
416 .size = 4096,
417 .read = pci_read_config,
418 .write = pci_write_config,
419};
420
421int pci_create_sysfs_dev_files (struct pci_dev *pdev)
422{
423 if (!sysfs_initialized)
424 return -EACCES;
425
426 if (pdev->cfg_size < 4096)
427 sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
428 else
429 sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
430
431 pci_create_resource_files(pdev);
432
433 /* If the device has a ROM, try to expose it in sysfs. */
434 if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
435 struct bin_attribute *rom_attr;
436
437 rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
438 if (rom_attr) {
439 memset(rom_attr, 0x00, sizeof(*rom_attr));
440 pdev->rom_attr = rom_attr;
441 rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
442 rom_attr->attr.name = "rom";
443 rom_attr->attr.mode = S_IRUSR;
444 rom_attr->attr.owner = THIS_MODULE;
445 rom_attr->read = pci_read_rom;
446 rom_attr->write = pci_write_rom;
447 sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
448 }
449 }
450 /* add platform-specific attributes */
451 pcibios_add_platform_entries(pdev);
452
453 return 0;
454}
455
456/**
457 * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
458 * @pdev: device whose entries we should free
459 *
460 * Cleanup when @pdev is removed from sysfs.
461 */
462void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
463{
464 if (pdev->cfg_size < 4096)
465 sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
466 else
467 sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
468
469 pci_remove_resource_files(pdev);
470
471 if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
472 if (pdev->rom_attr) {
473 sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
474 kfree(pdev->rom_attr);
475 }
476 }
477}
478
479static int __init pci_sysfs_init(void)
480{
481 struct pci_dev *pdev = NULL;
482
483 sysfs_initialized = 1;
484 for_each_pci_dev(pdev)
485 pci_create_sysfs_dev_files(pdev);
486
487 return 0;
488}
489
490__initcall(pci_sysfs_init);