]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/pci/pci.c
PCI PM: Register power state of devices during initialization
[linux-2.6-omap-h63xx.git] / drivers / pci / pci.c
index deeab19d7d102a5b2879cb21e5adfcf8437abc2e..c12f6c7906980fba382d4b42fcc9d02305248c18 100644 (file)
@@ -525,14 +525,17 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
  * pci_update_current_state - Read PCI power state of given device from its
  *                            PCI PM registers and cache it
  * @dev: PCI device to handle.
+ * @state: State to cache in case the device doesn't have the PM capability
  */
-static void pci_update_current_state(struct pci_dev *dev)
+void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
 {
        if (dev->pm_cap) {
                u16 pmcsr;
 
                pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
                dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
+       } else {
+               dev->current_state = state;
        }
 }
 
@@ -575,7 +578,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
                 */
                int ret = platform_pci_set_power_state(dev, PCI_D0);
                if (!ret)
-                       pci_update_current_state(dev);
+                       pci_update_current_state(dev, PCI_D0);
        }
        /* This device is quirked not to be put into D3, so
           don't put it in D3 */
@@ -588,7 +591,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
                /* Allow the platform to finalize the transition */
                int ret = platform_pci_set_power_state(dev, state);
                if (!ret) {
-                       pci_update_current_state(dev);
+                       pci_update_current_state(dev, state);
                        error = 0;
                }
        }
@@ -967,6 +970,32 @@ void pcim_pin_device(struct pci_dev *pdev)
  */
 void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {}
 
+static void do_pci_disable_device(struct pci_dev *dev)
+{
+       u16 pci_command;
+
+       pci_read_config_word(dev, PCI_COMMAND, &pci_command);
+       if (pci_command & PCI_COMMAND_MASTER) {
+               pci_command &= ~PCI_COMMAND_MASTER;
+               pci_write_config_word(dev, PCI_COMMAND, pci_command);
+       }
+
+       pcibios_disable_device(dev);
+}
+
+/**
+ * pci_disable_enabled_device - Disable device without updating enable_cnt
+ * @dev: PCI device to disable
+ *
+ * NOTE: This function is a backend of PCI power management routines and is
+ * not supposed to be called drivers.
+ */
+void pci_disable_enabled_device(struct pci_dev *dev)
+{
+       if (atomic_read(&dev->enable_cnt))
+               do_pci_disable_device(dev);
+}
+
 /**
  * pci_disable_device - Disable PCI device after use
  * @dev: PCI device to be disabled
@@ -981,7 +1010,6 @@ void
 pci_disable_device(struct pci_dev *dev)
 {
        struct pci_devres *dr;
-       u16 pci_command;
 
        dr = find_pci_dr(dev);
        if (dr)
@@ -990,14 +1018,9 @@ pci_disable_device(struct pci_dev *dev)
        if (atomic_sub_return(1, &dev->enable_cnt) != 0)
                return;
 
-       pci_read_config_word(dev, PCI_COMMAND, &pci_command);
-       if (pci_command & PCI_COMMAND_MASTER) {
-               pci_command &= ~PCI_COMMAND_MASTER;
-               pci_write_config_word(dev, PCI_COMMAND, pci_command);
-       }
-       dev->is_busmaster = 0;
+       do_pci_disable_device(dev);
 
-       pcibios_disable_device(dev);
+       dev->is_busmaster = 0;
 }
 
 /**
@@ -1237,14 +1260,15 @@ void pci_pm_init(struct pci_dev *dev)
        /* find PCI PM capability in list */
        pm = pci_find_capability(dev, PCI_CAP_ID_PM);
        if (!pm)
-               return;
+               goto Exit;
+
        /* Check device's ability to generate PME# */
        pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
 
        if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
                dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
                        pmc & PCI_PM_CAP_VER_MASK);
-               return;
+               goto Exit;
        }
 
        dev->pm_cap = pm;
@@ -1283,6 +1307,29 @@ void pci_pm_init(struct pci_dev *dev)
        } else {
                dev->pme_support = 0;
        }
+
+ Exit:
+       pci_update_current_state(dev, PCI_D0);
+}
+
+/**
+ * platform_pci_wakeup_init - init platform wakeup if present
+ * @dev: PCI device
+ *
+ * Some devices don't have PCI PM caps but can still generate wakeup
+ * events through platform methods (like ACPI events).  If @dev supports
+ * platform wakeup events, set the device flag to indicate as much.  This
+ * may be redundant if the device also supports PCI PM caps, but double
+ * initialization should be safe in that case.
+ */
+void platform_pci_wakeup_init(struct pci_dev *dev)
+{
+       if (!platform_pci_can_wakeup(dev))
+               return;
+
+       device_set_wakeup_capable(&dev->dev, true);
+       device_set_wakeup_enable(&dev->dev, false);
+       platform_pci_sleep_wake(dev, false);
 }
 
 /**
@@ -1398,6 +1445,26 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
        return pin;
 }
 
+/**
+ * pci_common_swizzle - swizzle INTx all the way to root bridge
+ * @dev: the PCI device
+ * @pinp: pointer to the INTx pin value (1=INTA, 2=INTB, 3=INTD, 4=INTD)
+ *
+ * Perform INTx swizzling for a device.  This traverses through all PCI-to-PCI
+ * bridges all the way up to a PCI root bus.
+ */
+u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp)
+{
+       u8 pin = *pinp;
+
+       while (dev->bus->self) {
+               pin = pci_swizzle_interrupt_pin(dev, pin);
+               dev = dev->bus->self;
+       }
+       *pinp = pin;
+       return PCI_SLOT(dev->devfn);
+}
+
 /**
  *     pci_release_region - Release a PCI bar
  *     @pdev: PCI device whose resources were previously reserved by pci_request_region
@@ -1624,6 +1691,22 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
                                        ((1 << 6) - 1), res_name);
 }
 
+static void __pci_set_master(struct pci_dev *dev, bool enable)
+{
+       u16 old_cmd, cmd;
+
+       pci_read_config_word(dev, PCI_COMMAND, &old_cmd);
+       if (enable)
+               cmd = old_cmd | PCI_COMMAND_MASTER;
+       else
+               cmd = old_cmd & ~PCI_COMMAND_MASTER;
+       if (cmd != old_cmd) {
+               dev_dbg(&dev->dev, "%s bus mastering\n",
+                       enable ? "enabling" : "disabling");
+               pci_write_config_word(dev, PCI_COMMAND, cmd);
+       }
+       dev->is_busmaster = enable;
+}
 
 /**
  * pci_set_master - enables bus-mastering for device dev
@@ -1632,21 +1715,21 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
  * Enables bus-mastering on the device and calls pcibios_set_master()
  * to do the needed arch specific settings.
  */
-void
-pci_set_master(struct pci_dev *dev)
+void pci_set_master(struct pci_dev *dev)
 {
-       u16 cmd;
-
-       pci_read_config_word(dev, PCI_COMMAND, &cmd);
-       if (! (cmd & PCI_COMMAND_MASTER)) {
-               dev_dbg(&dev->dev, "enabling bus mastering\n");
-               cmd |= PCI_COMMAND_MASTER;
-               pci_write_config_word(dev, PCI_COMMAND, cmd);
-       }
-       dev->is_busmaster = 1;
+       __pci_set_master(dev, true);
        pcibios_set_master(dev);
 }
 
+/**
+ * pci_clear_master - disables bus-mastering for device dev
+ * @dev: the PCI device to disable
+ */
+void pci_clear_master(struct pci_dev *dev)
+{
+       __pci_set_master(dev, false);
+}
+
 #ifdef PCI_DISABLE_MWI
 int pci_set_mwi(struct pci_dev *dev)
 {
@@ -2201,6 +2284,28 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags)
        return bars;
 }
 
+/**
+ * pci_resource_bar - get position of the BAR associated with a resource
+ * @dev: the PCI device
+ * @resno: the resource number
+ * @type: the BAR type to be filled in
+ *
+ * Returns BAR position in config space, or 0 if the BAR is invalid.
+ */
+int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type)
+{
+       if (resno < PCI_ROM_RESOURCE) {
+               *type = pci_bar_unknown;
+               return PCI_BASE_ADDRESS_0 + 4 * resno;
+       } else if (resno == PCI_ROM_RESOURCE) {
+               *type = pci_bar_mem32;
+               return dev->rom_base_reg;
+       }
+
+       dev_err(&dev->dev, "BAR: invalid resource #%d\n", resno);
+       return 0;
+}
+
 static void __devinit pci_no_domains(void)
 {
 #ifdef CONFIG_PCI_DOMAINS
@@ -2281,6 +2386,7 @@ EXPORT_SYMBOL(pci_release_selected_regions);
 EXPORT_SYMBOL(pci_request_selected_regions);
 EXPORT_SYMBOL(pci_request_selected_regions_exclusive);
 EXPORT_SYMBOL(pci_set_master);
+EXPORT_SYMBOL(pci_clear_master);
 EXPORT_SYMBOL(pci_set_mwi);
 EXPORT_SYMBOL(pci_try_set_mwi);
 EXPORT_SYMBOL(pci_clear_mwi);