]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/pci/pci.c
PCI ACPI: Rework PCI handling of wake-up
[linux-2.6-omap-h63xx.git] / drivers / pci / pci.c
index 183fddaa38b74e5854e386c18266b81814eaa5b9..a6b1b6f96abc6c7124b9b22b3ffec56a3513d462 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *     $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $
- *
  *     PCI Bus Services, see include/linux/pci.h for further explanation.
  *
  *     Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
@@ -18,6 +16,7 @@
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/log2.h>
+#include <linux/pci-aspm.h>
 #include <asm/dma.h>   /* isa_dma_bridge_buggy */
 #include "pci.h"
 
@@ -314,24 +313,6 @@ int pci_find_ht_capability(struct pci_dev *dev, int ht_cap)
 }
 EXPORT_SYMBOL_GPL(pci_find_ht_capability);
 
-void pcie_wait_pending_transaction(struct pci_dev *dev)
-{
-       int pos;
-       u16 reg16;
-
-       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
-       if (!pos)
-               return;
-       while (1) {
-               pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
-               if (!(reg16 & PCI_EXP_DEVSTA_TRPND))
-                       break;
-               cpu_relax();
-       }
-
-}
-EXPORT_SYMBOL_GPL(pcie_wait_pending_transaction);
-
 /**
  * pci_find_parent_resource - return resource region of parent bus of given region
  * @dev: PCI device structure contains resources to be searched
@@ -395,71 +376,96 @@ pci_restore_bars(struct pci_dev *dev)
                pci_update_resource(dev, &dev->resource[i], i);
 }
 
-int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);
+static struct pci_platform_pm_ops *pci_platform_pm;
+
+int pci_set_platform_pm(struct pci_platform_pm_ops *ops)
+{
+       if (!ops->is_manageable || !ops->set_state || !ops->choose_state
+           || !ops->sleep_wake || !ops->can_wakeup)
+               return -EINVAL;
+       pci_platform_pm = ops;
+       return 0;
+}
+
+static inline bool platform_pci_power_manageable(struct pci_dev *dev)
+{
+       return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false;
+}
+
+static inline int platform_pci_set_power_state(struct pci_dev *dev,
+                                                pci_power_t t)
+{
+       return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS;
+}
+
+static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
+{
+       return pci_platform_pm ?
+                       pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR;
+}
+
+static inline bool platform_pci_can_wakeup(struct pci_dev *dev)
+{
+       return pci_platform_pm ? pci_platform_pm->can_wakeup(dev) : false;
+}
+
+static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
+{
+       return pci_platform_pm ?
+                       pci_platform_pm->sleep_wake(dev, enable) : -ENODEV;
+}
 
 /**
- * pci_set_power_state - Set the power state of a PCI device
- * @dev: PCI device to be suspended
- * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering
+ * pci_raw_set_power_state - Use PCI PM registers to set the power state of
+ *                           given PCI device
+ * @dev: PCI device to handle.
+ * @pm: PCI PM capability offset of the device.
+ * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
  *
- * Transition a device to a new power state, using the Power Management 
- * Capabilities in the device's config space.
- *
- * RETURN VALUE: 
- * -EINVAL if trying to enter a lower state than we're already in.
- * 0 if we're already in the requested state.
- * -EIO if device does not support PCI PM.
- * 0 if we can successfully change the power state.
+ * RETURN VALUE:
+ * -EINVAL if the requested state is invalid.
+ * -EIO if device does not support PCI PM or its PM capabilities register has a
+ * wrong version, or device doesn't support the requested state.
+ * 0 if device already is in the requested state.
+ * 0 if device's power state has been successfully changed.
  */
