Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
[sfrench/cifs-2.6.git] / drivers / pci / pci-sysfs.c
index 56a3b397efee217736b1ce54d6f7621619cb04f4..f952bfea48a6f972e0a1de9cc8bfc4a33920bfb7 100644 (file)
@@ -15,7 +15,6 @@
  */
 
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/stat.h>
@@ -43,6 +42,29 @@ pci_config_attr(subsystem_vendor, "0x%04x\n");
 pci_config_attr(subsystem_device, "0x%04x\n");
 pci_config_attr(class, "0x%06x\n");
 pci_config_attr(irq, "%u\n");
+pci_config_attr(is_enabled, "%u\n");
+
+static ssize_t broken_parity_status_show(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       return sprintf (buf, "%u\n", pdev->broken_parity_status);
+}
+
+static ssize_t broken_parity_status_store(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       ssize_t consumed = -EINVAL;
+
+       if ((count > 0) && (*buf == '0' || *buf == '1')) {
+               pdev->broken_parity_status = *buf == '1' ? 1 : 0;
+               consumed = count;
+       }
+       return consumed;
+}
 
 static ssize_t local_cpus_show(struct device *dev,
                        struct device_attribute *attr, char *buf)
@@ -64,7 +86,7 @@ resource_show(struct device * dev, struct device_attribute *attr, char * buf)
        char * str = buf;
        int i;
        int max = 7;
-       u64 start, end;
+       resource_size_t start, end;
 
        if (pci_dev->subordinate)
                max = DEVICE_COUNT_RESOURCE;
@@ -90,6 +112,68 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
                       (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
                       (u8)(pci_dev->class));
 }
+static ssize_t
+is_enabled_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       int retval = 0;
+
+       /* this can crash the machine when done on the "wrong" device */
+       if (!capable(CAP_SYS_ADMIN))
+               return count;
+
+       if (*buf == '0')
+               pci_disable_device(pdev);
+
+       if (*buf == '1')
+               retval = pci_enable_device(pdev);
+
+       if (retval)
+               return retval;
+       return count;
+}
+
+static ssize_t
+msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+
+       if (!pdev->subordinate)
+               return 0;
+
+       return sprintf (buf, "%u\n",
+                       !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
+}
+
+static ssize_t
+msi_bus_store(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+
+       /* bad things may happen if the no_msi flag is changed
+        * while some drivers are loaded */
+       if (!capable(CAP_SYS_ADMIN))
+               return count;
+
+       if (!pdev->subordinate)
+               return count;
+
+       if (*buf == '0') {
+               pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+               dev_warn(&pdev->dev, "forced subordinate bus to not support MSI,"
+                        " bad things could happen.\n");
+       }
+
+       if (*buf == '1') {
+               pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI;
+               dev_warn(&pdev->dev, "forced subordinate bus to support MSI,"
+                        " bad things could happen.\n");
+       }
+
+       return count;
+}
 
 struct device_attribute pci_dev_attrs[] = {
        __ATTR_RO(resource),
@@ -101,6 +185,10 @@ struct device_attribute pci_dev_attrs[] = {
        __ATTR_RO(irq),
        __ATTR_RO(local_cpus),
        __ATTR_RO(modalias),
+       __ATTR(enable, 0600, is_enabled_show, is_enabled_store),
+       __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
+               broken_parity_status_show,broken_parity_status_store),
+       __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
        __ATTR_NULL,
 };
 
