diff options
-rw-r--r-- | drivers/pci/pci-driver.c | 4 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 33 | ||||
-rw-r--r-- | drivers/pci/pci.c | 40 | ||||
-rw-r--r-- | drivers/pci/pci.h | 1 | ||||
-rw-r--r-- | include/linux/pci.h | 3 |
5 files changed, 59 insertions, 22 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 84ec9c8f6703..e5ae3a0c13bb 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -329,8 +329,8 @@ static int pci_default_resume(struct pci_dev *pci_dev) | |||
329 | /* restore the PCI config space */ | 329 | /* restore the PCI config space */ |
330 | pci_restore_state(pci_dev); | 330 | pci_restore_state(pci_dev); |
331 | /* if the device was enabled before suspend, reenable */ | 331 | /* if the device was enabled before suspend, reenable */ |
332 | if (pci_dev->is_enabled) | 332 | if (atomic_read(&pci_dev->enable_cnt)) |
333 | retval = pci_enable_device(pci_dev); | 333 | retval = __pci_enable_device(pci_dev); |
334 | /* if the device was busmaster before the suspend, make it busmaster again */ | 334 | /* if the device was busmaster before the suspend, make it busmaster again */ |
335 | if (pci_dev->is_busmaster) | 335 | if (pci_dev->is_busmaster) |
336 | pci_set_master(pci_dev); | 336 | pci_set_master(pci_dev); |
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index f952bfea48a6..7a94076752d0 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -42,7 +42,6 @@ pci_config_attr(subsystem_vendor, "0x%04x\n"); | |||
42 | pci_config_attr(subsystem_device, "0x%04x\n"); | 42 | pci_config_attr(subsystem_device, "0x%04x\n"); |
43 | pci_config_attr(class, "0x%06x\n"); | 43 | pci_config_attr(class, "0x%06x\n"); |
44 | pci_config_attr(irq, "%u\n"); | 44 | pci_config_attr(irq, "%u\n"); |
45 | pci_config_attr(is_enabled, "%u\n"); | ||
46 | 45 | ||
47 | static ssize_t broken_parity_status_show(struct device *dev, | 46 | static ssize_t broken_parity_status_show(struct device *dev, |
48 | struct device_attribute *attr, | 47 | struct device_attribute *attr, |
@@ -112,26 +111,36 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, | |||
112 | (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), | 111 | (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), |
113 | (u8)(pci_dev->class)); | 112 | (u8)(pci_dev->class)); |
114 | } | 113 | } |
115 | static ssize_t | 114 | |
116 | is_enabled_store(struct device *dev, struct device_attribute *attr, | 115 | static ssize_t is_enabled_store(struct device *dev, |
117 | const char *buf, size_t count) | 116 | struct device_attribute *attr, const char *buf, |
117 | size_t count) | ||
118 | { | 118 | { |
119 | ssize_t result = -EINVAL; | ||
119 | struct pci_dev *pdev = to_pci_dev(dev); | 120 | struct pci_dev *pdev = to_pci_dev(dev); |
120 | int retval = 0; | ||
121 | 121 | ||
122 | /* this can crash the machine when done on the "wrong" device */ | 122 | /* this can crash the machine when done on the "wrong" device */ |
123 | if (!capable(CAP_SYS_ADMIN)) | 123 | if (!capable(CAP_SYS_ADMIN)) |
124 | return count; | 124 | return count; |
125 | 125 | ||
126 | if (*buf == '0') | 126 | if (*buf == '0') { |
127 | pci_disable_device(pdev); | 127 | if (atomic_read(&pdev->enable_cnt) != 0) |
128 | pci_disable_device(pdev); | ||
129 | else | ||
130 | result = -EIO; | ||
131 | } else if (*buf == '1') | ||
132 | result = pci_enable_device(pdev); | ||
133 | |||
134 | return result < 0 ? result : count; | ||
135 | } | ||
128 | 136 | ||
129 | if (*buf == '1') | 137 | static ssize_t is_enabled_show(struct device *dev, |
130 | retval = pci_enable_device(pdev); | 138 | struct device_attribute *attr, char *buf) |
139 | { | ||
140 | struct pci_dev *pdev; | ||
131 | 141 | ||
132 | if (retval) | 142 | pdev = to_pci_dev (dev); |
133 | return retval; | 143 | return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt)); |
134 | return count; | ||
135 | } | 144 | } |
136 | 145 | ||
137 | static ssize_t | 146 | static ssize_t |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 427991741cf3..5a14b73cf3a1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -612,30 +612,51 @@ pci_enable_device_bars(struct pci_dev *dev, int bars) | |||
612 | } | 612 | } |
613 | 613 | ||
614 | /** | 614 | /** |
615 | * pci_enable_device - Initialize device before it's used by a driver. | 615 | * __pci_enable_device - Initialize device before it's used by a driver. |
616 | * @dev: PCI device to be initialized | 616 | * @dev: PCI device to be initialized |
617 | * | 617 | * |
618 | * Initialize device before it's used by a driver. Ask low-level code | 618 | * Initialize device before it's used by a driver. Ask low-level code |
619 | * to enable I/O and memory. Wake up the device if it was suspended. | 619 | * to enable I/O and memory. Wake up the device if it was suspended. |
620 | * Beware, this function can fail. | 620 | * Beware, this function can fail. |
621 | * | ||
622 | * Note this function is a backend and is not supposed to be called by | ||
623 | * normal code, use pci_enable_device() instead. | ||
621 | */ | 624 | */ |
622 | int | 625 | int |
623 | pci_enable_device(struct pci_dev *dev) | 626 | __pci_enable_device(struct pci_dev *dev) |
624 | { | 627 | { |
625 | int err; | 628 | int err; |
626 | 629 | ||
627 | if (dev->is_enabled) | ||
628 | return 0; | ||
629 | |||
630 | err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); | 630 | err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); |
631 | if (err) | 631 | if (err) |
632 | return err; | 632 | return err; |
633 | pci_fixup_device(pci_fixup_enable, dev); | 633 | pci_fixup_device(pci_fixup_enable, dev); |
634 | dev->is_enabled = 1; | ||
635 | return 0; | 634 | return 0; |
636 | } | 635 | } |
637 | 636 | ||
638 | /** | 637 | /** |
638 | * pci_enable_device - Initialize device before it's used by a driver. | ||
639 | * @dev: PCI device to be initialized | ||
640 | * | ||
641 | * Initialize device before it's used by a driver. Ask low-level code | ||
642 | * to enable I/O and memory. Wake up the device if it was suspended. | ||
643 | * Beware, this function can fail. | ||
644 | * | ||
645 | * Note we don't actually enable the device many times if we call | ||
646 | * this function repeatedly (we just increment the count). | ||
647 | */ | ||
648 | int pci_enable_device(struct pci_dev *dev) | ||
649 | { | ||
650 | int result; | ||
651 | if (atomic_add_return(1, &dev->enable_cnt) > 1) | ||
652 | return 0; /* already enabled */ | ||
653 | result = __pci_enable_device(dev); | ||
654 | if (result < 0) | ||
655 | atomic_dec(&dev->enable_cnt); | ||
656 | return result; | ||
657 | } | ||
658 | |||
659 | /** | ||
639 | * pcibios_disable_device - disable arch specific PCI resources for device dev | 660 | * pcibios_disable_device - disable arch specific PCI resources for device dev |
640 | * @dev: the PCI device to disable | 661 | * @dev: the PCI device to disable |
641 | * | 662 | * |
@@ -651,12 +672,18 @@ void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {} | |||
651 | * | 672 | * |
652 | * Signal to the system that the PCI device is not in use by the system | 673 | * Signal to the system that the PCI device is not in use by the system |
653 | * anymore. This only involves disabling PCI bus-mastering, if active. | 674 | * anymore. This only involves disabling PCI bus-mastering, if active. |
675 | * | ||
676 | * Note we don't actually disable the device until all callers of | ||
677 | * pci_device_enable() have called pci_device_disable(). | ||
654 | */ | 678 | */ |
655 | void | 679 | void |
656 | pci_disable_device(struct pci_dev *dev) | 680 | pci_disable_device(struct pci_dev *dev) |
657 | { | 681 | { |
658 | u16 pci_command; | 682 | u16 pci_command; |
659 | 683 | ||
684 | if (atomic_sub_return(1, &dev->enable_cnt) != 0) | ||
685 | return; | ||
686 | |||
660 | if (dev->msi_enabled) | 687 | if (dev->msi_enabled) |
661 | disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), | 688 | disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), |
662 | PCI_CAP_ID_MSI); | 689 | PCI_CAP_ID_MSI); |
@@ -672,7 +699,6 @@ pci_disable_device(struct pci_dev *dev) | |||
672 | dev->is_busmaster = 0; | 699 | dev->is_busmaster = 0; |
673 | 700 | ||
674 | pcibios_disable_device(dev); | 701 | pcibios_disable_device(dev); |
675 | dev->is_enabled = 0; | ||
676 | } | 702 | } |
677 | 703 | ||
678 | /** | 704 | /** |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6bf327db5c5e..398852f526a6 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -1,5 +1,6 @@ | |||
1 | /* Functions internal to the PCI core code */ | 1 | /* Functions internal to the PCI core code */ |
2 | 2 | ||
3 | extern int __must_check __pci_enable_device(struct pci_dev *); | ||
3 | extern int pci_uevent(struct device *dev, char **envp, int num_envp, | 4 | extern int pci_uevent(struct device *dev, char **envp, int num_envp, |
4 | char *buffer, int buffer_size); | 5 | char *buffer, int buffer_size); |
5 | extern int pci_create_sysfs_dev_files(struct pci_dev *pdev); | 6 | extern int pci_create_sysfs_dev_files(struct pci_dev *pdev); |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 09be0f81b27b..01c707261f9c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -51,6 +51,7 @@ | |||
51 | #include <linux/list.h> | 51 | #include <linux/list.h> |
52 | #include <linux/compiler.h> | 52 | #include <linux/compiler.h> |
53 | #include <linux/errno.h> | 53 | #include <linux/errno.h> |
54 | #include <asm/atomic.h> | ||
54 | #include <linux/device.h> | 55 | #include <linux/device.h> |
55 | 56 | ||
56 | /* File state for mmap()s on /proc/bus/pci/X/Y */ | 57 | /* File state for mmap()s on /proc/bus/pci/X/Y */ |
@@ -159,7 +160,6 @@ struct pci_dev { | |||
159 | unsigned int transparent:1; /* Transparent PCI bridge */ | 160 | unsigned int transparent:1; /* Transparent PCI bridge */ |
160 | unsigned int multifunction:1;/* Part of multi-function device */ | 161 | unsigned int multifunction:1;/* Part of multi-function device */ |
161 | /* keep track of device state */ | 162 | /* keep track of device state */ |
162 | unsigned int is_enabled:1; /* pci_enable_device has been called */ | ||
163 | unsigned int is_busmaster:1; /* device is busmaster */ | 163 | unsigned int is_busmaster:1; /* device is busmaster */ |
164 | unsigned int no_msi:1; /* device may not use msi */ | 164 | unsigned int no_msi:1; /* device may not use msi */ |
165 | unsigned int no_d1d2:1; /* only allow d0 or d3 */ | 165 | unsigned int no_d1d2:1; /* only allow d0 or d3 */ |
@@ -167,6 +167,7 @@ struct pci_dev { | |||
167 | unsigned int broken_parity_status:1; /* Device generates false positive parity */ | 167 | unsigned int broken_parity_status:1; /* Device generates false positive parity */ |
168 | unsigned int msi_enabled:1; | 168 | unsigned int msi_enabled:1; |
169 | unsigned int msix_enabled:1; | 169 | unsigned int msix_enabled:1; |
170 | atomic_t enable_cnt; /* pci_enable_device has been called */ | ||
170 | 171 | ||
171 | u32 saved_config_space[16]; /* config space saved at suspend time */ | 172 | u32 saved_config_space[16]; /* config space saved at suspend time */ |
172 | struct hlist_head saved_cap_space; | 173 | struct hlist_head saved_cap_space; |