]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/pci/pci.c
PCI: fix ARI code to be compatible with mixed ARI/non-ARI systems
[linux-2.6-omap-h63xx.git] / drivers / pci / pci.c
index aee73cf251b6bb7c90a83038f97d5ba3dad216bf..21f2ac639cab2f31937971113fe9872275cfd9a0 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
+#include <linux/interrupt.h>
 #include <asm/dma.h>   /* isa_dma_bridge_buggy */
 #include "pci.h"
 
@@ -1308,27 +1309,32 @@ void pci_enable_ari(struct pci_dev *dev)
        int pos;
        u32 cap;
        u16 ctrl;
+       struct pci_dev *bridge;
 
-       if (!dev->is_pcie)
+       if (!dev->is_pcie || dev->devfn)
                return;
 
-       if (dev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
-           dev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
+       if (!pos)
                return;
 
-       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+       bridge = dev->bus->self;
+       if (!bridge || !bridge->is_pcie)
+               return;
+
+       pos = pci_find_capability(bridge, PCI_CAP_ID_EXP);
        if (!pos)
                return;
 
-       pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap);
+       pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap);
        if (!(cap & PCI_EXP_DEVCAP2_ARI))
                return;
 
-       pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
+       pci_read_config_word(bridge, pos + PCI_EXP_DEVCTL2, &ctrl);
        ctrl |= PCI_EXP_DEVCTL2_ARI;
-       pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+       pci_write_config_word(bridge, pos + PCI_EXP_DEVCTL2, ctrl);
 
-       dev->ari_enabled = 1;
+       bridge->ari_enabled = 1;
 }
 
 int
@@ -1745,6 +1751,103 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask)
 EXPORT_SYMBOL(pci_set_dma_seg_boundary);
 #endif
 
+/**
+ * pci_execute_reset_function() - Reset a PCI device function
+ * @dev: Device function to reset
+ *
+ * Some devices allow an individual function to be reset without affecting
+ * other functions in the same device.  The PCI device must be responsive
+ * to PCI config space in order to use this function.
+ *
+ * The device function is presumed to be unused when this function is called.
+ * Resetting the device will make the contents of PCI configuration space
+ * random, so any caller of this must be prepared to reinitialise the
+ * device including MSI, bus mastering, BARs, decoding IO and memory spaces,
+ * etc.
+ *
+ * Returns 0 if the device function was successfully reset or -ENOTTY if the
+ * device doesn't support resetting a single function.
+ */
+int pci_execute_reset_function(struct pci_dev *dev)
+{
+       u16 status;
+       u32 cap;
+       int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+
+       if (!exppos)
+               return -ENOTTY;
+       pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap);
+       if (!(cap & PCI_EXP_DEVCAP_FLR))
+               return -ENOTTY;
+
+       pci_block_user_cfg_access(dev);
+
+       /* Wait for Transaction Pending bit clean */
+       msleep(100);
+       pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
+       if (status & PCI_EXP_DEVSTA_TRPND) {
+               dev_info(&dev->dev, "Busy after 100ms while trying to reset; "
+                       "sleeping for 1 second\n");
+               ssleep(1);
+               pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status);
+               if (status & PCI_EXP_DEVSTA_TRPND)
+                       dev_info(&dev->dev, "Still busy after 1s; "
+                               "proceeding with reset anyway\n");
+       }
+
+       pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL,
+                               PCI_EXP_DEVCTL_BCR_FLR);
+       mdelay(100);
+
+       pci_unblock_user_cfg_access(dev);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pci_execute_reset_function);
+
+/**
+ * pci_reset_function() - quiesce and reset a PCI device function
+ * @dev: Device function to reset
+ *
+ * Some devices allow an individual function to be reset without affecting
+ * other functions in the same device.  The PCI device must be responsive
+ * to PCI config space in order to use this function.
+ *
+ * This function does not just reset the PCI portion of a device, but
+ * clears all the state associated with the device.  This function differs
+ * from pci_execute_reset_function in that it saves and restores device state
+ * over the reset.
+ *
+ * Returns 0 if the device function was successfully reset or -ENOTTY if the
+ * device doesn't support resetting a single function.
+ */
+int pci_reset_function(struct pci_dev *dev)
+{
+       u32 cap;
+       int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+       int r;
+
+       if (!exppos)
+               return -ENOTTY;
+       pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap);
+       if (!(cap & PCI_EXP_DEVCAP_FLR))
+               return -ENOTTY;
+
+       if (!dev->msi_enabled && !dev->msix_enabled)
+               disable_irq(dev->irq);
+       pci_save_state(dev);
+
+       pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+
+       r = pci_execute_reset_function(dev);
+
+       pci_restore_state(dev);
+       if (!dev->msi_enabled && !dev->msix_enabled)
+               enable_irq(dev->irq);
+
+       return r;
+}
+EXPORT_SYMBOL_GPL(pci_reset_function);
+
 /**
  * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
  * @dev: PCI device to query