-int
-pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+static int
+pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state)
 {
-       int pm, need_restore = 0;
        u16 pmcsr, pmc;
+       bool need_restore = false;
 
-       /* bound the state we're entering */
-       if (state > PCI_D3hot)
-               state = PCI_D3hot;
-
-       /*
-        * If the device or the parent bridge can't support PCI PM, ignore
-        * the request if we're doing anything besides putting it into D0
-        * (which would only happen on boot).
-        */
-       if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
-               return 0;
-
-       /* find PCI PM capability in list */
-       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-
-       /* abort if the device doesn't support PM capabilities */
        if (!pm)
                return -EIO;
 
+       if (state < PCI_D0 || state > PCI_D3hot)
+               return -EINVAL;
+
        /* Validate current state:
         * Can enter D0 from any state, but if we can only go deeper 
         * to sleep if we're already in a low power state
         */
-       if (state != PCI_D0 && dev->current_state > state) {
-               printk(KERN_ERR "%s(): %s: state=%d, current state=%d\n",
-                       __FUNCTION__, pci_name(dev), state, dev->current_state);
+       if (dev->current_state == state) {
+               /* we're already there */
+               return 0;
+       } else if (state != PCI_D0 && dev->current_state <= PCI_D3cold
+           && dev->current_state > state) {
+               dev_err(&dev->dev, "invalid power transition "
+                       "(from state %d to %d)\n", dev->current_state, state);
                return -EINVAL;
-       } else if (dev->current_state == state)
-               return 0;        /* we're already there */
+       }
 
+       pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
 
-       pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
        if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
-               printk(KERN_DEBUG
-                      "PCI: %s has unsupported PM cap regs version (%u)\n",
-                      pci_name(dev), pmc & PCI_PM_CAP_VER_MASK);
+               dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
+                       pmc & PCI_PM_CAP_VER_MASK);
                return -EIO;
        }
 
        /* check if this device supports the desired state */
-       if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
-               return -EIO;
-       else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
+       if ((state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
+          || (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2)))
                return -EIO;
 
        pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
@@ -478,7 +484,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        case PCI_UNKNOWN: /* Boot-up */
                if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
                 && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
-                       need_restore = 1;
+                       need_restore = true;
                /* Fall-through: force to D0 */
        default:
                pmcsr = 0;
@@ -495,13 +501,6 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        else if (state == PCI_D2 || dev->current_state == PCI_D2)
                udelay(200);
 
-       /*
-        * Give firmware a chance to be called, such as ACPI _PRx, _PSx
-        * Firmware method after native method ?
-        */
-       if (platform_pci_set_power_state)
-               platform_pci_set_power_state(dev, state);
-
        dev->current_state = state;
 
        /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
@@ -519,11 +518,87 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        if (need_restore)
                pci_restore_bars(dev);
 
+       if (dev->bus->self)
+               pcie_aspm_pm_state_change(dev->bus->self);
+
        return 0;
 }
 
-pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_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.
+ * @pm: PCI PM capability offset of the device.
+ */
+static void pci_update_current_state(struct pci_dev *dev, int pm)
+{
+       if (pm) {
+               u16 pmcsr;
+
+               pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
+               dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
+       }
+}
+
+/**
+ * pci_set_power_state - Set the power state of a PCI device
+ * @dev: PCI device to handle.
+ * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
+ *
+ * Transition a device to a new power state, using the platform formware and/or
+ * the device's PCI PM registers.
+ *
+ * RETURN VALUE:
+ * -EINVAL if the requested state is invalid.
+ * -EIO if device does not support PCI PM or its PM capabilities register has a
+ * wrong version, or device doesn't support the requested state.
+ * 0 if device already is in the requested state.
+ * 0 if device's power state has been successfully changed.
+ */
+int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+       int pm, error;
+
+       /* bound the state we're entering */
+       if (state > PCI_D3hot)
+               state = PCI_D3hot;
+       else if (state < PCI_D0)
+               state = PCI_D0;
+       else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
+               /*
+                * If the device or the parent bridge do not support PCI PM,
+                * ignore the request if we're doing anything other than putting
+                * it into D0 (which would only happen on boot).
+                */
+               return 0;
+
+       /* Find PCI PM capability in the list */
+       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+
+       if (state == PCI_D0 && platform_pci_power_manageable(dev)) {
+               /*
+                * Allow the platform to change the state, for example via ACPI
+                * _PR0, _PS0 and some such, but do not trust it.
+                */
+               int ret = platform_pci_set_power_state(dev, PCI_D0);
+               if (!ret)
+                       pci_update_current_state(dev, pm);
+       }
+
+       error = pci_raw_set_power_state(dev, pm, state);
+
+       if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
+               /* Allow the platform to finalize the transition */
+               int ret = platform_pci_set_power_state(dev, state);
+               if (!ret) {
+                       pci_update_current_state(dev, pm);
+                       error = 0;
+               }
+       }
+
+       return error;
+}
+
 /**
  * pci_choose_state - Choose the power state of a PCI device
  * @dev: PCI device to be suspended
@@ -541,11 +616,9 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
        if (!pci_find_capability(dev, PCI_CAP_ID_PM))
                return PCI_D0;
 
-       if (platform_pci_choose_state) {
-               ret = platform_pci_choose_state(dev, state);
-               if (ret != PCI_POWER_ERROR)
-                       return ret;
-       }
+       ret = platform_pci_choose_state(dev);
+       if (ret != PCI_POWER_ERROR)
+               return ret;
 
        switch (state.event) {
        case PM_EVENT_ON:
@@ -557,7 +630,8 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
        case PM_EVENT_HIBERNATE:
                return PCI_D3hot;
        default:
-               printk("Unrecognized suspend event %d\n", state.event);
+               dev_info(&dev->dev, "unrecognized suspend event %d\n",
+                        state.event);
                BUG();
        }
        return PCI_D0;
@@ -582,7 +656,7 @@ static int pci_save_pcie_state(struct pci_dev *dev)
        else
                found = 1;
        if (!save_state) {
-               dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+               dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n");
                return -ENOMEM;
        }
        cap = (u16 *)&save_state->data[0];
@@ -633,7 +707,7 @@ static int pci_save_pcix_state(struct pci_dev *dev)
        else
                found = 1;
        if (!save_state) {
-               dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+               dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n");
                return -ENOMEM;
        }
        cap = (u16 *)&save_state->data[0];
@@ -699,10 +773,9 @@ pci_restore_state(struct pci_dev *dev)
        for (i = 15; i >= 0; i--) {
                pci_read_config_dword(dev, i * 4, &val);
                if (val != dev->saved_config_space[i]) {
-                       printk(KERN_DEBUG "PM: Writing back config space on "
-                               "device %s at offset %x (was %x, writing %x)\n",
-                               pci_name(dev), i,
-                               val, (int)dev->saved_config_space[i]);
+                       dev_printk(KERN_DEBUG, &dev->dev, "restoring config "
+                               "space at offset %#x (was %#x, writing %#x)\n",
+                               i, val, (int)dev->saved_config_space[i]);
                        pci_write_config_dword(dev,i * 4,
                                dev->saved_config_space[i]);
                }
@@ -936,9 +1009,6 @@ pci_disable_device(struct pci_dev *dev)
        if (atomic_sub_return(1, &dev->enable_cnt) != 0)
                return;
 
-       /* Wait for all transactions are finished before disabling the device */
-       pcie_wait_pending_transaction(dev);
-
        pci_read_config_word(dev, PCI_COMMAND, &pci_command);
        if (pci_command & PCI_COMMAND_MASTER) {
                pci_command &= ~PCI_COMMAND_MASTER;
@@ -977,6 +1047,56 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
        return pcibios_set_pcie_reset_state(dev, state);
 }
 
