]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
PCI: Add accessors for PCI Express Capability
authorJiang Liu <jiang.liu@huawei.com>
Tue, 24 Jul 2012 09:20:05 +0000 (17:20 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 23 Aug 2012 15:41:20 +0000 (09:41 -0600)
The PCI Express Capability (PCIe spec r3.0, sec 7.8) comes in two
versions, v1 and v2.  In v1 Capability structures (PCIe spec r1.0 and
r1.1), some fields are optional, so the structure size depends on the
device type.

This patch adds functions to access this capability so drivers don't
have to be aware of the differences between v1 and v2.  Note that these
new functions apply only to the "PCI Express Capability," not to any of
the other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.)

Function pcie_capability_read_word/dword() reads the PCIe Capabilities
register and returns the value in the reference parameter "val".  If
the PCIe Capabilities register is not implemented on the PCIe device,
"val" is set to 0.

Function pcie_capability_write_word/dword() writes the value to the
specified PCIe Capability register.

Function pcie_capability_clear_and_set_word/dword() sets and/or clears bits
of a PCIe Capability register.

[bhelgaas: changelog, drop "pci_" prefixes, don't export
pcie_capability_reg_implemented()]
Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/access.c
include/linux/pci.h
include/linux/pci_regs.h

index ba91a7e17519252e89f1fa112b4d89ce5da088d2..3af0478c057bdbf8253f2ad62cebaadef22f3656 100644 (file)
@@ -469,3 +469,205 @@ void pci_cfg_access_unlock(struct pci_dev *dev)
        raw_spin_unlock_irqrestore(&pci_lock, flags);
 }
 EXPORT_SYMBOL_GPL(pci_cfg_access_unlock);
