From 109cdbc223f6e2d6c80f8371f22415b50c18a366 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 18 May 2012 16:52:19 -0600 Subject: PCI: remove pci_bus_find_ext_capability() (unused) pci_bus_find_ext_capability() is unused, and this patch removes it. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c0..de9386da2eb 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -329,49 +329,6 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) } EXPORT_SYMBOL_GPL(pci_find_ext_capability); -/** - * pci_bus_find_ext_capability - find an extended capability - * @bus: the PCI bus to query - * @devfn: PCI device to query - * @cap: capability code - * - * Like pci_find_ext_capability() but works for pci devices that do not have a - * pci_dev structure set up yet. - * - * Returns the address of the requested capability structure within the - * device's PCI configuration space or 0 in case the device does not - * support it. - */ -int pci_bus_find_ext_capability(struct pci_bus *bus, unsigned int devfn, - int cap) -{ - u32 header; - int ttl; - int pos = PCI_CFG_SPACE_SIZE; - - /* minimum 8 bytes per capability */ - ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; - - if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) - return 0; - if (header == 0xffffffff || header == 0) - return 0; - - while (ttl-- > 0) { - if (PCI_EXT_CAP_ID(header) == cap) - return pos; - - pos = PCI_EXT_CAP_NEXT(header); - if (pos < PCI_CFG_SPACE_SIZE) - break; - - if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) - break; - } - - return 0; -} - static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap) { int rc, ttl = PCI_FIND_CAP_TTL; -- cgit v1.2.3-70-g09d2 From 533b6608b73669309becd90f11f939b60bb221be Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 18 May 2012 16:52:34 -0600 Subject: PCI: remove pci_max_busnr() (was already commented out) pci_max_busnr() has been commented out for years (since 54c762fe62), and this patch removes it completely. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index de9386da2eb..2cc53acad26 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -136,30 +136,6 @@ void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) EXPORT_SYMBOL_GPL(pci_ioremap_bar); #endif -#if 0 -/** - * pci_max_busnr - returns maximum PCI bus number - * - * Returns the highest PCI bus number present in the system global list of - * PCI buses. - */ -unsigned char __devinit -pci_max_busnr(void) -{ - struct pci_bus *bus = NULL; - unsigned char max, n; - - max = 0; - while ((bus = pci_find_next_bus(bus)) != NULL) { - n = pci_bus_max_busnr(bus); - if(n > max) - max = n; - } - return max; -} - -#endif /* 0 */ - #define PCI_FIND_CAP_TTL 48 static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, -- cgit v1.2.3-70-g09d2 From c32823f82b42abc1f08b365085862fd1d57c0b61 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:25 -0600 Subject: PCI: make pci_ltr_supported() static The PCI Express Latency Tolerance Reporting (LTR) feature's pci_ltr_supported() routine is currently only used within drivers/pci/pci.c so make it static. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +-- include/linux/pci.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c0..847e0c35cdb 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2169,7 +2169,7 @@ EXPORT_SYMBOL(pci_disable_obff); * RETURNS: * True if @dev supports latency tolerance reporting, false otherwise. */ -bool pci_ltr_supported(struct pci_dev *dev) +static bool pci_ltr_supported(struct pci_dev *dev) { int pos; u32 cap; @@ -2185,7 +2185,6 @@ bool pci_ltr_supported(struct pci_dev *dev) return cap & PCI_EXP_DEVCAP2_LTR; } -EXPORT_SYMBOL(pci_ltr_supported); /** * pci_enable_ltr - enable latency tolerance reporting diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6a..b2bec26b7f0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -875,7 +875,6 @@ enum pci_obff_signal_type { int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); void pci_disable_obff(struct pci_dev *dev); -bool pci_ltr_supported(struct pci_dev *dev); int pci_enable_ltr(struct pci_dev *dev); void pci_disable_ltr(struct pci_dev *dev); int pci_set_ltr(struct pci_dev *dev, int snoop_lat_ns, int nosnoop_lat_ns); -- cgit v1.2.3-70-g09d2 From cb97ae3485955401d637bd269b0d24d3cd3fd3ec Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:31 -0600 Subject: PCI: remove redundant checking in PCI Express capability routines There are a number of redundant pci_is_pcie() checks in various PCI Express capabilities related routines like the following: if (!pci_is_pcie(dev)) return false; pos = pci_pcie_cap(dev); if (!pos) return false; The current pci_is_pcie() implementation is merely: static inline bool pci_is_pcie(struct pci_dev *dev) { return !!pci_pcie_cap(dev); } so we can just drop the pci_is_pcie() test in such cases. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 847e0c35cdb..766bb13bb0a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1994,7 +1994,7 @@ void pci_enable_ari(struct pci_dev *dev) return; bridge = dev->bus->self; - if (!bridge || !pci_is_pcie(bridge)) + if (!bridge) return; pos = pci_pcie_cap(bridge); @@ -2054,9 +2054,6 @@ void pci_disable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - if (!pci_is_pcie(dev)) - return; - pos = pci_pcie_cap(dev); if (!pos) return; @@ -2096,9 +2093,6 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) u16 ctrl; int ret; - if (!pci_is_pcie(dev)) - return -ENOTSUPP; - pos = pci_pcie_cap(dev); if (!pos) return -ENOTSUPP; @@ -2149,9 +2143,6 @@ void pci_disable_obff(struct pci_dev *dev) int pos; u16 ctrl; - if (!pci_is_pcie(dev)) - return; - pos = pci_pcie_cap(dev); if (!pos) return; @@ -2174,9 +2165,6 @@ static bool pci_ltr_supported(struct pci_dev *dev) int pos; u32 cap; - if (!pci_is_pcie(dev)) - return false; - pos = pci_pcie_cap(dev); if (!pos) return false; -- cgit v1.2.3-70-g09d2 From c463b8cb9350cf1230cefe467a1cf279140a5437 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:37 -0600 Subject: PCI: add pci_pcie_cap2() check for PCIe feature capabilities >= v2 This patch resolves potential issues when accessing PCI Express Capability structures. The makeup of the capability varies substantially between v1 and v2: Version 1 of the PCI Express Capability (defined by PCI Express 1.0 and 1.1 base) neither requires the endpoint to implement the entire PCIe capability structure nor specifies default values of registers that are not implemented by the device. Version 2 of the PCI Express Capability (defined by PCIe 1.1 Capability Structure Expansion ECN, PCIe 2.0, 2.1, and 3.0) added additional registers to the structure and requires all registers to be either implemented or hardwired to 0. Due to the differences in the capability structures, code dealing with capability features must be careful not to access the additional registers introduced with v2 unless the device is specifically known to be a v2 capable device. Otherwise, attempts to access non-existant registers will occur. This is a subtle issue that is hard to track down when it occurs (and it has - see commit 864d296cf94). To try and help mitigate such occurrences, this patch introduces pci_pcie_cap2() which is similar to pci_pcie_cap() but also checks that the PCIe capability version is >= 2. pci_pcie_cap2() should be used for qualifying PCIe capability features introduced after v1. Suggested by Don Dutile. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 65 +++++++++++++++++++++++++++++++++++++----------- include/linux/pci_regs.h | 6 +++++ 2 files changed, 56 insertions(+), 15 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 766bb13bb0a..985df63aa59 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -277,6 +277,38 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap) return pos; } +/** + * pci_pcie_cap2 - query for devices' PCI_CAP_ID_EXP v2 capability structure + * @dev: PCI device to check + * + * Like pci_pcie_cap() but also checks that the PCIe capability version is + * >= 2. Note that v1 capability structures could be sparse in that not + * all register fields were required. v2 requires the entire structure to + * be present size wise, while still allowing for non-implemented registers + * to exist but they must be hardwired to 0. + * + * Due to the differences in the versions of capability structures, one + * must be careful not to try and access non-existant registers that may + * exist in early versions - v1 - of Express devices. + * + * Returns the offset of the PCIe capability structure as long as the + * capability version is >= 2; otherwise 0 is returned. + */ +static int pci_pcie_cap2(struct pci_dev *dev) +{ + u16 flags; + int pos; + + pos = pci_pcie_cap(dev); + if (pos) { + pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags); + if ((flags & PCI_EXP_FLAGS_VERS) < 2) + pos = 0; + } + + return pos; +} + /** * pci_find_ext_capability - Find an extended capability * @dev: PCI device to query @@ -1983,7 +2015,7 @@ void pci_enable_ari(struct pci_dev *dev) { int pos; u32 cap; - u16 flags, ctrl; + u16 ctrl; struct pci_dev *bridge; if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn) @@ -1997,15 +2029,11 @@ void pci_enable_ari(struct pci_dev *dev) if (!bridge) return; - pos = pci_pcie_cap(bridge); + /* ARI is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(bridge); if (!pos) return; - /* ARI is a PCIe v2 feature */ - pci_read_config_word(bridge, pos + PCI_EXP_FLAGS, &flags); - if ((flags & PCI_EXP_FLAGS_VERS) < 2) - return; - pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap); if (!(cap & PCI_EXP_DEVCAP2_ARI)) return; @@ -2018,7 +2046,7 @@ void pci_enable_ari(struct pci_dev *dev) } /** - * pci_enable_ido - enable ID-based ordering on a device + * pci_enable_ido - enable ID-based Ordering on a device * @dev: the PCI device * @type: which types of IDO to enable * @@ -2031,7 +2059,8 @@ void pci_enable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* ID-based Ordering is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2054,7 +2083,8 @@ void pci_disable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* ID-based Ordering is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2093,7 +2123,8 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) u16 ctrl; int ret; - pos = pci_pcie_cap(dev); + /* OBFF is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return -ENOTSUPP; @@ -2143,7 +2174,8 @@ void pci_disable_obff(struct pci_dev *dev) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* OBFF is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2165,7 +2197,8 @@ static bool pci_ltr_supported(struct pci_dev *dev) int pos; u32 cap; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return false; @@ -2193,7 +2226,8 @@ int pci_enable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return -ENOTSUPP; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return -ENOTSUPP; @@ -2228,7 +2262,8 @@ void pci_disable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 4b608f54341..e7642f5bb12 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -507,6 +507,12 @@ #define PCI_EXP_RTSTA 32 /* Root Status */ #define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ #define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ +/* + * Note that the following PCI Express 'Capability Structure' registers + * were introduced with 'Capability Version' 0x2 (v2). These registers + * do not exist on devices with Capability Version 1. Use pci_pcie_cap2() + * to use these fields safely. + */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ #define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ #define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ -- cgit v1.2.3-70-g09d2 From 9cb604ed45a31419bab3877472691a5da15a3c47 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:43 -0600 Subject: PCI: remove redundant capabilities checking in pci_{save, restore}_pcie_state Unlike PCI Express v1's Capabilities Structure, v2's requires the entire structure to be implemented. In v2 structures, register fields that are not implemented are present but hardwired to 0x0. These may include: Link Capabilities, Status, and Control; Slot Capabilities, Status, and Control; Root Capabilities, Status, and Control; and all of the '2' (Device, Link, and Slot) Capabilities, Status, and Control registers. This patch removes the redundant capability checks corresponding to the Link 2's and Slot 2's, Capabilities, Status, and Control registers as they will be present if Device Capabilities 2's registers are (which explains why the macros for each of the three are identical). Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 985df63aa59..fe26df7cf5c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -854,12 +854,6 @@ EXPORT_SYMBOL(pci_choose_state); ((flags & PCI_EXP_FLAGS_VERS) > 1 || \ (type == PCI_EXP_TYPE_ROOT_PORT || \ type == PCI_EXP_TYPE_RC_EC)) -#define pcie_cap_has_devctl2(type, flags) \ - ((flags & PCI_EXP_FLAGS_VERS) > 1) -#define pcie_cap_has_lnkctl2(type, flags) \ - ((flags & PCI_EXP_FLAGS_VERS) > 1) -#define pcie_cap_has_sltctl2(type, flags) \ - ((flags & PCI_EXP_FLAGS_VERS) > 1) static struct pci_cap_saved_state *pci_find_saved_cap( struct pci_dev *pci_dev, char cap) @@ -902,13 +896,14 @@ static int pci_save_pcie_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); if (pcie_cap_has_rtctl(dev->pcie_type, flags)) pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); - if (pcie_cap_has_devctl2(dev->pcie_type, flags)) - pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]); - if (pcie_cap_has_lnkctl2(dev->pcie_type, flags)) - pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]); - if (pcie_cap_has_sltctl2(dev->pcie_type, flags)) - pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]); + pos = pci_pcie_cap2(dev); + if (!pos) + return 0; + + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]); + pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]); + pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]); return 0; } @@ -935,12 +930,14 @@ static void pci_restore_pcie_state(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]); if (pcie_cap_has_rtctl(dev->pcie_type, flags)) pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]); - if (pcie_cap_has_devctl2(dev->pcie_type, flags)) - pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]); - if (pcie_cap_has_lnkctl2(dev->pcie_type, flags)) - pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]); - if (pcie_cap_has_sltctl2(dev->pcie_type, flags)) - pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]); + + pos = pci_pcie_cap2(dev); + if (!pos) + return; + + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]); + pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]); + pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]); } -- cgit v1.2.3-70-g09d2 From ad805758c0eb25bce7b2e3b298d63dc62a1bc71c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:07 +0000 Subject: PCI: add ACS validation utility In a PCI environment, transactions aren't always required to reach the root bus before being re-routed. Intermediate switches between an endpoint and the root bus can redirect DMA back downstream before things like IOMMUs have a chance to intervene. Legacy PCI is always susceptible to this as it operates on a shared bus. PCIe added a new capability to describe and control this behavior, Access Control Services, or ACS. The utility function pci_acs_enabled() allows us to test the ACS capabilities of an individual devices against a set of flags while pci_acs_path_enabled() tests a complete path from a given downstream device up to the specified upstream device. We also include the ability to add device specific tests as it's likely we'll see devices that do not implement ACS, but want to indicate support for various capabilities in this space. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/quirks.c | 33 +++++++++++++++++++++++++ include/linux/pci.h | 10 +++++++- 3 files changed, 111 insertions(+), 1 deletion(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c0..1ccf7d49f52 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2359,6 +2359,75 @@ void pci_enable_acs(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); } +/** + * pci_acs_enabled - test ACS against required flags for a given device + * @pdev: device to test + * @acs_flags: required PCI ACS flags + * + * Return true if the device supports the provided flags. Automatically + * filters out flags that are not implemented on multifunction devices. + */ +bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags) +{ + int pos, ret; + u16 ctrl; + + ret = pci_dev_specific_acs_enabled(pdev, acs_flags); + if (ret >= 0) + return ret > 0; + + if (!pci_is_pcie(pdev)) + return false; + + /* Filter out flags not applicable to multifunction */ + if (pdev->multifunction) + acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | + PCI_ACS_EC | PCI_ACS_DT); + + if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM || + pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || + pdev->multifunction) { + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); + if (!pos) + return false; + + pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); + if ((ctrl & acs_flags) != acs_flags) + return false; + } + + return true; +} + +/** + * pci_acs_path_enable - test ACS flags from start to end in a hierarchy + * @start: starting downstream device + * @end: ending upstream device or NULL to search to the root bus + * @acs_flags: required flags + * + * Walk up a device tree from start to end testing PCI ACS support. If + * any step along the way does not support the required flags, return false. + */ +bool pci_acs_path_enabled(struct pci_dev *start, + struct pci_dev *end, u16 acs_flags) +{ + struct pci_dev *pdev, *parent = start; + + do { + pdev = parent; + + if (!pci_acs_enabled(pdev, acs_flags)) + return false; + + if (pci_is_root_bus(pdev->bus)) + return (end == NULL); + + parent = pdev->bus->self; + } while (pdev != end); + + return true; +} + /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index acd3956b44b..27e2c8f4ec7 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3230,3 +3230,36 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev) return pci_dev_get(dev); } + +static const struct pci_dev_acs_enabled { + u16 vendor; + u16 device; + int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags); +} pci_dev_acs_enabled[] = { + { 0 } +}; + +int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) +{ + const struct pci_dev_acs_enabled *i; + int ret; + + /* + * Allow devices that do not expose standard PCIe ACS capabilities + * or control to indicate their support here. Multi-function express + * devices which do not allow internal peer-to-peer between functions, + * but do not implement PCIe ACS may wish to return true here. + */ + for (i = pci_dev_acs_enabled; i->acs_enabled; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) { + ret = i->acs_enabled(dev, acs_flags); + if (ret >= 0) + return ret; + } + } + + return -ENOTTY; +} diff --git a/include/linux/pci.h b/include/linux/pci.h index 39983be7b25..dd7af0f37b3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1490,6 +1490,7 @@ enum pci_fixup_pass { #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); struct pci_dev *pci_get_dma_source(struct pci_dev *dev); +int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); #else static inline void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {} @@ -1497,6 +1498,11 @@ static inline struct pci_dev *pci_get_dma_source(struct pci_dev *dev) { return pci_dev_get(dev); } +static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, + u16 acs_flags) +{ + return -ENOTTY; +} #endif void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); @@ -1599,7 +1605,9 @@ static inline bool pci_is_pcie(struct pci_dev *dev) } void pci_request_acs(void); - +bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags); +bool pci_acs_path_enabled(struct pci_dev *start, + struct pci_dev *end, u16 acs_flags); #define PCI_VPD_LRDT 0x80 /* Large Resource Data Type */ #define PCI_VPD_LRDT_ID(x) (x | PCI_VPD_LRDT) -- cgit v1.2.3-70-g09d2 From b918c62e086b2130a7bae44110ca516ef10bfe5a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:11 -0700 Subject: PCI: replace struct pci_bus secondary/subordinate with busn_res Replace the struct pci_bus secondary/subordinate members with the struct resource busn_res. Later we'll build a resource tree of these bus numbers. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/alpha/kernel/pci.c | 2 +- arch/arm/kernel/bios32.c | 2 +- arch/ia64/pci/pci.c | 2 +- arch/microblaze/pci/pci-common.c | 4 +-- arch/mips/pci/pci.c | 2 +- arch/powerpc/kernel/pci-common.c | 6 ++-- arch/powerpc/kernel/pci_64.c | 2 +- arch/powerpc/kernel/pci_of_scan.c | 2 +- arch/powerpc/platforms/powernv/pci-ioda.c | 8 ++--- arch/powerpc/platforms/pseries/pci_dlpar.c | 2 +- arch/sh/drivers/pci/pci.c | 2 +- arch/sparc/kernel/pci.c | 6 ++-- arch/tile/kernel/pci.c | 4 +-- arch/x86/pci/acpi.c | 2 +- arch/xtensa/kernel/pci.c | 2 +- drivers/iommu/intel-iommu.c | 2 +- drivers/net/ethernet/broadcom/tg3.c | 4 +-- drivers/parisc/dino.c | 10 +++--- drivers/parisc/iosapic.c | 2 +- drivers/parisc/lba_pci.c | 22 ++++++------ drivers/pci/hotplug/acpiphp_glue.c | 8 ++--- drivers/pci/hotplug/cpci_hotplug_pci.c | 6 ++-- drivers/pci/hotplug/pciehp_pci.c | 4 +-- drivers/pci/hotplug/shpchp_pci.c | 6 ++-- drivers/pci/hotplug/shpchp_sysfs.c | 6 ++-- drivers/pci/iov.c | 4 +-- drivers/pci/pci.c | 2 +- drivers/pci/probe.c | 58 +++++++++++++++--------------- drivers/pci/setup-bus.c | 24 ++++++------- drivers/pcmcia/cardbus.c | 2 +- drivers/pcmcia/yenta_socket.c | 26 +++++++------- 31 files changed, 117 insertions(+), 117 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 1a629636cc1..53229a49631 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -359,7 +359,7 @@ common_init_pci(void) hose, &resources); hose->bus = bus; hose->need_domain_info = need_domain_info; - next_busno = bus->subordinate + 1; + next_busno = bus->busn_res.end + 1; /* Don't allow 8-bit bus number overflow inside the hose - reserve some space for bridges. */ if (next_busno > 224) { diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 25552508c3f..f07710849b5 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -461,7 +461,7 @@ static void __init pcibios_init_hw(struct hw_pci *hw, struct list_head *head) if (!sys->bus) panic("PCI: unable to scan bus!"); - busnr = sys->bus->subordinate + 1; + busnr = sys->bus->busn_res.end + 1; list_add(&sys->node, head); } else { diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 524df4295c9..3ca9bed7dc5 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -384,7 +384,7 @@ pci_acpi_scan_root(struct acpi_pci_root *root) return NULL; } - pbus->subordinate = pci_scan_child_bus(pbus); + pbus->busn_res.end = pci_scan_child_bus(pbus); return pbus; out3: diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index ed22bfc5db1..9b32483cd0c 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -1506,10 +1506,10 @@ static void __devinit pcibios_scan_phb(struct pci_controller *hose) pci_free_resource_list(&resources); return; } - bus->secondary = hose->first_busno; + bus->busn_res.start = hose->first_busno; hose->bus = bus; - hose->last_busno = bus->subordinate; + hose->last_busno = bus->busn_res.end; } static int __init pcibios_init(void) diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 271e8c4a54c..0a9bf778edb 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -102,7 +102,7 @@ static void __devinit pcibios_scanbus(struct pci_controller *hose) need_domain_info = need_domain_info || hose->index; hose->need_domain_info = need_domain_info; if (bus) { - next_busno = bus->subordinate + 1; + next_busno = bus->busn_res.end + 1; /* Don't allow 8-bit bus number overflow inside the hose - reserve some space for bridges. */ if (next_busno > 224) { diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 8e78e93c818..3532b535698 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1646,7 +1646,7 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) pci_free_resource_list(&resources); return; } - bus->secondary = hose->first_busno; + bus->busn_res.start = hose->first_busno; hose->bus = bus; /* Get probe mode and perform scan */ @@ -1655,12 +1655,12 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) mode = ppc_md.pci_probe_mode(bus); pr_debug(" probe mode: %d\n", mode); if (mode == PCI_PROBE_DEVTREE) { - bus->subordinate = hose->last_busno; + bus->busn_res.end = hose->last_busno; of_scan_bus(node, bus); } if (mode == PCI_PROBE_NORMAL) - hose->last_busno = bus->subordinate = pci_scan_child_bus(bus); + hose->last_busno = bus->busn_res.end = pci_scan_child_bus(bus); /* Platform gets a chance to do some global fixups before * we proceed to resource allocation diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 94a54f61d34..4ff190ff24a 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -236,7 +236,7 @@ long sys_pciconfig_iobase(long which, unsigned long in_bus, for (ln = pci_root_buses.next; ln != &pci_root_buses; ln = ln->next) { bus = pci_bus_b(ln); - if (in_bus >= bus->number && in_bus <= bus->subordinate) + if (in_bus >= bus->number && in_bus <= bus->busn_res.end) break; bus = NULL; } diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 89dde171a6f..a36281aa98f 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -240,7 +240,7 @@ void __devinit of_scan_pci_bridge(struct pci_dev *dev) } bus->primary = dev->bus->number; - bus->subordinate = busrange[1]; + bus->busn_res.end = busrange[1]; bus->bridge_ctl = 0; /* parse ranges property */ diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index fbdd74dac3a..9cda6a1ad0c 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -589,7 +589,7 @@ static int __devinit pnv_ioda_configure_pe(struct pnv_phb *phb, dcomp = OPAL_IGNORE_RID_DEVICE_NUMBER; fcomp = OPAL_IGNORE_RID_FUNCTION_NUMBER; parent = pe->pbus->self; - count = pe->pbus->subordinate - pe->pbus->secondary + 1; + count = pe->pbus->busn_res.end - pe->pbus->busn_res.start + 1; switch(count) { case 1: bcomp = OpalPciBusAll; break; case 2: bcomp = OpalPciBus7Bits; break; @@ -816,11 +816,11 @@ static void __devinit pnv_ioda_setup_bus_PE(struct pci_dev *dev, pe->pdev = NULL; pe->tce32_seg = -1; pe->mve_number = -1; - pe->rid = bus->secondary << 8; + pe->rid = bus->busn_res.start << 8; pe->dma_weight = 0; - pe_info(pe, "Secondary busses %d..%d associated with PE\n", - bus->secondary, bus->subordinate); + pe_info(pe, "Secondary busses %pR associated with PE\n", + &bus->busn_res); if (pnv_ioda_configure_pe(phb, pe)) { /* XXX What do we do here ? */ diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 8b7bafa489c..3ccebc83dc0 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -121,7 +121,7 @@ void pcibios_add_pci_devices(struct pci_bus * bus) if (!num) return; pcibios_setup_bus_devices(bus); - max = bus->secondary; + max = bus->busn_res.start; for (pass=0; pass < 2; pass++) list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 9d10a3cb879..43068dcb451 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -59,7 +59,7 @@ static void __devinit pcibios_scanbus(struct pci_channel *hose) need_domain_info = need_domain_info || hose->index; hose->need_domain_info = need_domain_info; if (bus) { - next_busno = bus->subordinate + 1; + next_busno = bus->busn_res.end + 1; /* Don't allow 8-bit bus number overflow inside the hose - reserve some space for bridges. */ if (next_busno > 224) { diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index fdaf2181167..c85bfd788f7 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -535,7 +535,7 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, } bus->primary = dev->bus->number; - bus->subordinate = busrange[1]; + bus->busn_res.end = busrange[1]; bus->bridge_ctl = 0; /* parse ranges property, or cook one up by hand for Simba */ @@ -693,8 +693,8 @@ struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm, pci_free_resource_list(&resources); return NULL; } - bus->secondary = pbm->pci_first_busno; - bus->subordinate = pbm->pci_last_busno; + bus->busn_res.start = pbm->pci_first_busno; + bus->busn_res.end = pbm->pci_last_busno; pci_of_scan_bus(pbm, node, bus); pci_bus_add_devices(bus); diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c index b56d12bf590..54cc8d77c90 100644 --- a/arch/tile/kernel/pci.c +++ b/arch/tile/kernel/pci.c @@ -329,7 +329,7 @@ int __init pcibios_init(void) */ bus = pci_scan_bus(0, controller->ops, controller); controller->root_bus = bus; - controller->last_busno = bus->subordinate; + controller->last_busno = bus->busn_res.end; } } @@ -366,7 +366,7 @@ int __init pcibios_init(void) */ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && (PCI_SLOT(dev->devfn) == 0)) { - next_bus = dev->subordinate; + next_bus = dev->busn_res.end; controllers[i].mem_resources[0] = *next_bus->resource[0]; controllers[i].mem_resources[1] = diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index fc09c2754e0..350fe63c8a4 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -440,7 +440,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd, &resources); if (bus) { - bus->subordinate = pci_scan_child_bus(bus); + bus->busn_res.end = pci_scan_child_bus(bus); pci_set_host_bridge_release( to_pci_host_bridge(bus->bridge), release_pci_root_info, info); diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index eb30e356f5b..9c57c1e6870 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -187,7 +187,7 @@ static int __init pcibios_init(void) bus = pci_scan_root_bus(NULL, pci_ctrl->first_busno, pci_ctrl->ops, pci_ctrl, &resources); pci_ctrl->bus = bus; - pci_ctrl->last_busno = bus->subordinate; + pci_ctrl->last_busno = bus->busn_res.end; if (next_busno <= pci_ctrl->last_busno) next_busno = pci_ctrl->last_busno+1; } diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index b12af2ff8c5..2fb7d1598a6 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -661,7 +661,7 @@ static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn) if (drhd->devices[i] && drhd->devices[i]->subordinate && drhd->devices[i]->subordinate->number <= bus && - drhd->devices[i]->subordinate->subordinate >= bus) + drhd->devices[i]->subordinate->busn_res.end >= bus) return drhd->iommu; } diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index edeeb516807..09fa3c687a1 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -14168,7 +14168,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (bridge->subordinate && (bridge->subordinate->number <= tp->pdev->bus->number) && - (bridge->subordinate->subordinate >= + (bridge->subordinate->busn_res.end >= tp->pdev->bus->number)) { tg3_flag_set(tp, 5701_DMA_BUG); pci_dev_put(bridge); @@ -14196,7 +14196,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (bridge && bridge->subordinate && (bridge->subordinate->number <= tp->pdev->bus->number) && - (bridge->subordinate->subordinate >= + (bridge->subordinate->busn_res.end >= tp->pdev->bus->number)) { tg3_flag_set(tp, 40BIT_DMA_BUG); pci_dev_put(bridge); diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index 432d4bbcc62..70517b0f94e 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -174,7 +174,7 @@ static int dino_cfg_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3); void __iomem *base_addr = d->hba.base_addr; unsigned long flags; @@ -209,7 +209,7 @@ static int dino_cfg_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3); void __iomem *base_addr = d->hba.base_addr; unsigned long flags; @@ -554,7 +554,7 @@ dino_fixup_bus(struct pci_bus *bus) struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge)); DBG(KERN_WARNING "%s(0x%p) bus %d platform_data 0x%p\n", - __func__, bus, bus->secondary, + __func__, bus, bus->busn_res.start, bus->bridge->platform_data); /* Firmware doesn't set up card-mode dino, so we have to */ @@ -998,12 +998,12 @@ static int __init dino_probe(struct parisc_device *dev) return 0; } - bus->subordinate = pci_scan_child_bus(bus); + bus->busn_res.end = pci_scan_child_bus(bus); /* This code *depends* on scanning being single threaded * if it isn't, this global bus number count will fail */ - dino_current_bus = bus->subordinate + 1; + dino_current_bus = bus->busn_res.end + 1; pci_bus_assign_resources(bus); pci_bus_add_devices(bus); return 0; diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index 1f9e9fefb8e..83380c8fcb6 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -532,7 +532,7 @@ iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev) intr_slot = PCI_SLOT(pcidev->devfn); } DBG_IRT("iosapic_xlate_pin: bus %d slot %d pin %d\n", - pcidev->bus->secondary, intr_slot, intr_pin); + pcidev->bus->busn_res.start, intr_slot, intr_pin); return irt_find_irqline(isi, intr_slot, intr_pin); } diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 052fa230bc7..cd8f9ce8720 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -189,8 +189,8 @@ lba_dump_res(struct resource *r, int d) static int lba_device_present(u8 bus, u8 dfn, struct lba_device *d) { - u8 first_bus = d->hba.hba_bus->secondary; - u8 last_sub_bus = d->hba.hba_bus->subordinate; + u8 first_bus = d->hba.hba_bus->busn_res.start; + u8 last_sub_bus = d->hba.hba_bus->busn_res.end; if ((bus < first_bus) || (bus > last_sub_bus) || @@ -364,7 +364,7 @@ lba_rd_cfg(struct lba_device *d, u32 tok, u8 reg, u32 size) static int elroy_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data) { struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 tok = LBA_CFG_TOK(local_bus, devfn); void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; @@ -380,7 +380,7 @@ static int elroy_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int return 0; } - if (LBA_SKIP_PROBE(d) && !lba_device_present(bus->secondary, devfn, d)) { + if (LBA_SKIP_PROBE(d) && !lba_device_present(bus->busn_res.start, devfn, d)) { DBG_CFG("%s(%x+%2x) -> -1 (b)\n", __func__, tok, pos); /* either don't want to look or know device isn't present. */ *data = ~0U; @@ -431,7 +431,7 @@ lba_wr_cfg(struct lba_device *d, u32 tok, u8 reg, u32 data, u32 size) static int elroy_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data) { struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 tok = LBA_CFG_TOK(local_bus,devfn); if ((pos > 255) || (devfn > 255)) @@ -444,7 +444,7 @@ static int elroy_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int return 0; } - if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->secondary, devfn, d))) { + if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->busn_res.start, devfn, d))) { DBG_CFG("%s(%x+%2x) = 0x%x (b)\n", __func__, tok, pos,data); return 1; /* New Workaround */ } @@ -481,7 +481,7 @@ static struct pci_ops elroy_cfg_ops = { static int mercury_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data) { struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 tok = LBA_CFG_TOK(local_bus, devfn); void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; @@ -514,7 +514,7 @@ static int mercury_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, i { struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 tok = LBA_CFG_TOK(local_bus,devfn); if ((pos > 255) || (devfn > 255)) @@ -636,7 +636,7 @@ lba_fixup_bus(struct pci_bus *bus) struct lba_device *ldev = LBA_DEV(parisc_walk_tree(bus->bridge)); DBG("lba_fixup_bus(0x%p) bus %d platform_data 0x%p\n", - bus, bus->secondary, bus->bridge->platform_data); + bus, (int)bus->busn_res.start, bus->bridge->platform_data); /* ** Properly Setup MMIO resources for this bus. @@ -1511,7 +1511,7 @@ lba_driver_probe(struct parisc_device *dev) return 0; } - lba_bus->subordinate = pci_scan_child_bus(lba_bus); + lba_bus->busn_res.end = pci_scan_child_bus(lba_bus); /* This is in lieu of calling pci_assign_unassigned_resources() */ if (is_pdc_pat()) { @@ -1541,7 +1541,7 @@ lba_driver_probe(struct parisc_device *dev) lba_dev->flags |= LBA_FLAG_SKIP_PROBE; } - lba_next_bus = lba_bus->subordinate + 1; + lba_next_bus = lba_res->busn_res.end + 1; pci_bus_add_devices(lba_bus); /* Whew! Finally done! Tell services we got this one covered. */ diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44fa645..62d0ae4dfca 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -100,11 +100,11 @@ static int post_dock_fixups(struct notifier_block *nb, unsigned long val, PCI_PRIMARY_BUS, &buses); - if (((buses >> 8) & 0xff) != bus->secondary) { + if (((buses >> 8) & 0xff) != bus->busn_res.start) { buses = (buses & 0xff000000) | ((unsigned int)(bus->primary) << 0) - | ((unsigned int)(bus->secondary) << 8) - | ((unsigned int)(bus->subordinate) << 16); + | ((unsigned int)(bus->busn_res.start) << 8) + | ((unsigned int)(bus->busn_res.end) << 16); pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); } return NOTIFY_OK; @@ -692,7 +692,7 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) * bus->subordinate value because it could have * padding in it. */ - max = bus->secondary; + max = bus->busn_res.start; list_for_each(tmp, &bus->children) { n = pci_bus_max_busnr(pci_bus_b(tmp)); diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index ae853ccd0cd..42f3a61db87 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -292,8 +292,8 @@ int __ref cpci_configure_slot(struct slot *slot) (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { /* Find an unused bus number for the new bridge */ struct pci_bus *child; - unsigned char busnr, start = parent->secondary; - unsigned char end = parent->subordinate; + unsigned char busnr, start = parent->busn_res.start; + unsigned char end = parent->busn_res.end; for (busnr = start; busnr <= end; busnr++) { if (!pci_find_bus(pci_domain_nr(parent), @@ -312,7 +312,7 @@ int __ref cpci_configure_slot(struct slot *slot) pci_dev_put(dev); continue; } - child->subordinate = pci_do_scan_bus(child); + child->busn_res.end = pci_do_scan_bus(child); pci_bus_size_bridges(child); } pci_dev_put(dev); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 47d9dc06b10..b898f06b588 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -37,8 +37,8 @@ static int __ref pciehp_add_bridge(struct pci_dev *dev) { struct pci_bus *parent = dev->bus; - int pass, busnr, start = parent->secondary; - int end = parent->subordinate; + int pass, busnr, start = parent->busn_res.start; + int end = parent->busn_res.end; for (busnr = start; busnr <= end; busnr++) { if (!pci_find_bus(pci_domain_nr(parent), busnr)) diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index df7e4bfadae..d021eb031b3 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -64,8 +64,8 @@ int __ref shpchp_configure_device(struct slot *p_slot) (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { /* Find an unused bus number for the new bridge */ struct pci_bus *child; - unsigned char busnr, start = parent->secondary; - unsigned char end = parent->subordinate; + unsigned char busnr, start = parent->busn_res.start; + unsigned char end = parent->busn_res.end; for (busnr = start; busnr <= end; busnr++) { if (!pci_find_bus(pci_domain_nr(parent), busnr)) @@ -84,7 +84,7 @@ int __ref shpchp_configure_device(struct slot *p_slot) pci_dev_put(dev); continue; } - child->subordinate = pci_do_scan_bus(child); + child->busn_res.end = pci_do_scan_bus(child); pci_bus_size_bridges(child); } pci_configure_slot(dev); diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c index efa30da1ae8..eeb23ceae4a 100644 --- a/drivers/pci/hotplug/shpchp_sysfs.c +++ b/drivers/pci/hotplug/shpchp_sysfs.c @@ -73,13 +73,13 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha } } out += sprintf(out, "Free resources: bus numbers\n"); - for (busnr = bus->secondary; busnr <= bus->subordinate; busnr++) { + for (busnr = bus->busn_res.start; busnr <= bus->busn_res.end; busnr++) { if (!pci_find_bus(pci_domain_nr(bus), busnr)) break; } - if (busnr < bus->subordinate) + if (busnr < bus->busn_res.end) out += sprintf(out, "start = %8.8x, length = %8.8x\n", - busnr, (bus->subordinate - busnr)); + busnr, (int)(bus->busn_res.end - busnr)); return out - buf; } diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 6554e1a0f63..e873060fb35 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -47,7 +47,7 @@ static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) if (!child) return NULL; - child->subordinate = busnr; + child->busn_res.end = busnr; child->dev.parent = bus->bridge; rc = pci_bus_add_child(child); if (rc) { @@ -327,7 +327,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) iov->offset = offset; iov->stride = stride; - if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) { + if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->busn_res.end) { dev_err(&dev->dev, "SR-IOV: bus number out of range\n"); return -ENOMEM; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c0..aeda6e9c245 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -110,7 +110,7 @@ unsigned char pci_bus_max_busnr(struct pci_bus* bus) struct list_head *tmp; unsigned char max, n; - max = bus->subordinate; + max = bus->busn_res.end; list_for_each(tmp, &bus->children) { n = pci_bus_max_busnr(pci_bus_b(tmp)); if(n > max) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 658ac977cb5..651b096134d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -381,8 +381,8 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ return; - dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n", - child->secondary, child->subordinate, + dev_info(&dev->dev, "PCI bridge to %pR%s\n", + &child->busn_res, dev->transparent ? " (subtractive decode)" : ""); pci_bus_remove_resources(child); @@ -599,9 +599,9 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, * Set up the primary, secondary and subordinate * bus numbers. */ - child->number = child->secondary = busnr; - child->primary = parent->secondary; - child->subordinate = 0xff; + child->number = child->busn_res.start = busnr; + child->primary = parent->busn_res.start; + child->busn_res.end = 0xff; if (!bridge) return child; @@ -643,8 +643,8 @@ static void pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max) if (!pcibios_assign_all_busses()) return; - while (parent->parent && parent->subordinate < max) { - parent->subordinate = max; + while (parent->parent && parent->busn_res.end < max) { + parent->busn_res.end = max; pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max); parent = parent->parent; } @@ -718,15 +718,15 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, if (!child) goto out; child->primary = primary; - child->subordinate = subordinate; + child->busn_res.end = subordinate; child->bridge_ctl = bctl; } cmax = pci_scan_child_bus(child); if (cmax > max) max = cmax; - if (child->subordinate > max) - max = child->subordinate; + if (child->busn_res.end > max) + max = child->busn_res.end; } else { /* * We need to assign a number to this bus which we always @@ -759,8 +759,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, } buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) - | ((unsigned int)(child->secondary) << 8) - | ((unsigned int)(child->subordinate) << 16); + | ((unsigned int)(child->busn_res.start) << 8) + | ((unsigned int)(child->busn_res.end) << 16); /* * yenta.c forces a secondary latency timer of 176. @@ -805,8 +805,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, break; while (parent->parent) { if ((!pcibios_assign_all_busses()) && - (parent->subordinate > max) && - (parent->subordinate <= max+i)) { + (parent->busn_res.end > max) && + (parent->busn_res.end <= max+i)) { j = 1; } parent = parent->parent; @@ -827,7 +827,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, /* * Set the subordinate bus number to its real value. */ - child->subordinate = max; + child->busn_res.end = max; pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } @@ -837,19 +837,19 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, /* Has only triggered on CardBus, fixup is in yenta_socket */ while (bus->parent) { - if ((child->subordinate > bus->subordinate) || - (child->number > bus->subordinate) || + if ((child->busn_res.end > bus->busn_res.end) || + (child->number > bus->busn_res.end) || (child->number < bus->number) || - (child->subordinate < bus->number)) { - dev_info(&child->dev, "[bus %02x-%02x] %s " - "hidden behind%s bridge %s [bus %02x-%02x]\n", - child->number, child->subordinate, - (bus->number > child->subordinate && - bus->subordinate < child->number) ? + (child->busn_res.end < bus->number)) { + dev_info(&child->dev, "%pR %s " + "hidden behind%s bridge %s %pR\n", + &child->busn_res, + (bus->number > child->busn_res.end && + bus->busn_res.end < child->number) ? "wholly" : "partially", bus->self->transparent ? " transparent" : "", dev_name(&bus->dev), - bus->number, bus->subordinate); + &bus->busn_res); } bus = bus->parent; } @@ -1548,7 +1548,7 @@ EXPORT_SYMBOL_GPL(pcie_bus_configure_settings); unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) { - unsigned int devfn, pass, max = bus->secondary; + unsigned int devfn, pass, max = bus->busn_res.start; struct pci_dev *dev; dev_dbg(&bus->dev, "scanning bus\n"); @@ -1642,7 +1642,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, /* Create legacy_io and legacy_mem files for this bus */ pci_create_legacy_files(b); - b->number = b->secondary = bus; + b->number = b->busn_res.start = bus; if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); @@ -1693,7 +1693,7 @@ struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus, if (!b) return NULL; - b->subordinate = pci_scan_child_bus(b); + b->busn_res.end = pci_scan_child_bus(b); pci_bus_add_devices(b); return b; } @@ -1710,7 +1710,7 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, pci_add_resource(&resources, &iomem_resource); b = pci_create_root_bus(parent, bus, ops, sysdata, &resources); if (b) - b->subordinate = pci_scan_child_bus(b); + b->busn_res.end = pci_scan_child_bus(b); else pci_free_resource_list(&resources); return b; @@ -1727,7 +1727,7 @@ struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, pci_add_resource(&resources, &iomem_resource); b = pci_create_root_bus(NULL, bus, ops, sysdata, &resources); if (b) { - b->subordinate = pci_scan_child_bus(b); + b->busn_res.end = pci_scan_child_bus(b); pci_bus_add_devices(b); } else { pci_free_resource_list(&resources); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8fa2d4be88d..192172c87b7 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -404,8 +404,8 @@ void pci_setup_cardbus(struct pci_bus *bus) struct resource *res; struct pci_bus_region region; - dev_info(&bridge->dev, "CardBus bridge to [bus %02x-%02x]\n", - bus->secondary, bus->subordinate); + dev_info(&bridge->dev, "CardBus bridge to %pR\n", + &bus->busn_res); res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); @@ -553,8 +553,8 @@ static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) { struct pci_dev *bridge = bus->self; - dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n", - bus->secondary, bus->subordinate); + dev_info(&bridge->dev, "PCI bridge to %pR\n", + &bus->busn_res); if (type & IORESOURCE_IO) pci_setup_bridge_io(bus); @@ -745,8 +745,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, if (!size0 && !size1) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " - "%pR to [bus %02x-%02x] (unused)\n", b_res, - bus->secondary, bus->subordinate); + "%pR to %pR (unused)\n", b_res, + &bus->busn_res); b_res->flags = 0; return; } @@ -757,8 +757,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, if (size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096); dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " - "%pR to [bus %02x-%02x] add_size %lx\n", b_res, - bus->secondary, bus->subordinate, size1-size0); + "%pR to %pR add_size %lx\n", b_res, + &bus->busn_res, size1-size0); } } @@ -863,8 +863,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (!size0 && !size1) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " - "%pR to [bus %02x-%02x] (unused)\n", b_res, - bus->secondary, bus->subordinate); + "%pR to %pR (unused)\n", b_res, + &bus->busn_res); b_res->flags = 0; return 1; } @@ -874,8 +874,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align); dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " - "%pR to [bus %02x-%02x] add_size %llx\n", b_res, - bus->secondary, bus->subordinate, (unsigned long long)size1-size0); + "%pR to %pR add_size %llx\n", b_res, + &bus->busn_res, (unsigned long long)size1-size0); } return 1; } diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index 6e75153c5b4..24caeaf5052 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -73,7 +73,7 @@ int __ref cb_alloc(struct pcmcia_socket *s) s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0)); pci_fixup_cardbus(bus); - max = bus->secondary; + max = bus->busn_res.start; for (pass = 0; pass < 2; pass++) list_for_each_entry(dev, &bus->devices, bus_list) if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index d07f9ac8c41..667678db115 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1048,8 +1048,8 @@ static void yenta_config_init(struct yenta_socket *socket) config_writeb(socket, PCI_LATENCY_TIMER, 168); config_writel(socket, PCI_PRIMARY_BUS, (176 << 24) | /* sec. latency timer */ - (dev->subordinate->subordinate << 16) | /* subordinate bus */ - (dev->subordinate->secondary << 8) | /* secondary bus */ + ((unsigned int)dev->subordinate->busn_res.end << 16) | /* subordinate bus */ + ((unsigned int)dev->subordinate->busn_res.start << 8) | /* secondary bus */ dev->subordinate->primary); /* primary bus */ /* @@ -1086,14 +1086,14 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) struct pci_bus *bridge_to_fix = cardbus_bridge->parent; /* Check bus numbers are already set up correctly: */ - if (bridge_to_fix->subordinate >= cardbus_bridge->subordinate) + if (bridge_to_fix->busn_res.end >= cardbus_bridge->busn_res.end) return; /* The subordinate number is ok, nothing to do */ if (!bridge_to_fix->parent) return; /* Root bridges are ok */ /* stay within the limits of the bus range of the parent: */ - upper_limit = bridge_to_fix->parent->subordinate; + upper_limit = bridge_to_fix->parent->busn_res.end; /* check the bus ranges of all silbling bridges to prevent overlap */ list_for_each(tmp, &bridge_to_fix->parent->children) { @@ -1104,36 +1104,36 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) * current upper limit, set the new upper limit to * the bus number below the silbling's range: */ - if (silbling->secondary > bridge_to_fix->subordinate - && silbling->secondary <= upper_limit) - upper_limit = silbling->secondary - 1; + if (silbling->busn_res.start > bridge_to_fix->busn_res.end + && silbling->busn_res.start <= upper_limit) + upper_limit = silbling->busn_res.start - 1; } /* Show that the wanted subordinate number is not possible: */ - if (cardbus_bridge->subordinate > upper_limit) + if (cardbus_bridge->busn_res.end > upper_limit) dev_printk(KERN_WARNING, &cardbus_bridge->dev, "Upper limit for fixing this " "bridge's parent bridge: #%02x\n", upper_limit); /* If we have room to increase the bridge's subordinate number, */ - if (bridge_to_fix->subordinate < upper_limit) { + if (bridge_to_fix->busn_res.end < upper_limit) { /* use the highest number of the hidden bus, within limits */ unsigned char subordinate_to_assign = - min(cardbus_bridge->subordinate, upper_limit); + min_t(int, cardbus_bridge->busn_res.end, upper_limit); dev_printk(KERN_INFO, &bridge_to_fix->dev, "Raising subordinate bus# of parent " "bus (#%02x) from #%02x to #%02x\n", bridge_to_fix->number, - bridge_to_fix->subordinate, subordinate_to_assign); + (int)bridge_to_fix->busn_res.end, subordinate_to_assign); /* Save the new subordinate in the bus struct of the bridge */ - bridge_to_fix->subordinate = subordinate_to_assign; + bridge_to_fix->busn_res.end = subordinate_to_assign; /* and update the PCI config space with the new subordinate */ pci_write_config_byte(bridge_to_fix->self, - PCI_SUBORDINATE_BUS, bridge_to_fix->subordinate); + PCI_SUBORDINATE_BUS, bridge_to_fix->busn_res.end); } } -- cgit v1.2.3-70-g09d2 From fbebb9fd22581b6422d60669c4ff86ce99d6cdba Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 16 Jun 2012 14:40:22 -0600 Subject: PCI: add infrastructure for devices with broken INTx masking pci_intx_mask_supported() assumes INTx masking is supported if the PCI_COMMAND_INTX_DISABLE bit is writable. But when that bit is set, some devices don't actually mask INTx or update PCI_STATUS_INTERRUPT as we expect. This patch adds a way for quirks to identify these broken devices. [bhelgaas: split out from Chelsio quirk addition] Signed-off-by: Jan Kiszka Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +++ drivers/pci/quirks.c | 10 ++++++++++ include/linux/pci.h | 1 + 3 files changed, 14 insertions(+) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c0..9ae517a6836 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2876,6 +2876,9 @@ bool pci_intx_mask_supported(struct pci_dev *dev) bool mask_supported = false; u16 orig, new; + if (dev->broken_intx_masking) + return false; + pci_cfg_access_lock(dev); pci_read_config_word(dev, PCI_COMMAND, &orig); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a752167754..cc13415416d 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2929,6 +2929,16 @@ static void __devinit disable_igfx_irq(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); +/* + * Some devices may pass our check in pci_intx_mask_supported if + * PCI_COMMAND_INTX_DISABLE works though they actually do not properly + * support this feature. + */ +static void __devinit quirk_broken_intx_masking(struct pci_dev *dev) +{ + dev->broken_intx_masking = 1; +} + static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6a..40a039f1dff 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -324,6 +324,7 @@ struct pci_dev { unsigned int is_hotplug_bridge:1; unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first:1; + unsigned int broken_intx_masking:1; pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ -- cgit v1.2.3-70-g09d2 From d6d88c832eaea6c6947ddf7b664601930a9f8a14 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 19 Jun 2012 06:54:49 -0600 Subject: PCI: use __weak consistently Use "__weak" instead of the gcc-specific "__attribute__ ((weak))" Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 2 +- drivers/pci/pci.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 86c63fe45d1..a0b435f20bd 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1112,7 +1112,7 @@ static struct bin_attribute pcie_config_attr = { .write = pci_write_config, }; -int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev) +int __weak pcibios_add_platform_entries(struct pci_dev *dev) { return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c0..8f4a5ea543f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1349,7 +1349,7 @@ void pcim_pin_device(struct pci_dev *pdev) * is the default implementation. Architecture implementations can * override this. */ -void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {} +void __weak pcibios_disable_device (struct pci_dev *dev) {} static void do_pci_disable_device(struct pci_dev *dev) { @@ -1413,8 +1413,8 @@ pci_disable_device(struct pci_dev *dev) * Sets the PCIe reset state for the device. This is the default * implementation. Architecture implementations can override this. */ -int __attribute__ ((weak)) pcibios_set_pcie_reset_state(struct pci_dev *dev, - enum pcie_reset_state state) +int __weak pcibios_set_pcie_reset_state(struct pci_dev *dev, + enum pcie_reset_state state) { return -EINVAL; } @@ -3851,7 +3851,7 @@ static void __devinit pci_no_domains(void) * greater than 0xff). This is the default implementation. Architecture * implementations can override this. */ -int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev) +int __weak pci_ext_cfg_avail(struct pci_dev *dev) { return 1; } -- cgit v1.2.3-70-g09d2 From 8291550f8479fde2cee571d1b367e6918819f189 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 19 Jun 2012 07:35:34 -0600 Subject: PCI: fix upstream P2P bridge checks when enabling OBFF and LTR pci_enable_obff() and pci_enable_ltr() incorrectly check "dev->bus" instead of "dev->bus->self" to determine whether the upstream device is a P2P bridge or a host bridge. For devices on the root bus, the upstream device is a host bridge, "dev->bus != NULL" and "dev->bus->self == NULL", and we panic with a null pointer dereference. These functions should previously have panicked when called on devices supporting OBFF or LTR, so they should be regarded as untested. Found by Coverity (CID 143038 and CID 143039). Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f4a5ea543f..b9e93cf1eb4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2108,7 +2108,7 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) return -ENOTSUPP; /* no OBFF support at all */ /* Make sure the topology supports OBFF as well */ - if (dev->bus) { + if (dev->bus->self) { ret = pci_enable_obff(dev->bus->self, type); if (ret) return ret; @@ -2215,7 +2215,7 @@ int pci_enable_ltr(struct pci_dev *dev) return -EINVAL; /* Enable upstream ports first */ - if (dev->bus) { + if (dev->bus->self) { ret = pci_enable_ltr(dev->bus->self); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 809a3bf9f34cb6d0c0383b31b3495fa1ed3508a6 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 20 Jun 2012 16:41:16 -0600 Subject: PCI: remove useless pcix_set_mmrbc() dev->bus check For a valid pci_dev, dev->bus != NULL always, so remove this unnecessary test. Found by Coverity (CID 101680). Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b9e93cf1eb4..7f1310e5853 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3395,8 +3395,7 @@ int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc) o = (cmd & PCI_X_CMD_MAX_READ) >> 2; if (o != v) { - if (v > o && dev->bus && - (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC)) + if (v > o && (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC)) return -EIO; cmd &= ~PCI_X_CMD_MAX_READ; -- cgit v1.2.3-70-g09d2 From 71a83bd727cc31c5fe960c3758cb396267ff710e Mon Sep 17 00:00:00 2001 From: Zheng Yan Date: Sat, 23 Jun 2012 10:23:49 +0800 Subject: PCI/PM: add runtime PM support to PCIe port This patch adds runtime PM support to PCIe port. This is needed by PCIe D3cold support, where PCIe device without ACPI node may be powered on/off by PCIe port. Because runtime suspend is broken for some chipsets, a black list is used to disable runtime PM support for these chipsets. Reviewed-by: Rafael J. Wysocki Signed-off-by: Zheng Yan Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 10 ++++++++++ drivers/pci/pcie/portdrv_pci.c | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c0..9eae64b1795 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1518,6 +1518,16 @@ static void pci_pme_list_scan(struct work_struct *work) if (!list_empty(&pci_pme_list)) { list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { if (pme_dev->dev->pme_poll) { + struct pci_dev *bridge; + + bridge = pme_dev->dev->bus->self; + /* + * If bridge is in low power state, the + * configuration space of subordinate devices + * may be not accessible + */ + if (bridge && bridge->current_state != PCI_D0) + continue; pci_pme_wakeup(pme_dev->dev, NULL); } else { list_del(&pme_dev->list); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index e0610bda1de..7c576b9aa01 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,15 @@ static int pcie_port_resume_noirq(struct device *dev) return 0; } +#ifdef CONFIG_PM_RUNTIME +static int pcie_port_runtime_pm(struct device *dev) +{ + return 0; +} +#else +#define pcie_port_runtime_pm NULL +#endif + static const struct dev_pm_ops pcie_portdrv_pm_ops = { .suspend = pcie_port_device_suspend, .resume = pcie_port_device_resume, @@ -107,6 +117,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .poweroff = pcie_port_device_suspend, .restore = pcie_port_device_resume, .resume_noirq = pcie_port_resume_noirq, + .runtime_suspend = pcie_port_runtime_pm, + .runtime_resume = pcie_port_runtime_pm, }; #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) @@ -116,6 +128,14 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { #define PCIE_PORTDRV_PM_OPS NULL #endif /* !PM */ +/* + * PCIe port runtime suspend is broken for some chipsets, so use a + * black list to disable runtime PM for these chipsets. + */ +static const struct pci_device_id port_runtime_pm_black_list[] = { + { /* end: all zeroes */ } +}; + /* * pcie_portdrv_probe - Probe PCI-Express port devices * @dev: PCI-Express port device being probed @@ -144,12 +164,16 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev, return status; pci_save_state(dev); + if (!pci_match_id(port_runtime_pm_black_list, dev)) + pm_runtime_put_noidle(&dev->dev); return 0; } static void pcie_portdrv_remove(struct pci_dev *dev) { + if (!pci_match_id(port_runtime_pm_black_list, dev)) + pm_runtime_get_noresume(&dev->dev); pcie_port_device_remove(dev); pci_disable_device(dev); } -- cgit v1.2.3-70-g09d2 From 448bd857d48e69b33ef323739dc6d8ca20d4cda7 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Sat, 23 Jun 2012 10:23:51 +0800 Subject: PCI/PM: add PCIe runtime D3cold support This patch adds runtime D3cold support and corresponding ACPI platform support. This patch only enables runtime D3cold support; it does not enable D3cold support during system suspend/hibernate. D3cold is the deepest power saving state for a PCIe device, where its main power is removed. While it is in D3cold, you can't access the device at all, not even its configuration space (which is still accessible in D3hot). Therefore the PCI PM registers can not be used to transition into/out of the D3cold state; that must be done by platform logic such as ACPI _PR3. To support wakeup from D3cold, a system may provide auxiliary power, which allows a device to request wakeup using a Beacon or the sideband WAKE# signal. WAKE# is usually connected to platform logic such as ACPI GPE. This is quite different from other power saving states, where devices request wakeup via a PME message on the PCIe link. Some devices, such as those in plug-in slots, have no direct platform logic. For example, there is usually no ACPI _PR3 for them. D3cold support for these devices can be done via the PCIe Downstream Port leading to the device. When the PCIe port is powered on/off, the device is powered on/off too. Wakeup events from the device will be notified to the corresponding PCIe port. For more information about PCIe D3cold and corresponding ACPI support, please refer to: - PCI Express Base Specification Revision 2.0 - Advanced Configuration and Power Interface Specification Revision 5.0 [bhelgaas: changelog] Reviewed-by: Rafael J. Wysocki Originally-by: Zheng Yan Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 23 +++++++-- drivers/pci/pci-driver.c | 10 +++- drivers/pci/pci-sysfs.c | 29 +++++++++++ drivers/pci/pci.c | 114 +++++++++++++++++++++++++++++++++++++---- drivers/pci/pci.h | 1 + drivers/pci/pcie/portdrv_pci.c | 44 ++++++++++++++-- include/linux/pci.h | 16 ++++-- 7 files changed, 215 insertions(+), 22 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index a9efebc586b..e1658afef87 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -48,6 +48,12 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev) return; + if (pci_dev->current_state == PCI_D3cold) { + pci_wakeup_event(pci_dev); + pm_runtime_resume(&pci_dev->dev); + return; + } + if (!pci_dev->pm_cap || !pci_dev->pme_support || pci_check_pme_status(pci_dev)) { if (pci_dev->pme_poll) @@ -187,10 +193,13 @@ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { - int acpi_state; + int acpi_state, d_max; - acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, - ACPI_STATE_D3); + if (pdev->no_d3cold) + d_max = ACPI_STATE_D3_HOT; + else + d_max = ACPI_STATE_D3_COLD; + acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, d_max); if (acpi_state < 0) return PCI_POWER_ERROR; @@ -297,7 +306,13 @@ static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) { - if (dev->pme_interrupt) + /* + * Per PCI Express Base Specification Revision 2.0 section + * 5.3.3.2 Link Wakeup, platform support is needed for D3cold + * waking up to power on the main link even if there is PME + * support for D3cold + */ + if (dev->pme_interrupt && !dev->runtime_d3cold) return 0; if (!acpi_pm_device_run_wake(&dev->dev, enable)) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bf0cee629b6..ca2e4c79a58 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1019,10 +1019,13 @@ static int pci_pm_runtime_suspend(struct device *dev) if (!pm || !pm->runtime_suspend) return -ENOSYS; + pci_dev->no_d3cold = false; error = pm->runtime_suspend(dev); suspend_report_result(pm->runtime_suspend, error); if (error) return error; + if (!pci_dev->d3cold_allowed) + pci_dev->no_d3cold = true; pci_fixup_device(pci_fixup_suspend, pci_dev); @@ -1044,6 +1047,7 @@ static int pci_pm_runtime_suspend(struct device *dev) static int pci_pm_runtime_resume(struct device *dev) { + int rc; struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; @@ -1054,7 +1058,11 @@ static int pci_pm_runtime_resume(struct device *dev) __pci_enable_wake(pci_dev, PCI_D0, true, false); pci_fixup_device(pci_fixup_resume, pci_dev); - return pm->runtime_resume(dev); + rc = pm->runtime_resume(dev); + + pci_dev->runtime_d3cold = false; + + return rc; } static int pci_pm_runtime_idle(struct device *dev) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 86c63fe45d1..1426db0c060 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "pci.h" static int sysfs_initialized; /* = 0 */ @@ -378,6 +379,31 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, #endif +#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) +static ssize_t d3cold_allowed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + pdev->d3cold_allowed = !!val; + pm_runtime_resume(dev); + + return count; +} + +static ssize_t d3cold_allowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + return sprintf (buf, "%u\n", pdev->d3cold_allowed); +} +#endif + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), @@ -401,6 +427,9 @@ struct device_attribute pci_dev_attrs[] = { #ifdef CONFIG_HOTPLUG __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), +#endif +#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) + __ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store), #endif __ATTR_NULL, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9eae64b1795..8effb9b23ee 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -622,7 +622,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) dev_info(&dev->dev, "Refused to change power state, " "currently in D%d\n", dev->current_state); - /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT + /* + * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning * from D3hot to D0 _may_ perform an internal reset, thereby * going to "D0 Uninitialized" rather than "D0 Initialized". @@ -654,6 +655,16 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state) if (dev->pm_cap) { u16 pmcsr; + /* + * Configuration space is not accessible for device in + * D3cold, so just keep or set D3cold for safety + */ + if (dev->current_state == PCI_D3cold) + return; + if (state == PCI_D3cold) { + dev->current_state = PCI_D3cold; + return; + } pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { @@ -694,8 +705,50 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) */ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) { - if (state == PCI_D0) + if (state == PCI_D0) { pci_platform_power_transition(dev, PCI_D0); + /* + * Mandatory power management transition delays, see + * PCI Express Base Specification Revision 2.0 Section + * 6.6.1: Conventional Reset. Do not delay for + * devices powered on/off by corresponding bridge, + * because have already delayed for the bridge. + */ + if (dev->runtime_d3cold) { + msleep(dev->d3cold_delay); + /* + * When powering on a bridge from D3cold, the + * whole hierarchy may be powered on into + * D0uninitialized state, resume them to give + * them a chance to suspend again + */ + pci_wakeup_bus(dev->subordinate); + } + } +} + +/** + * __pci_dev_set_current_state - Set current state of a PCI device + * @dev: Device to handle + * @data: pointer to state to be set + */ +static int __pci_dev_set_current_state(struct pci_dev *dev, void *data) +{ + pci_power_t state = *(pci_power_t *)data; + + dev->current_state = state; + return 0; +} + +/** + * __pci_bus_set_current_state - Walk given bus and set current state of devices + * @bus: Top bus of the subtree to walk. + * @state: state to be set + */ +static void __pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state) +{ + if (bus) + pci_walk_bus(bus, __pci_dev_set_current_state, &state); } /** @@ -707,8 +760,15 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) */ int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state) { - return state >= PCI_D0 ? - pci_platform_power_transition(dev, state) : -EINVAL; + int ret; + + if (state < PCI_D0) + return -EINVAL; + ret = pci_platform_power_transition(dev, state); + /* Power off the bridge may power off the whole hierarchy */ + if (!ret && state == PCI_D3cold) + __pci_bus_set_current_state(dev->subordinate, PCI_D3cold); + return ret; } EXPORT_SYMBOL_GPL(__pci_complete_power_transition); @@ -732,8 +792,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) int error; /* bound the state we're entering */ - if (state > PCI_D3hot) - state = PCI_D3hot; + if (state > PCI_D3cold) + state = PCI_D3cold; else if (state < PCI_D0) state = PCI_D0; else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev)) @@ -748,10 +808,15 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) /* This device is quirked not to be put into D3, so don't put it in D3 */ - if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) + if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) return 0; - error = pci_raw_set_power_state(dev, state); + /* + * To put device in D3cold, we put device into D3hot in native + * way, then put device into D3cold with platform ops + */ + error = pci_raw_set_power_state(dev, state > PCI_D3hot ? + PCI_D3hot : state); if (!__pci_complete_power_transition(dev, state)) error = 0; @@ -1497,6 +1562,28 @@ void pci_pme_wakeup_bus(struct pci_bus *bus) pci_walk_bus(bus, pci_pme_wakeup, (void *)true); } +/** + * pci_wakeup - Wake up a PCI device + * @dev: Device to handle. + * @ign: ignored parameter + */ +static int pci_wakeup(struct pci_dev *pci_dev, void *ign) +{ + pci_wakeup_event(pci_dev); + pm_request_resume(&pci_dev->dev); + return 0; +} + +/** + * pci_wakeup_bus - Walk given bus and wake up devices on it + * @bus: Top bus of the subtree to walk. + */ +void pci_wakeup_bus(struct pci_bus *bus) +{ + if (bus) + pci_walk_bus(bus, pci_wakeup, NULL); +} + /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. @@ -1754,6 +1841,10 @@ int pci_prepare_to_sleep(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; + /* D3cold during system suspend/hibernate is not supported */ + if (target_state > PCI_D3hot) + target_state = PCI_D3hot; + pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev)); error = pci_set_power_state(dev, target_state); @@ -1791,12 +1882,16 @@ int pci_finish_runtime_suspend(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; + dev->runtime_d3cold = target_state == PCI_D3cold; + __pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev)); error = pci_set_power_state(dev, target_state); - if (error) + if (error) { __pci_enable_wake(dev, target_state, true, false); + dev->runtime_d3cold = false; + } return error; } @@ -1866,6 +1961,7 @@ void pci_pm_init(struct pci_dev *dev) dev->pm_cap = pm; dev->d3_delay = PCI_PM_D3_WAIT; + dev->d3cold_delay = PCI_PM_D3COLD_WAIT; dev->d1_support = false; dev->d2_support = false; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b23..5cd3dce7a24 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -70,6 +70,7 @@ extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); extern int pci_finish_runtime_suspend(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +extern void pci_wakeup_bus(struct pci_bus *bus); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 7c576b9aa01..3a7eefcb270 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -101,12 +101,48 @@ static int pcie_port_resume_noirq(struct device *dev) } #ifdef CONFIG_PM_RUNTIME -static int pcie_port_runtime_pm(struct device *dev) +struct d3cold_info { + bool no_d3cold; + unsigned int d3cold_delay; +}; + +static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data) +{ + struct d3cold_info *info = data; + + info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay, + info->d3cold_delay); + if (pdev->no_d3cold) + info->no_d3cold = true; + return 0; +} + +static int pcie_port_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct d3cold_info d3cold_info = { + .no_d3cold = false, + .d3cold_delay = PCI_PM_D3_WAIT, + }; + + /* + * If any subordinate device disable D3cold, we should not put + * the port into D3cold. The D3cold delay of port should be + * the max of that of all subordinate devices. + */ + pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info); + pdev->no_d3cold = d3cold_info.no_d3cold; + pdev->d3cold_delay = d3cold_info.d3cold_delay; + return 0; +} + +static int pcie_port_runtime_resume(struct device *dev) { return 0; } #else -#define pcie_port_runtime_pm NULL +#define pcie_port_runtime_suspend NULL +#define pcie_port_runtime_resume NULL #endif static const struct dev_pm_ops pcie_portdrv_pm_ops = { @@ -117,8 +153,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .poweroff = pcie_port_device_suspend, .restore = pcie_port_device_resume, .resume_noirq = pcie_port_resume_noirq, - .runtime_suspend = pcie_port_runtime_pm, - .runtime_resume = pcie_port_runtime_pm, + .runtime_suspend = pcie_port_runtime_suspend, + .runtime_resume = pcie_port_runtime_resume, }; #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6a..002cfd3e33c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -132,9 +132,10 @@ static inline const char *pci_power_name(pci_power_t state) return pci_power_names[1 + (int) state]; } -#define PCI_PM_D2_DELAY 200 -#define PCI_PM_D3_WAIT 10 -#define PCI_PM_BUS_WAIT 50 +#define PCI_PM_D2_DELAY 200 +#define PCI_PM_D3_WAIT 10 +#define PCI_PM_D3COLD_WAIT 100 +#define PCI_PM_BUS_WAIT 50 /** The pci_channel state describes connectivity between the CPU and * the pci device. If some PCI bus between here and the pci device @@ -278,11 +279,18 @@ struct pci_dev { unsigned int pme_poll:1; /* Poll device's PME status bit */ unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ - unsigned int no_d1d2:1; /* Only allow D0 and D3 */ + unsigned int no_d1d2:1; /* D1 and D2 are forbidden */ + unsigned int no_d3cold:1; /* D3cold is forbidden */ + unsigned int d3cold_allowed:1; /* D3cold is allowed by user */ unsigned int mmio_always_on:1; /* disallow turning off io/mem decoding during bar sizing */ unsigned int wakeup_prepared:1; + unsigned int runtime_d3cold:1; /* whether go through runtime + D3cold, not set for devices + powered on/off by the + corresponding bridge */ unsigned int d3_delay; /* D3->D0 transition time in ms */ + unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ #ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state; /* ASPM link state. */ -- cgit v1.2.3-70-g09d2 From 2b6f2c3520124e8bad4bffa71f5b98e602b9cf03 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 25 Jun 2012 21:30:57 -0600 Subject: PCI: pull pcibios_setup() up into core Currently, all of the architectures implement their own pcibios_setup() routine. Most of the implementations do nothing so this patch introduces a generic (__weak) routine in the core that can be used by all architectures as a default. If necessary, it can be overridden by architecture-specific code. Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 12 ++++++++++++ include/linux/pci.h | 1 + 2 files changed, 13 insertions(+) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c0..c87d518acac 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2665,6 +2665,18 @@ static void __pci_set_master(struct pci_dev *dev, bool enable) dev->is_busmaster = enable; } +/** + * pcibios_setup - process "pci=" kernel boot arguments + * @str: string used to pass in "pci=" kernel boot arguments + * + * Process kernel boot arguments. This is the default implementation. + * Architecture specific implementations can override this as necessary. + */ +char * __weak __init pcibios_setup(char *str) +{ + return str; +} + /** * pcibios_set_master - enable PCI bus-mastering for device dev * @dev: the PCI device to enable diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6a..f91143e86f8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -642,6 +642,7 @@ extern int no_pci_devices(void); void pcibios_fixup_bus(struct pci_bus *); int __must_check pcibios_enable_device(struct pci_dev *, int mask); +/* Architecture specific versions may override this (weak) */ char *pcibios_setup(char *str); /* Used only when drivers/pci/setup.c is used */ -- cgit v1.2.3-70-g09d2 From db288c9c5f9db45610dab3940377625132b4af41 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 15:20:00 -0600 Subject: PCI / PM: restore the original behavior of pci_set_power_state() Commit cc2893b6 (PCI: Ensure we re-enable devices on resume) addressed the problem with USB not being powered after resume on recent Lenovo machines, but it did that in a suboptimal way. Namely, it should have changed the relevant code paths only, which are pci_pm_resume_noirq() and pci_pm_restore_noirq() supposed to restore the device's power and standard configuration registers after system resume from suspend or hibernation. Instead, however, it modified pci_set_power_state() which is executed in several other situations too. That resulted in some undesirable effects, like attempting to change a device's power state in the same way multiple times in a row (up to as many as 4 times in a row in the snd_hda_intel driver). Fix the bug addressed by commit cc2893b6 in an alternative way, by forcibly powering up all devices in pci_pm_default_resume_early(), which is called by pci_pm_resume_noirq() and pci_pm_restore_noirq() to restore the device's power and standard configuration registers, and modifying pci_pm_runtime_resume() to avoid the forcible power-up if not necessary. Then, revert the changes made by commit cc2893b6 to make the confusion introduced by it go away. Acked-by: Matthew Garrett Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 14 ++++++++------ drivers/pci/pci.c | 19 ++++++++++++++++++- drivers/pci/pci.h | 1 + 3 files changed, 27 insertions(+), 7 deletions(-) (limited to 'drivers/pci/pci.c') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index ca2e4c79a58..1dd1d9dfa17 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -459,16 +459,17 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev) return 0; } +#endif + +#ifdef CONFIG_PM_SLEEP + static void pci_pm_default_resume_early(struct pci_dev *pci_dev) { - pci_restore_standard_config(pci_dev); + pci_power_up(pci_dev); + pci_restore_state(pci_dev); pci_fixup_device(pci_fixup_resume_early, pci_dev); } -#endif - -#ifdef CONFIG_PM_SLEEP - /* * Default "suspend" method for devices that have no driver provided suspend, * or not even a driver at all (second part). @@ -1054,7 +1055,8 @@ static int pci_pm_runtime_resume(struct device *dev) if (!pm || !pm->runtime_resume) return -ENOSYS; - pci_pm_default_resume_early(pci_dev); + pci_restore_standard_config(pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); __pci_enable_wake(pci_dev, PCI_D0, true, false); pci_fixup_device(pci_fixup_resume, pci_dev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8effb9b23ee..acae2705e7f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -672,6 +672,19 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state) } } +/** + * pci_power_up - Put the given device into D0 forcibly + * @dev: PCI device to power up + */ +void pci_power_up(struct pci_dev *dev) +{ + if (platform_pci_power_manageable(dev)) + platform_pci_set_power_state(dev, PCI_D0); + + pci_raw_set_power_state(dev, PCI_D0); + pci_update_current_state(dev, PCI_D0); +} + /** * pci_platform_power_transition - Use platform to change device power state * @dev: PCI device to handle. @@ -762,7 +775,7 @@ int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state) { int ret; - if (state < PCI_D0) + if (state <= PCI_D0) return -EINVAL; ret = pci_platform_power_transition(dev, state); /* Power off the bridge may power off the whole hierarchy */ @@ -804,6 +817,10 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) */ return 0; + /* Check if we're already there */ + if (dev->current_state == state) + return 0; + __pci_start_power_transition(dev, state); /* This device is quirked not to be put into D3, so diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5cd3dce7a24..331857855eb 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -67,6 +67,7 @@ struct pci_platform_pm_ops { extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); +extern void pci_power_up(struct pci_dev *dev); extern void pci_disable_enabled_device(struct pci_dev *dev); extern int pci_finish_runtime_suspend(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); -- cgit v1.2.3-70-g09d2