+/**
+ * pci_pme_capable - check the capability of PCI device to generate PME#
+ * @dev: PCI device to handle.
+ * @pm: PCI PM capability offset of the device.
+ * @state: PCI state from which device will issue PME#.
+ */
+static bool pci_pme_capable(struct pci_dev *dev, int pm, pci_power_t state)
+{
+       u16 pmc;
+
+       if (!pm)
+               return false;
+
+       /* Check device's ability to generate PME# from given state */
+       pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
+
+       pmc &= PCI_PM_CAP_PME_MASK;
+       pmc >>= ffs(PCI_PM_CAP_PME_MASK) - 1;   /* First bit of mask */
+
+       return !!(pmc & (1 << state));
+}
+
+/**
+ * pci_pme_active - enable or disable PCI device's PME# function
+ * @dev: PCI device to handle.
+ * @pm: PCI PM capability offset of the device.
+ * @enable: 'true' to enable PME# generation; 'false' to disable it.
+ *
+ * The caller must verify that the device is capable of generating PME# before
+ * calling this function with @enable equal to 'true'.
+ */
+static void pci_pme_active(struct pci_dev *dev, int pm, bool enable)
+{
+       u16 pmcsr;
+
+       if (!pm)
+               return;
+
+       pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
+       /* Clear PME_Status by writing 1 to it and enable PME# */
+       pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
+       if (!enable)
+               pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
+
+       pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
+
+       dev_printk(KERN_INFO, &dev->dev, "PME# %s\n",
+                       enable ? "enabled" : "disabled");
+}
+
 /**
  * pci_enable_wake - enable PCI device as wakeup event source
  * @dev: PCI device affected
@@ -988,66 +1108,83 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
  * called automatically by this routine.
  *
  * Devices with legacy power management (no standard PCI PM capabilities)
- * always require such platform hooks.  Depending on the platform, devices
- * supporting the standard PCI PME# signal may require such platform hooks;
- * they always update bits in config space to allow PME# generation.
+ * always require such platform hooks.
  *
- * -EIO is returned if the device can't ever be a wakeup event source.
- * -EINVAL is returned if the device can't generate wakeup events from
- * the specified PCI state.  Returns zero if the operation is successful.
+ * RETURN VALUE:
+ * 0 is returned on success
+ * -EINVAL is returned if device is not supposed to wake up the system
+ * Error code depending on the platform is returned if both the platform and
+ * the native mechanism fail to enable the generation of wake-up events
  */
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
 {
        int pm;
-       int status;
-       u16 value;
-
-       /* Note that drivers should verify device_may_wakeup(&dev->dev)
-        * before calling this function.  Platform code should report
-        * errors when drivers try to enable wakeup on devices that
-        * can't issue wakeups, or on which wakeups were disabled by
-        * userspace updating the /sys/devices.../power/wakeup file.
-        */
+       int error = 0;
+       bool pme_done = false;
 
-       status = call_platform_enable_wakeup(&dev->dev, enable);
-
-       /* find PCI PM capability in list */
-       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+       if (!device_may_wakeup(&dev->dev))
+               return -EINVAL;
 
-       /* If device doesn't support PM Capabilities, but caller wants to
-        * disable wake events, it's a NOP.  Otherwise fail unless the
-        * platform hooks handled this legacy device already.
+       /*
+        * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don
+        * Anderson we should be doing PME# wake enable followed by ACPI wake
+        * enable.  To disable wake-up we call the platform first, for symmetry.
         */
-       if (!pm)
-               return enable ? status : 0;
 
-       /* Check device's ability to generate PME# */
-       pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
+       if (!enable && platform_pci_can_wakeup(dev))
+               error = platform_pci_sleep_wake(dev, false);
 
-       value &= PCI_PM_CAP_PME_MASK;
-       value >>= ffs(PCI_PM_CAP_PME_MASK) - 1;   /* First bit of mask */
-
-       /* Check if it can generate PME# from requested state. */
-       if (!value || !(value & (1 << state))) {
-               /* if it can't, revert what the platform hook changed,
-                * always reporting the base "EINVAL, can't PME#" error
-                */
-               if (enable)
-                       call_platform_enable_wakeup(&dev->dev, 0);
-               return enable ? -EINVAL : 0;
+       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+       if (!enable || pci_pme_capable(dev, pm, state)) {
+               pci_pme_active(dev, pm, enable);
+               pme_done = true;
        }
 
-       pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
+       if (enable && platform_pci_can_wakeup(dev))
+               error = platform_pci_sleep_wake(dev, true);
 
-       /* Clear PME_Status by writing 1 to it and enable PME# */
-       value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
+       return pme_done ? 0 : error;
+}
 
-       if (!enable)
-               value &= ~PCI_PM_CTRL_PME_ENABLE;
+/**
+ * pci_pm_init - Initialize PM functions of given PCI device
+ * @dev: PCI device to handle.
+ */
+void pci_pm_init(struct pci_dev *dev)
+{
+       int pm;
+       u16 pmc;
+
+       /* find PCI PM capability in list */
+       pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+       if (!pm)
+               return;
+       /* Check device's ability to generate PME# */
+       pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
 
-       pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
+       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;
+       }
 
-       return 0;
+       if (pmc & PCI_PM_CAP_PME_MASK) {
+               dev_printk(KERN_INFO, &dev->dev,
+                       "PME# supported from%s%s%s%s%s\n",
+                       (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "",
+                       (pmc & PCI_PM_CAP_PME_D1) ? " D1" : "",
+                       (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
+                       (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "",
+                       (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : "");
+               /*
+                * Make device's PM flags reflect the wake-up capability, but
+                * let the user space enable it to wake up the system as needed.
+                */
+               device_set_wakeup_capable(&dev->dev, true);
+               device_set_wakeup_enable(&dev->dev, false);
+               /* Disable the PME# generation functionality */
+               pci_pme_active(dev, pm, false);
+       }
 }
 
 int
@@ -1133,13 +1270,11 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
        return 0;
 
 err_out:
-       printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%llx@%llx "
-               "for device %s\n",
-               pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
-               bar + 1, /* PCI BAR # */
-               (unsigned long long)pci_resource_len(pdev, bar),
-               (unsigned long long)pci_resource_start(pdev, bar),
-               pci_name(pdev));
+       dev_warn(&pdev->dev, "BAR %d: can't reserve %s region [%#llx-%#llx]\n",
+                bar,
+                pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
+                (unsigned long long)pci_resource_start(pdev, bar),
+                (unsigned long long)pci_resource_end(pdev, bar));
        return -EBUSY;
 }
 
@@ -1231,7 +1366,7 @@ pci_set_master(struct pci_dev *dev)
 
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (! (cmd & PCI_COMMAND_MASTER)) {
-               pr_debug("PCI: Enabling bus mastering for device %s\n", pci_name(dev));
+               dev_dbg(&dev->dev, "enabling bus mastering\n");
                cmd |= PCI_COMMAND_MASTER;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
@@ -1296,8 +1431,8 @@ pci_set_cacheline_size(struct pci_dev *dev)
        if (cacheline_size == pci_cache_line_size)
                return 0;
 
-       printk(KERN_DEBUG "PCI: cache line size of %d is not supported "
-              "by device %s\n", pci_cache_line_size << 2, pci_name(dev));
+       dev_printk(KERN_DEBUG, &dev->dev, "cache line size of %d is not "
+                  "supported\n", pci_cache_line_size << 2);
 
        return -EINVAL;
 }
@@ -1322,8 +1457,7 @@ pci_set_mwi(struct pci_dev *dev)
 
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (! (cmd & PCI_COMMAND_INVALIDATE)) {
-               pr_debug("PCI: Enabling Mem-Wr-Inval for device %s\n",
-                       pci_name(dev));
+               dev_dbg(&dev->dev, "enabling Mem-Wr-Inval\n");
                cmd |= PCI_COMMAND_INVALIDATE;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }