]> 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 e1a17b85447bb94cb7d8f2545b572ba7f67606ea..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"
 
@@ -213,10 +214,13 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap)
 int pci_find_ext_capability(struct pci_dev *dev, int cap)
 {
        u32 header;
-       int ttl = 480; /* 3840 bytes, minimum 8 bytes per capability */
-       int pos = 0x100;
+       int ttl;
+       int pos = PCI_CFG_SPACE_SIZE;
 
-       if (dev->cfg_size <= 256)
+       /* minimum 8 bytes per capability */
+       ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
+
+       if (dev->cfg_size <= PCI_CFG_SPACE_SIZE)
                return 0;
 
        if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
@@ -234,7 +238,7 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
                        return pos;
 
                pos = PCI_EXT_CAP_NEXT(header);
-               if (pos < 0x100)
+               if (pos < PCI_CFG_SPACE_SIZE)
                        break;
 
                if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
@@ -1270,8 +1274,8 @@ void pci_pm_init(struct pci_dev *dev)
 
                if (dev->d1_support || dev->d2_support)
                        dev_printk(KERN_DEBUG, &dev->dev, "supports%s%s\n",
-                                  dev->d1_support ? " D1": "",
-                                  dev->d2_support ? " D2": "");
+                                  dev->d1_support ? " D1" : "",
+                                  dev->d2_support ? " D2" : "");
        }
 
        pmc &= PCI_PM_CAP_PME_MASK;
@@ -1296,6 +1300,43 @@ void pci_pm_init(struct pci_dev *dev)
        }
 }
 
+/**
+ * pci_enable_ari - enable ARI forwarding if hardware support it
+ * @dev: the PCI device
+ */
+void pci_enable_ari(struct pci_dev *dev)
+{
+       int pos;
+       u32 cap;
+       u16 ctrl;
+       struct pci_dev *bridge;
+
+       if (!dev->is_pcie || dev->devfn)
+               return;
+
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
+       if (!pos)
+               return;
+
+       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(bridge, pos + PCI_EXP_DEVCAP2, &cap);
+       if (!(cap & PCI_EXP_DEVCAP2_ARI))
+               return;
+
+       pci_read_config_word(bridge, pos + PCI_EXP_DEVCTL2, &ctrl);
+       ctrl |= PCI_EXP_DEVCTL2_ARI;
+       pci_write_config_word(bridge, pos + PCI_EXP_DEVCTL2, ctrl);
+
+       bridge->ari_enabled = 1;
+}
+
 int
 pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
 {
@@ -1710,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
@@ -1898,6 +2036,9 @@ static int __devinit pci_init(void)
        while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
                pci_fixup_device(pci_fixup_final, dev);
        }
+
+       msi_init();
+
        return 0;
 }