aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pci-driver.c
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2006-11-22 15:40:31 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2006-12-01 17:36:59 -0500
commitbae94d02371c402408a4edfb95e71e88dbd3e973 (patch)
tree8886acf5950d8f95d5d4d5a9737c462035709914 /drivers/pci/pci-driver.c
parent039d09a845209122c5193e650ab2d8b3c849ca7c (diff)
PCI: switch pci_{enable,disable}_device() to be nestable
Changes the pci_{enable,disable}_device() functions to work in a nested basis, so that eg, three calls to enable_device() require three calls to disable_device(). The reason for this is to simplify PCI drivers for multi-interface/capability devices. These are devices that cram more than one interface in a single function. A relevant example of that is the Wireless [USB] Host Controller Interface (similar to EHCI) [see http://www.intel.com/technology/comms/wusb/whci.htm]. In these kind of devices, multiple interfaces are accessed through a single bar and IRQ line. For that, the drivers map only the smallest area of the bar to access their register banks and use shared IRQ handlers. However, because the order at which those drivers load cannot be known ahead of time, the sequence in which the calls to pci_enable_device() and pci_disable_device() cannot be predicted. Thus: 1. driverA starts pci_enable_device() 2. driverB starts pci_enable_device() 3. driverA shutdown pci_disable_device() 4. driverB shutdown pci_disable_device() between steps 3 and 4, driver B would loose access to it's device, even if it didn't intend to. By using this modification, the device won't be disabled until all the callers to enable() have called disable(). This is implemented by replacing 'struct pci_dev->is_enabled' from a bitfield to an atomic use count. Each caller to enable increments it, each caller to disable decrements it. When the count increments from 0 to 1, __pci_enable_device() is called to actually enable the device. When it drops to zero, pci_disable_device() actually does the disabling. We keep the backend __pci_enable_device() for pci_default_resume() to use and also change the sysfs method implementation, so that userspace enabling/disabling the device doesn't disable it one time too much. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r--drivers/pci/pci-driver.c4
1 files changed, 2 insertions, 2 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);