X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpci%2Fpci-sysfs.c;h=965a5934623a30b19740308c7d31a5bd954bd322;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=7fcf935c51b516def1ec2c9c8dfaca9d12b2b905;hpb=5167311cae6aa3a5ff5afd39f88c32a435c969ef;p=linux-2.6.git diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7fcf935c5..965a59346 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "pci.h" @@ -28,7 +29,7 @@ static int sysfs_initialized; /* = 0 */ /* show configuration fields */ #define pci_config_attr(field, format_string) \ static ssize_t \ -field##_show(struct device *dev, char *buf) \ +field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ struct pci_dev *pdev; \ \ @@ -43,35 +44,53 @@ pci_config_attr(subsystem_device, "0x%04x\n"); pci_config_attr(class, "0x%06x\n"); pci_config_attr(irq, "%u\n"); -static ssize_t local_cpus_show(struct device *dev, char *buf) +static ssize_t local_cpus_show(struct device *dev, + struct device_attribute *attr, char *buf) { - cpumask_t mask = pcibus_to_cpumask(to_pci_dev(dev)->bus->number); - int len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask); + cpumask_t mask; + int len; + + mask = pcibus_to_cpumask(to_pci_dev(dev)->bus); + len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask); strcat(buf,"\n"); return 1+len; } /* show resources */ static ssize_t -resource_show(struct device * dev, char * buf) +resource_show(struct device * dev, struct device_attribute *attr, char * buf) { struct pci_dev * pci_dev = to_pci_dev(dev); char * str = buf; int i; int max = 7; + u64 start, end; if (pci_dev->subordinate) max = DEVICE_COUNT_RESOURCE; for (i = 0; i < max; i++) { - str += sprintf(str,"0x%016lx 0x%016lx 0x%016lx\n", - pci_resource_start(pci_dev,i), - pci_resource_end(pci_dev,i), - pci_resource_flags(pci_dev,i)); + struct resource *res = &pci_dev->resource[i]; + pci_resource_to_user(pci_dev, i, res, &start, &end); + str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx\n", + (unsigned long long)start, + (unsigned long long)end, + (unsigned long long)res->flags); } return (str - buf); } +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + + return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x\n", + pci_dev->vendor, pci_dev->device, + pci_dev->subsystem_vendor, pci_dev->subsystem_device, + (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), + (u8)(pci_dev->class)); +} + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), @@ -81,6 +100,7 @@ struct device_attribute pci_dev_attrs[] = { __ATTR_RO(class), __ATTR_RO(irq), __ATTR_RO(local_cpus), + __ATTR_RO(modalias), __ATTR_NULL, }; @@ -90,6 +110,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); unsigned int size = 64; loff_t init_off = off; + u8 *data = (u8*) buf; /* Several chips lock up trying to read undefined config space */ if (capable(CAP_SYS_ADMIN)) { @@ -107,30 +128,47 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) size = count; } - while (off & 3) { - unsigned char val; - pci_read_config_byte(dev, off, &val); - buf[off - init_off] = val; + if ((off & 1) && size) { + u8 val; + pci_user_read_config_byte(dev, off, &val); + data[off - init_off] = val; off++; - if (--size == 0) - break; + size--; + } + + if ((off & 3) && size > 2) { + u16 val; + pci_user_read_config_word(dev, off, &val); + data[off - init_off] = val & 0xff; + data[off - init_off + 1] = (val >> 8) & 0xff; + off += 2; + size -= 2; } while (size > 3) { - unsigned int val; - pci_read_config_dword(dev, off, &val); - buf[off - init_off] = val & 0xff; - buf[off - init_off + 1] = (val >> 8) & 0xff; - buf[off - init_off + 2] = (val >> 16) & 0xff; - buf[off - init_off + 3] = (val >> 24) & 0xff; + u32 val; + pci_user_read_config_dword(dev, off, &val); + data[off - init_off] = val & 0xff; + data[off - init_off + 1] = (val >> 8) & 0xff; + data[off - init_off + 2] = (val >> 16) & 0xff; + data[off - init_off + 3] = (val >> 24) & 0xff; off += 4; size -= 4; } - while (size > 0) { - unsigned char val; - pci_read_config_byte(dev, off, &val); - buf[off - init_off] = val; + if (size >= 2) { + u16 val; + pci_user_read_config_word(dev, off, &val); + data[off - init_off] = val & 0xff; + data[off - init_off + 1] = (val >> 8) & 0xff; + off += 2; + size -= 2; + } + + if (size > 0) { + u8 val; + pci_user_read_config_byte(dev, off, &val); + data[off - init_off] = val; off++; --size; } @@ -144,6 +182,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); unsigned int size = count; loff_t init_off = off; + u8 *data = (u8*) buf; if (off > dev->cfg_size) return 0; @@ -151,26 +190,41 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) size = dev->cfg_size - off; count = size; } - - while (off & 3) { - pci_write_config_byte(dev, off, buf[off - init_off]); + + if ((off & 1) && size) { + pci_user_write_config_byte(dev, off, data[off - init_off]); off++; - if (--size == 0) - break; + size--; } + + if ((off & 3) && size > 2) { + u16 val = data[off - init_off]; + val |= (u16) data[off - init_off + 1] << 8; + pci_user_write_config_word(dev, off, val); + off += 2; + size -= 2; + } while (size > 3) { - unsigned int val = buf[off - init_off]; - val |= (unsigned int) buf[off - init_off + 1] << 8; - val |= (unsigned int) buf[off - init_off + 2] << 16; - val |= (unsigned int) buf[off - init_off + 3] << 24; - pci_write_config_dword(dev, off, val); + u32 val = data[off - init_off]; + val |= (u32) data[off - init_off + 1] << 8; + val |= (u32) data[off - init_off + 2] << 16; + val |= (u32) data[off - init_off + 3] << 24; + pci_user_write_config_dword(dev, off, val); off += 4; size -= 4; } + + if (size >= 2) { + u16 val = data[off - init_off]; + val |= (u16) data[off - init_off + 1] << 8; + pci_user_write_config_word(dev, off, val); + off += 2; + size -= 2; + } - while (size > 0) { - pci_write_config_byte(dev, off, buf[off - init_off]); + if (size) { + pci_user_write_config_byte(dev, off, data[off - init_off]); off++; --size; } @@ -178,6 +232,178 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) return count; } +#ifdef HAVE_PCI_LEGACY +/** + * pci_read_legacy_io - read byte(s) from legacy I/O port space + * @kobj: kobject corresponding to file to read from + * @buf: buffer to store results + * @off: offset into legacy I/O port space + * @count: number of bytes to read + * + * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific + * callback routine (pci_legacy_read). + */ +ssize_t +pci_read_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct pci_bus *bus = to_pci_bus(container_of(kobj, + struct class_device, + kobj)); + + /* Only support 1, 2 or 4 byte accesses */ + if (count != 1 && count != 2 && count != 4) + return -EINVAL; + + return pci_legacy_read(bus, off, (u32 *)buf, count); +} + +/** + * pci_write_legacy_io - write byte(s) to legacy I/O port space + * @kobj: kobject corresponding to file to read from + * @buf: buffer containing value to be written + * @off: offset into legacy I/O port space + * @count: number of bytes to write + * + * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific + * callback routine (pci_legacy_write). + */ +ssize_t +pci_write_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct pci_bus *bus = to_pci_bus(container_of(kobj, + struct class_device, + kobj)); + /* Only support 1, 2 or 4 byte accesses */ + if (count != 1 && count != 2 && count != 4) + return -EINVAL; + + return pci_legacy_write(bus, off, *(u32 *)buf, count); +} + +/** + * pci_mmap_legacy_mem - map legacy PCI memory into user memory space + * @kobj: kobject corresponding to device to be mapped + * @attr: struct bin_attribute for this file + * @vma: struct vm_area_struct passed to mmap + * + * Uses an arch specific callback, pci_mmap_legacy_page_range, to mmap + * legacy memory space (first meg of bus space) into application virtual + * memory space. + */ +int +pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + struct pci_bus *bus = to_pci_bus(container_of(kobj, + struct class_device, + kobj)); + + return pci_mmap_legacy_page_range(bus, vma); +} +#endif /* HAVE_PCI_LEGACY */ + +#ifdef HAVE_PCI_MMAP +/** + * pci_mmap_resource - map a PCI resource into user memory space + * @kobj: kobject for mapping + * @attr: struct bin_attribute for the file being mapped + * @vma: struct vm_area_struct passed into the mmap + * + * Use the regular PCI mapping routines to map a PCI resource into userspace. + * FIXME: write combining? maybe automatic for prefetchable regions? + */ +static int +pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + struct pci_dev *pdev = to_pci_dev(container_of(kobj, + struct device, kobj)); + struct resource *res = (struct resource *)attr->private; + enum pci_mmap_state mmap_type; + u64 start, end; + int i; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if (res == &pdev->resource[i]) + break; + if (i >= PCI_ROM_RESOURCE) + return -ENODEV; + + /* pci_mmap_page_range() expects the same kind of entry as coming + * from /proc/bus/pci/ which is a "user visible" value. If this is + * different from the resource itself, arch will do necessary fixup. + */ + pci_resource_to_user(pdev, i, res, &start, &end); + vma->vm_pgoff += start >> PAGE_SHIFT; + mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; + + return pci_mmap_page_range(pdev, vma, mmap_type, 0); +} + +/** + * pci_create_resource_files - create resource files in sysfs for @dev + * @dev: dev in question + * + * Walk the resources in @dev creating files for each resource available. + */ +static void +pci_create_resource_files(struct pci_dev *pdev) +{ + int i; + + /* Expose the PCI resources from this device as files */ + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + struct bin_attribute *res_attr; + + /* skip empty resources */ + if (!pci_resource_len(pdev, i)) + continue; + + /* allocate attribute structure, piggyback attribute name */ + res_attr = kzalloc(sizeof(*res_attr) + 10, GFP_ATOMIC); + if (res_attr) { + char *res_attr_name = (char *)(res_attr + 1); + + pdev->res_attr[i] = res_attr; + sprintf(res_attr_name, "resource%d", i); + res_attr->attr.name = res_attr_name; + res_attr->attr.mode = S_IRUSR | S_IWUSR; + res_attr->attr.owner = THIS_MODULE; + res_attr->size = pci_resource_len(pdev, i); + res_attr->mmap = pci_mmap_resource; + res_attr->private = &pdev->resource[i]; + sysfs_create_bin_file(&pdev->dev.kobj, res_attr); + } + } +} + +/** + * pci_remove_resource_files - cleanup resource files + * @dev: dev to cleanup + * + * If we created resource files for @dev, remove them from sysfs and + * free their resources. + */ +static void +pci_remove_resource_files(struct pci_dev *pdev) +{ + int i; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + struct bin_attribute *res_attr; + + res_attr = pdev->res_attr[i]; + if (res_attr) { + sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); + kfree(res_attr); + } + } +} +#else /* !HAVE_PCI_MMAP */ +static inline void pci_create_resource_files(struct pci_dev *dev) { return; } +static inline void pci_remove_resource_files(struct pci_dev *dev) { return; } +#endif /* HAVE_PCI_MMAP */ + /** * pci_write_rom - used to enable access to the PCI ROM display * @kobj: kernel object handle @@ -269,12 +495,15 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev) else sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); + pci_create_resource_files(pdev); + /* If the device has a ROM, try to expose it in sysfs. */ if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { struct bin_attribute *rom_attr; rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC); if (rom_attr) { + memset(rom_attr, 0x00, sizeof(*rom_attr)); pdev->rom_attr = rom_attr; rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE); rom_attr->attr.name = "rom"; @@ -304,6 +533,8 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) else sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); + pci_remove_resource_files(pdev); + if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { if (pdev->rom_attr) { sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); @@ -317,7 +548,7 @@ static int __init pci_sysfs_init(void) struct pci_dev *pdev = NULL; sysfs_initialized = 1; - while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) + for_each_pci_dev(pdev) pci_create_sysfs_dev_files(pdev); return 0;