@@ -130,7 +218,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
 
        if ((off & 1) && size) {
                u8 val;
-               pci_read_config_byte(dev, off, &val);
+               pci_user_read_config_byte(dev, off, &val);
                data[off - init_off] = val;
                off++;
                size--;
@@ -138,7 +226,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
 
        if ((off & 3) && size > 2) {
                u16 val;
-               pci_read_config_word(dev, off, &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;
@@ -147,7 +235,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
 
        while (size > 3) {
                u32 val;
-               pci_read_config_dword(dev, off, &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;
@@ -158,7 +246,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
 
        if (size >= 2) {
                u16 val;
-               pci_read_config_word(dev, off, &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;
@@ -167,7 +255,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
 
        if (size > 0) {
                u8 val;
-               pci_read_config_byte(dev, off, &val);
+               pci_user_read_config_byte(dev, off, &val);
                data[off - init_off] = val;
                off++;
                --size;
@@ -192,7 +280,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
        }
        
        if ((off & 1) && size) {
-               pci_write_config_byte(dev, off, data[off - init_off]);
+               pci_user_write_config_byte(dev, off, data[off - init_off]);
                off++;
                size--;
        }
@@ -200,7 +288,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
        if ((off & 3) && size > 2) {
                u16 val = data[off - init_off];
                val |= (u16) data[off - init_off + 1] << 8;
-                pci_write_config_word(dev, off, val);
+                pci_user_write_config_word(dev, off, val);
                 off += 2;
                 size -= 2;
         }
@@ -210,7 +298,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
                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_write_config_dword(dev, off, val);
+               pci_user_write_config_dword(dev, off, val);
                off += 4;
                size -= 4;
        }
@@ -218,13 +306,13 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
        if (size >= 2) {
                u16 val = data[off - init_off];
                val |= (u16) data[off - init_off + 1] << 8;
-               pci_write_config_word(dev, off, val);
+               pci_user_write_config_word(dev, off, val);
                off += 2;
                size -= 2;
        }
 
        if (size) {
-               pci_write_config_byte(dev, off, data[off - init_off]);
+               pci_user_write_config_byte(dev, off, data[off - init_off]);
                off++;
                --size;
        }
@@ -320,7 +408,7 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
                                                       struct device, kobj));
        struct resource *res = (struct resource *)attr->private;
        enum pci_mmap_state mmap_type;
-       u64 start, end;
+       resource_size_t start, end;
        int i;
 
        for (i = 0; i < PCI_ROM_RESOURCE; i++)
@@ -340,16 +428,39 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
        return pci_mmap_page_range(pdev, vma, mmap_type, 0);
 }
 
+/**
+ * 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);
+               }
+       }
+}
+
 /**
  * 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)
+static int pci_create_resource_files(struct pci_dev *pdev)
 {
        int i;
+       int retval;
 
        /* Expose the PCI resources from this device as files */
        for (i = 0; i < PCI_ROM_RESOURCE; i++) {
@@ -360,7 +471,7 @@ pci_create_resource_files(struct pci_dev *pdev)
                        continue;
 
                /* allocate attribute structure, piggyback attribute name */
-               res_attr = kcalloc(1, sizeof(*res_attr) + 10, GFP_ATOMIC);
+               res_attr = kzalloc(sizeof(*res_attr) + 10, GFP_ATOMIC);
                if (res_attr) {
                        char *res_attr_name = (char *)(res_attr + 1);
 
@@ -372,35 +483,19 @@ pci_create_resource_files(struct pci_dev *pdev)
                        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);
+                       retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
+                       if (retval) {
+                               pci_remove_resource_files(pdev);
+                               return retval;
+                       }
+               } else {
+                       return -ENOMEM;
                }
        }
+       return 0;
 }
 #else /* !HAVE_PCI_MMAP */
-static inline void pci_create_resource_files(struct pci_dev *dev) { return; }
+static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; }
 static inline void pci_remove_resource_files(struct pci_dev *dev) { return; }
 #endif /* HAVE_PCI_MMAP */
 
@@ -485,25 +580,29 @@ static struct bin_attribute pcie_config_attr = {
        .write = pci_write_config,
 };
 
-int pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
 {
+       struct bin_attribute *rom_attr = NULL;
+       int retval;
+
        if (!sysfs_initialized)
                return -EACCES;
 
        if (pdev->cfg_size < 4096)
-               sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
+               retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
        else
-               sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+               retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+       if (retval)
+               goto err;
 
-       pci_create_resource_files(pdev);
+       retval = pci_create_resource_files(pdev);
+       if (retval)
+               goto err_bin_file;
 
        /* 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);
+               rom_attr = kzalloc(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";
@@ -511,13 +610,28 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev)
                        rom_attr->attr.owner = THIS_MODULE;
                        rom_attr->read = pci_read_rom;
                        rom_attr->write = pci_write_rom;
-                       sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+                       retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+                       if (retval)
+                               goto err_rom;
+               } else {
+                       retval = -ENOMEM;
+                       goto err_bin_file;
                }
        }
        /* add platform-specific attributes */
        pcibios_add_platform_entries(pdev);
-       
+
        return 0;
+
+err_rom:
+       kfree(rom_attr);
+err_bin_file:
+       if (pdev->cfg_size < 4096)
+               sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+       else
+               sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+err:
+       return retval;
 }
 
 /**
@@ -528,6 +642,9 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev)
  */
 void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
 {
+       if (!sysfs_initialized)
+               return;
+
        if (pdev->cfg_size < 4096)
                sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
        else
@@ -546,10 +663,14 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
 static int __init pci_sysfs_init(void)
 {
        struct pci_dev *pdev = NULL;
-       
+       int retval;
+
        sysfs_initialized = 1;
-       for_each_pci_dev(pdev)
-               pci_create_sysfs_dev_files(pdev);
+       for_each_pci_dev(pdev) {
+               retval = pci_create_sysfs_dev_files(pdev);
+               if (retval)
+                       return retval;
+       }
 
        return 0;
 }