compat: backport PCI: Add accessors for PCI Express Capability
authorLuis R. Rodriguez <mcgrof@do-not-panic.com>
Wed, 5 Sep 2012 01:15:30 +0000 (18:15 -0700)
committerLuis R. Rodriguez <mcgrof@do-not-panic.com>
Wed, 5 Sep 2012 02:25:27 +0000 (19:25 -0700)
This backports this patch:

commit 8c0d3a02c1309eb6112d2e7c8172e8ceb26ecfca
Author: Jiang Liu <jiang.liu@huawei.com>
Date:   Tue Jul 24 17:20:05 2012 +0800

    PCI: Add accessors for PCI Express Capability

    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>
This is not yet upstream into a public release but based on
star alignments I can confidently say this will be part
of the v3.7-rc1 release unless Linus throws a shit fit over
it.

Worth mentioning is that the pci_dev->pcie_flags_reg
is not available on older kernels so we had to write
a pcie_flags_reg() helper. Additionally a lot of pci_reg.h
updates were made into the respective header file as they
were introduced.

mcgrof@garbanzo ~/compat (git::master)$ time ckmake
Trying kernel 3.5.0-030500-generic [OK]
Trying kernel 3.4.4-030404-generic [OK]
Trying kernel 3.3.7-030307-generic [OK]
Trying kernel 3.2.2-030202-generic [OK]
Trying kernel 3.1.10-030110-generic [OK]
Trying kernel 3.0.18-030018-generic [OK]
Trying kernel 2.6.39-02063904-generic [OK]
Trying kernel 2.6.38-02063808-generic [OK]
Trying kernel 2.6.37-02063706-generic [OK]
Trying kernel 2.6.36-02063604-generic [OK]
Trying kernel 2.6.35-02063512-generic [OK]
Trying kernel 2.6.34-02063410-generic [OK]
Trying kernel 2.6.33-02063305-generic [OK]
Trying kernel 2.6.32-02063255-generic [OK]
Trying kernel 2.6.31-02063113-generic [OK]
Trying kernel 2.6.30-02063010-generic [OK]
Trying kernel 2.6.29-02062906-generic [OK]
Trying kernel 2.6.28-02062810-generic [OK]
Trying kernel 2.6.27-020627-generic [OK]
Trying kernel 2.6.26-020626-generic [OK]
Trying kernel 2.6.25-020625-generic [OK]
Trying kernel 2.6.24-020624-generic [OK]

real    0m59.966s
user    3m12.628s
sys     0m29.542s

Signed-off-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
compat/compat-3.7.c
include/linux/compat-2.6.28.h
include/linux/compat-2.6.29.h
include/linux/compat-2.6.30.h
include/linux/compat-3.3.h
include/linux/compat-3.7.h

index f251788fd52b3482abd04c9952b8034fa3d6cd3d..b37b73a31fdf5d8ceb6ff4baff3daeac6417fce3 100644 (file)
@@ -19,3 +19,233 @@ bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork,
        return false;
 }
 EXPORT_SYMBOL_GPL(mod_delayed_work);