+
+static inline int pcie_cap_version(const struct pci_dev *dev)
+{
+       return dev->pcie_flags_reg & PCI_EXP_FLAGS_VERS;
+}
+
+static inline bool pcie_cap_has_devctl(const struct pci_dev *dev)
+{
+       return true;
+}
+
+static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
+{
+       int type = pci_pcie_type(dev);
+
+       return pcie_cap_version(dev) > 1 ||
+              type == PCI_EXP_TYPE_ROOT_PORT ||
+              type == PCI_EXP_TYPE_ENDPOINT ||
+              type == PCI_EXP_TYPE_LEG_END;
+}
+
+static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
+{
+       int type = pci_pcie_type(dev);
+
+       return pcie_cap_version(dev) > 1 ||
+              type == PCI_EXP_TYPE_ROOT_PORT ||
+              (type == PCI_EXP_TYPE_DOWNSTREAM &&
+               dev->pcie_flags_reg & PCI_EXP_FLAGS_SLOT);
+}
+
+static inline bool pcie_cap_has_rtctl(const struct pci_dev *dev)
+{
+       int type = pci_pcie_type(dev);
+
+       return pcie_cap_version(dev) > 1 ||
+              type == PCI_EXP_TYPE_ROOT_PORT ||
+              type == PCI_EXP_TYPE_RC_EC;
+}
+
+static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
+{
+       if (!pci_is_pcie(dev))
+               return false;
+
+       switch (pos) {
+       case PCI_EXP_FLAGS_TYPE:
+               return true;
+       case PCI_EXP_DEVCAP:
+       case PCI_EXP_DEVCTL:
+       case PCI_EXP_DEVSTA:
+               return pcie_cap_has_devctl(dev);
+       case PCI_EXP_LNKCAP:
+       case PCI_EXP_LNKCTL:
+       case PCI_EXP_LNKSTA:
+               return pcie_cap_has_lnkctl(dev);
+       case PCI_EXP_SLTCAP:
+       case PCI_EXP_SLTCTL:
+       case PCI_EXP_SLTSTA:
+               return pcie_cap_has_sltctl(dev);
+       case PCI_EXP_RTCTL:
+       case PCI_EXP_RTCAP:
+       case PCI_EXP_RTSTA:
+               return pcie_cap_has_rtctl(dev);
+       case PCI_EXP_DEVCAP2:
+       case PCI_EXP_DEVCTL2:
+       case PCI_EXP_LNKCAP2:
+       case PCI_EXP_LNKCTL2:
+       case PCI_EXP_LNKSTA2:
+               return pcie_cap_version(dev) > 1;
+       default:
+               return false;
+       }
+}
+
+/*
+ * Note that these accessor functions are only for the "PCI Express
+ * Capability" (see PCIe spec r3.0, sec 7.8).  They do not apply to the
+ * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.)
+ */
+int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val)
+{
+       int ret;
+
+       *val = 0;
+       if (pos & 1)
+               return -EINVAL;
+
+       if (pcie_capability_reg_implemented(dev, pos)) {
+               ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val);
+               /*
+                * Reset *val to 0 if pci_read_config_word() fails, it may
+                * have been written as 0xFFFF if hardware error happens
+                * during pci_read_config_word().
+                */
+               if (ret)
+                       *val = 0;
+               return ret;
+       }
+
+       /*
+        * For Functions that do not implement the Slot Capabilities,
+        * Slot Status, and Slot Control registers, these spaces must
+        * be hardwired to 0b, with the exception of the Presence Detect
+        * State bit in the Slot Status register of Downstream Ports,
+        * which must be hardwired to 1b.  (PCIe Base Spec 3.0, sec 7.8)
+        */
+       if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA &&
+                pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+               *val = PCI_EXP_SLTSTA_PDS;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(pcie_capability_read_word);
+
+int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val)
+{
+       int ret;
+
+       *val = 0;
+       if (pos & 3)
+               return -EINVAL;
+
+       if (pcie_capability_reg_implemented(dev, pos)) {
+               ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+               /*
+                * Reset *val to 0 if pci_read_config_dword() fails, it may
+                * have been written as 0xFFFFFFFF if hardware error happens
+                * during pci_read_config_dword().
+                */
+               if (ret)
+                       *val = 0;
+               return ret;
+       }
+
+       if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL &&
+                pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+               *val = PCI_EXP_SLTSTA_PDS;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(pcie_capability_read_dword);
+
+int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val)
+{
+       if (pos & 1)
+               return -EINVAL;
+
+       if (!pcie_capability_reg_implemented(dev, pos))
+               return 0;
+
+       return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL(pcie_capability_write_word);
+
+int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val)
+{
+       if (pos & 3)
+               return -EINVAL;
+
+       if (!pcie_capability_reg_implemented(dev, pos))
+               return 0;
+
+       return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL(pcie_capability_write_dword);
+
+int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
+                                      u16 clear, u16 set)
+{
+       int ret;
+       u16 val;
+
+       ret = pcie_capability_read_word(dev, pos, &val);
+       if (!ret) {
+               val &= ~clear;
+               val |= set;
+               ret = pcie_capability_write_word(dev, pos, val);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(pcie_capability_clear_and_set_word);
+
+int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
+                                       u32 clear, u32 set)
+{
+       int ret;
+       u32 val;
+
+       ret = pcie_capability_read_dword(dev, pos, &val);
+       if (!ret) {
+               val &= ~clear;
+               val |= set;
+               ret = pcie_capability_write_dword(dev, pos, val);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(pcie_capability_clear_and_set_dword);
index 9807da507e1f626712a72ec90127a3390980cb87..b8667e0548e0d159c068b8159daf8af5ad77d296 100644 (file)
@@ -816,6 +816,39 @@ static inline int pci_write_config_dword(const struct pci_dev *dev, int where,
        return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val);
 }
 
+int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val);
+int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val);
+int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val);
+int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val);
+int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
+                                      u16 clear, u16 set);
+int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
+                                       u32 clear, u32 set);
+
+static inline int pcie_capability_set_word(struct pci_dev *dev, int pos,
+                                          u16 set)
+{
+       return pcie_capability_clear_and_set_word(dev, pos, 0, set);
+}
+
+static inline int pcie_capability_set_dword(struct pci_dev *dev, int pos,
+                                           u32 set)
+{
+       return pcie_capability_clear_and_set_dword(dev, pos, 0, set);
+}
+
+static inline int pcie_capability_clear_word(struct pci_dev *dev, int pos,
+                                            u16 clear)
+{
+       return pcie_capability_clear_and_set_word(dev, pos, clear, 0);
+}
+
+static inline int pcie_capability_clear_dword(struct pci_dev *dev, int pos,
+                                             u32 clear)
+{
+       return pcie_capability_clear_and_set_dword(dev, pos, clear, 0);
+}
+
 /* user-space driven config access */
 int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
 int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
index 7fb75b143755c6a2b4e0105cd9d23511196fb8eb..3958f70f320222ab9159a65b8c6ce68f5ee0af7e 100644 (file)
 #define  PCI_EXP_LNKCAP2_SLS_8_0GB 0x04        /* Current Link Speed 8.0GT/s */
 #define  PCI_EXP_LNKCAP2_CROSSLINK 0x100 /* Crosslink supported */
 #define PCI_EXP_LNKCTL2                48      /* Link Control 2 */
+#define PCI_EXP_LNKSTA2                50      /* Link Status 2 */
 #define PCI_EXP_SLTCTL2                56      /* Slot Control 2 */
 
 /* Extended Capabilities (PCI-X 2.0 and Express) */