From fbc930e41dabb4f7c56dd432e374c223ab02cb76 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 4 Sep 2012 18:15:30 -0700 Subject: [PATCH] compat: backport PCI: Add accessors for PCI Express Capability This backports this patch: commit 8c0d3a02c1309eb6112d2e7c8172e8ceb26ecfca Author: Jiang Liu 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 Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas 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 --- compat/compat-3.7.c | 230 ++++++++++++++++++++++++++++++++++ include/linux/compat-2.6.28.h | 6 + include/linux/compat-2.6.29.h | 3 + include/linux/compat-2.6.30.h | 4 + include/linux/compat-3.3.h | 21 ++++ include/linux/compat-3.7.h | 37 ++++++ 6 files changed, 301 insertions(+) diff --git a/compat/compat-3.7.c b/compat/compat-3.7.c index f251788fd52b..b37b73a31fdf 100644 --- a/compat/compat-3.7.c +++ b/compat/compat-3.7.c @@ -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, ®16); + + 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); diff --git a/include/linux/compat-2.6.28.h b/include/linux/compat-2.6.28.h index 9b6b9580e87c..8df4e46149b2 100644 --- a/include/linux/compat-2.6.28.h +++ b/include/linux/compat-2.6.28.h @@ -17,6 +17,7 @@ #endif #include +#include 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 */ diff --git a/include/linux/compat-2.6.29.h b/include/linux/compat-2.6.29.h index e859a190a392..4becc7e1f934 100644 --- a/include/linux/compat-2.6.29.h +++ b/include/linux/compat-2.6.29.h @@ -17,6 +17,7 @@ #include #include #include +#include /* 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 */ diff --git a/include/linux/compat-2.6.30.h b/include/linux/compat-2.6.30.h index 28f3d54a923a..b4aafaec5de6 100644 --- a/include/linux/compat-2.6.30.h +++ b/include/linux/compat-2.6.30.h @@ -6,6 +6,7 @@ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) #include +#include #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 */ diff --git a/include/linux/compat-3.3.h b/include/linux/compat-3.3.h index 61d33b036a24..3cbbaceb1421 100644 --- a/include/linux/compat-3.3.h +++ b/include/linux/compat-3.3.h @@ -5,6 +5,8 @@ #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)) +#include + /* include to override NL80211_FEATURE_SK_TX_STATUS */ #include #include @@ -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 */ diff --git a/include/linux/compat-3.7.h b/include/linux/compat-3.7.h index 22a7f9f6fb28..6cee78794b4e 100644 --- a/include/linux/compat-3.7.h +++ b/include/linux/compat-3.7.h @@ -7,6 +7,8 @@ #include #include +#include +#include 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 */ -- 2.30.2