+
+/*
+ * Kernels >= 3.7 get their PCI-E Capabilities Register cached
+ * via the pci_dev->pcie_flags_reg so for older kernels we have
+ * no other option but to read this every single time we need
+ * it accessed. If we really cared to improve the efficiency
+ * of this we could try to find an unused u16 varible on the
+ * pci_dev but if we found it we likely would remove it from
+ * the kernel anyway right? Bite me.
+ */
+static inline u16 pcie_flags_reg(const struct pci_dev *dev)
+{
+       int pos;
+       u16 reg16;
+
+       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+       if (!pos)
+               return 0;
+
+       pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
+
+       return reg16;
+}
+
+static inline int pci_pcie_type(const struct pci_dev *dev)
+{
+       return (pcie_flags_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4;
+}
+
+static inline int pcie_cap_version(const struct pci_dev *dev)
+{
+       return pcie_flags_reg(dev) & 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 &&
+               pcie_flags_reg(dev) & 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 9b6b9580e87cadf10f7c6cb919f433a3d461de68..8df4e46149b2e350de24c8afaf5f069470e9432c 100644 (file)
@@ -17,6 +17,7 @@
 #endif
 
 #include <linux/pci.h>
+#include <linux/pci_regs.h>
 
 typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } compat_cpumask_t;
 
@@ -267,6 +268,11 @@ int pci_wake_from_d3(struct pci_dev *dev, bool enable);
 #define pr_fmt(fmt) fmt
 #endif
 
+#define PCI_EXP_DEVCAP2                36      /* Device Capabilities 2 */
+#define  PCI_EXP_DEVCAP2_ARI   0x20    /* Alternative Routing-ID */
+#define PCI_EXP_DEVCTL2                40      /* Device Control 2 */
+#define  PCI_EXP_DEVCTL2_ARI   0x20    /* Alternative Routing-ID */
+
 #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)) */
 
 #endif /* LINUX_26_28_COMPAT_H */
index e859a190a3926bce58f725f999276e14b6f04621..4becc7e1f934a66cdf8506849ff9fcea624945da 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/skbuff.h>
 #include <linux/usb.h>
 #include <linux/types.h>
+#include <linux/pci_regs.h>
 
 /* backports  */
 static inline void usb_autopm_put_interface_async(struct usb_interface *intf)
@@ -415,4 +416,6 @@ static inline int ndo_do_ioctl(struct net_device *dev,
 #define compat_pci_resume(fn)
 #endif
 
+#define  PCI_EXP_SLTSTA_PDS    0x0040  /* Presence Detect State */
+
 #endif /*  LINUX_26_29_COMPAT_H */
index 28f3d54a923aa02a9b35a527f1cdb2a898ce5472..b4aafaec5de69ed2e9fc5232fe2d12c1a1f9006c 100644 (file)
@@ -6,6 +6,7 @@
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30))
 
 #include <linux/device.h>
+#include <linux/pci_regs.h>
 
 #ifndef TP_PROTO
 #define TP_PROTO(args...)      TPPROTO(args)
@@ -42,6 +43,9 @@ static inline void dev_set_uevent_suppress(struct device *dev, int val)
        }                                       \
 })
 
+#define PCI_EXP_LNKCTL2                        48      /* Link Control 2 */
+#define PCI_EXP_SLTCTL2                        56      /* Slot Control 2 */
+
 #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) */
 
 #endif /* LINUX_26_30_COMPAT_H */
index 61d33b036a248e0d8afe118b33386394c6cf2a19..3cbbaceb14211937d4590c8200f89a583d3f748f 100644 (file)
@@ -5,6 +5,8 @@
 
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0))
 
+#include <linux/pci_regs.h>
+
 /* include to override NL80211_FEATURE_SK_TX_STATUS */
 #include <linux/nl80211.h>
 #include <linux/skbuff.h>
@@ -74,6 +76,25 @@ module_exit(__driver##_exit);
        module_driver(__usb_driver, usb_register, \
                       usb_deregister)
 
+
+/*
+ * PCI_EXP_TYPE_RC_EC was added via 1b6b8ce2 on v2.6.30-rc4~20 :
+ *
+ * mcgrof@frijol ~/linux-next (git::master)$ git describe --contains 1b6b8ce2
+ * v2.6.30-rc4~20^2
+ *
+ * but the fix for its definition was merged on v3.3-rc1~101^2~67
+ *
+ * mcgrof@frijol ~/linux-next (git::master)$ git describe --contains 1830ea91
+ * v3.3-rc1~101^2~67
+ *
+ * while we can assume it got merged and backported on v3.2.28 (which it did
+ * see c1c3cd9) we cannot assume every kernel has it fixed so lets just undef
+ * it here and redefine it.
+ */
+#undef PCI_EXP_TYPE_RC_EC
+#define  PCI_EXP_TYPE_RC_EC    0xa     /* Root Complex Event Collector */
+
 #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)) */
 
 #endif /* LINUX_3_3_COMPAT_H */
index 22a7f9f6fb2868c710c5a643f7ae613dd53b06a9..6cee78794b4e4d9b767ab6d84d473c10e9bb5b74 100644 (file)
@@ -7,6 +7,8 @@
 
 #include <linux/workqueue.h>
 #include <linux/tty.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
 
 bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork,
                      unsigned long delay);
@@ -18,6 +20,41 @@ bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork,
 #define tty_port_register_device(port, driver, index, device) \
        tty_register_device(driver, index, device)
 
+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);
+}
+
+#define PCI_EXP_LNKSTA2                        50      /* Link Status 2 */
+
 #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) */
 
 #endif /* LINUX_3_7_COMPAT_H */