From 499733e0cc1a00523c5056a690f65dea7b9da140 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Wed, 5 Aug 2015 02:23:38 +0530 Subject: PCI: generic: Remove dependency on ARM-specific struct hw_pci The generic OF-based host controller driver uses pci_common_init_dev(), which is ARM-specific and requires the ARM struct hw_pci. The part of pci_common_init_dev() that is needed is limited and can be done here without using hw_pci. Note that the ARM pcibios functions expect the PCI sysdata to be a pointer to a struct pci_sys_data. Add a struct pci_sys_data as the first element in struct gen_pci so that when we use a gen_pci pointer as sysdata, it is also a pointer to a struct pci_sys_data. Create and scan the root bus directly without using the ARM pci_common_init_dev() interface. [bhelgaas: changelog, move pcie_bus_configure_settings() before pci_bus_add_devices(), combine !PCI_PROBE_ONLY blocks] Tested-by: Lorenzo Pieralisi Tested-by: Suravee Suthikulpanit Tested-by: Pavel Fedin Signed-off-by: Jayachandran C Signed-off-by: Bjorn Helgaas Acked-by: Lorenzo Pieralisi --- drivers/pci/host/pci-host-generic.c | 52 +++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index ba46e581db99..265dd25169bf 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -38,7 +38,16 @@ struct gen_pci_cfg_windows { const struct gen_pci_cfg_bus_ops *ops; }; +/* + * ARM pcibios functions expect the ARM struct pci_sys_data as the PCI + * sysdata. Add pci_sys_data as the first element in struct gen_pci so + * that when we use a gen_pci pointer as sysdata, it is also a pointer to + * a struct pci_sys_data. + */ struct gen_pci { +#ifdef CONFIG_ARM + struct pci_sys_data sys; +#endif struct pci_host_bridge host; struct gen_pci_cfg_windows cfg; struct list_head resources; @@ -48,8 +57,7 @@ static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus, unsigned int devfn, int where) { - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; + struct gen_pci *pci = bus->sysdata; resource_size_t idx = bus->number - pci->cfg.bus_range->start; return pci->cfg.win[idx] + ((devfn << 8) | where); @@ -64,8 +72,7 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus, unsigned int devfn, int where) { - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; + struct gen_pci *pci = bus->sysdata; resource_size_t idx = bus->number - pci->cfg.bus_range->start; return pci->cfg.win[idx] + ((devfn << 12) | where); @@ -198,13 +205,6 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) return 0; } -static int gen_pci_setup(int nr, struct pci_sys_data *sys) -{ - struct gen_pci *pci = sys->private_data; - list_splice_init(&pci->resources, &sys->resources); - return 1; -} - static int gen_pci_probe(struct platform_device *pdev) { int err; @@ -214,13 +214,7 @@ static int gen_pci_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - struct hw_pci hw = { - .nr_controllers = 1, - .private_data = (void **)&pci, - .setup = gen_pci_setup, - .map_irq = of_irq_parse_and_map_pci, - .ops = &gen_pci_ops, - }; + struct pci_bus *bus, *child; if (!pci) return -ENOMEM; @@ -258,7 +252,27 @@ static int gen_pci_probe(struct platform_device *pdev) return err; } - pci_common_init_dev(dev, &hw); + /* Do not reassign resources if probe only */ + if (!pci_has_flag(PCI_PROBE_ONLY)) + pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS); + + bus = pci_scan_root_bus(dev, 0, &gen_pci_ops, pci, &pci->resources); + if (!bus) { + dev_err(dev, "Scanning rootbus failed"); + return -ENODEV; + } + + pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); + + if (!pci_has_flag(PCI_PROBE_ONLY)) { + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + } + + pci_bus_add_devices(bus); return 0; } -- cgit v1.2.1 From 459a07721c113b807ffcaa7bc98dd5d26beb39d5 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Wed, 5 Aug 2015 02:23:39 +0530 Subject: PCI: Build setup-irq.o for arm64 ARM64 requires setup-irq.o to provide pci_fixup_irqs() implementation. We are adding this now to support the pci-host-generic host controller, but we enable it for ARM64 PCI so that other host controllers can use this as well. Signed-off-by: Jayachandran C Signed-off-by: Bjorn Helgaas --- drivers/pci/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 73e4af400a5a..be3f631c3f75 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_PCI_IOV) += iov.o # obj-$(CONFIG_ALPHA) += setup-irq.o obj-$(CONFIG_ARM) += setup-irq.o +obj-$(CONFIG_ARM64) += setup-irq.o obj-$(CONFIG_UNICORE32) += setup-irq.o obj-$(CONFIG_SUPERH) += setup-irq.o obj-$(CONFIG_MIPS) += setup-irq.o -- cgit v1.2.1 From aa4a5c0d2d7e3c30f9df033ea0367f148bb369f6 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Wed, 5 Aug 2015 02:23:40 +0530 Subject: PCI: generic: Add arm64 support Make pci-host-generic driver (kernel option PCI_HOST_GENERIC) available on arm64. Signed-off-by: Jayachandran C Signed-off-by: Bjorn Helgaas --- drivers/pci/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index c132bddc03f3..675c2d1ef887 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -53,7 +53,7 @@ config PCI_RCAR_GEN2_PCIE config PCI_HOST_GENERIC bool "Generic PCI host controller" - depends on ARM && OF + depends on (ARM || ARM64) && OF help Say Y here if you want to support a simple generic PCI host controller, such as the one emulated by kvmtool. -- cgit v1.2.1 From d2a7926d42b3b46e45b4e44dc3302b2701ec0856 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Mon, 3 Aug 2015 21:27:10 -0500 Subject: PCI: Add pci_scan_root_bus_msi() Add a pci_scan_root_bus_msi() interface so an arch can specify the MSI controller up front. This removes the need for a pcibios callback to set the MSI controller later. This is not exported because I'd like to replace the variety of "scan root bus" interfaces with a single, more extensible interface that can handle the MSI controller, domain, pci_ops, resources, etc. I hope this interface is temporary. [bhelgaas: changelog, split into separate patch] Suggested-by: Russell King Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Jingoo Han --- drivers/pci/probe.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cefd636681b6..9ff4df0db0c3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2096,8 +2096,9 @@ void pci_bus_release_busn_res(struct pci_bus *b) res, ret ? "can not be" : "is"); } -struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, struct list_head *resources) +struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, + struct pci_ops *ops, void *sysdata, + struct list_head *resources, struct msi_controller *msi) { struct resource_entry *window; bool found = false; @@ -2114,6 +2115,8 @@ struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, if (!b) return NULL; + b->msi = msi; + if (!found) { dev_info(&b->dev, "No busn resource found for root bus, will use [bus %02x-ff]\n", @@ -2128,6 +2131,13 @@ struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, return b; } + +struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, + struct pci_ops *ops, void *sysdata, struct list_head *resources) +{ + return pci_scan_root_bus_msi(parent, bus, ops, sysdata, resources, + NULL); +} EXPORT_SYMBOL(pci_scan_root_bus); struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, -- cgit v1.2.1 From 8953aab1e80fd299d6185a57edaff733fa5c6a55 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Wed, 29 Jul 2015 12:33:18 +0100 Subject: ARM/PCI, designware, xilinx: Use pci_scan_root_bus_msi() ARM previously stored the msi_controller pointer in its sysdata, struct pci_sys_data, and implemented pcibios_msi_controller() to retrieve it. That made PCI host controller drivers specific to ARM because they had to put the msi_controller pointer in the ARM-specific pci_sys_data. There is now a generic mechanism, pci_scan_root_bus_msi(), for giving the msi_controller pointer to the PCI core. Use this for all ARM systems and for the DesignWare and Xilinx PCI host controller drivers. This removes an ARM dependency from the DesignWare, DRA7xx, EXYNOS, i.MX6, Keystone, Layerscape, SPEAr13xx, and Xilinx drivers. [bhelgaas: changelog, split into separate patch] Suggested-by: Russell King Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han CC: Pratyush Anand CC: Arnd Bergmann CC: Simon Horman CC: Russell King CC: Thomas Petazzoni CC: Thierry Reding CC: Michal Simek CC: Marc Zyngier --- drivers/pci/host/pcie-designware.c | 12 +++++++++--- drivers/pci/host/pcie-xilinx.c | 11 ++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 69486be7181e..fe11cc175f77 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -526,7 +526,6 @@ int dw_pcie_host_init(struct pcie_port *pp) #ifdef CONFIG_PCI_MSI dw_pcie_msi_chip.dev = pp->dev; - dw_pci.msi_ctrl = &dw_pcie_msi_chip; #endif dw_pci.nr_controllers = 1; @@ -708,8 +707,15 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) struct pcie_port *pp = sys_to_pcie(sys); pp->root_bus_nr = sys->busnr; - bus = pci_scan_root_bus(pp->dev, sys->busnr, - &dw_pcie_ops, sys, &sys->resources); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + bus = pci_scan_root_bus_msi(pp->dev, sys->busnr, &dw_pcie_ops, + sys, &sys->resources, + &dw_pcie_msi_chip); + else + bus = pci_scan_root_bus(pp->dev, sys->busnr, &dw_pcie_ops, + sys, &sys->resources); + if (!bus) return NULL; diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index f1a06a091ccb..38d3114b97d3 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -647,9 +647,15 @@ static struct pci_bus *xilinx_pcie_scan_bus(int nr, struct pci_sys_data *sys) struct pci_bus *bus; port->root_busno = sys->busnr; - bus = pci_scan_root_bus(port->dev, sys->busnr, &xilinx_pcie_ops, - sys, &sys->resources); + if (IS_ENABLED(CONFIG_PCI_MSI)) + bus = pci_scan_root_bus_msi(port->dev, sys->busnr, + &xilinx_pcie_ops, sys, + &sys->resources, + &xilinx_pcie_msi_chip); + else + bus = pci_scan_root_bus(port->dev, sys->busnr, + &xilinx_pcie_ops, sys, &sys->resources); return bus; } @@ -847,7 +853,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) #ifdef CONFIG_PCI_MSI xilinx_pcie_msi_chip.dev = port->dev; - hw.msi_ctrl = &xilinx_pcie_msi_chip; #endif pci_common_init_dev(dev, &hw); -- cgit v1.2.1 From 2291ec09025f85c11fbd0e9877c4306a8aa6e7e9 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Mon, 3 Aug 2015 22:04:06 -0500 Subject: PCI/MSI: Remove unused pcibios_msi_controller() hook The pcibios_msi_controller() hook was only implemented by ARM, and it sets pci_bus->msi now, so it doesn't need this hook anymore. Remove the unused pcibios_msi_controller() hook. [bhelgaas: changelog, split into separate patch] Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index f66be868ad21..0d20142c1ba0 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -77,24 +77,9 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) /* Arch hooks */ -struct msi_controller * __weak pcibios_msi_controller(struct pci_dev *dev) -{ - return NULL; -} - -static struct msi_controller *pci_msi_controller(struct pci_dev *dev) -{ - struct msi_controller *msi_ctrl = dev->bus->msi; - - if (msi_ctrl) - return msi_ctrl; - - return pcibios_msi_controller(dev); -} - int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) { - struct msi_controller *chip = pci_msi_controller(dev); + struct msi_controller *chip = dev->bus->msi; int err; if (!chip || !chip->setup_irq) -- cgit v1.2.1 From 3a10766d0624c72b978790dba864cf7d6160f89f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 4 Aug 2015 14:54:04 -0500 Subject: PCI: Drop references acquired by of_parse_phandle() of_parse_phandle() returns a device_node pointer with the refcount incremented. We should dispose of this reference when we're finished. Drop the reference acquired by of_parse_phandle(). Signed-off-by: Bjorn Helgaas Reviewed-by: Lorenzo Pieralisi --- drivers/pci/host/pci-mvebu.c | 1 + drivers/pci/host/pci-xgene.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 70aa09556ec5..67ec5e1c99db 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -879,6 +879,7 @@ static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie) return; pcie->msi = of_pci_find_msi_chip_by_node(msi_node); + of_node_put(msi_node); if (pcie->msi) pcie->msi->dev = &pcie->pdev->dev; diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index a9dfb70d623a..4c2fb1f78d62 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -514,6 +514,7 @@ static int xgene_pcie_msi_enable(struct pci_bus *bus) if (!bus->msi) return -ENODEV; + of_node_put(msi_node); bus->msi->dev = &bus->dev; return 0; } -- cgit v1.2.1 From 9dae3a97297f71e884ed8e7664955bcacb86f010 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 20 Aug 2015 16:08:27 -0500 Subject: PCI: Move MPS configuration check to pci_configure_device() Previously we checked for invalid MPS settings, i.e., a device with MPS different than its upstream bridge, in pcie_bus_detect_mps(). We only did this if the arch or hotplug driver called pcie_bus_configure_settings(), and then only if PCIe bus tuning was disabled (PCIE_BUS_TUNE_OFF). Move the MPS checking code to pci_configure_device(), so we do it in the pci_device_add() path for every device. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9ff4df0db0c3..eb32395d5897 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1275,6 +1275,27 @@ int pci_setup_device(struct pci_dev *dev) return 0; } +static void pci_configure_mps(struct pci_dev *dev) +{ + struct pci_dev *bridge = pci_upstream_bridge(dev); + int mps, p_mps; + + if (!pci_is_pcie(dev) || !bridge || !pci_is_pcie(bridge)) + return; + + mps = pcie_get_mps(dev); + p_mps = pcie_get_mps(bridge); + + if (mps == p_mps) + return; + + if (pcie_bus_config == PCIE_BUS_TUNE_OFF) { + dev_warn(&dev->dev, "Max Payload Size %d, but upstream %s set to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n", + mps, pci_name(bridge), p_mps); + return; + } +} + static struct hpp_type0 pci_default_type0 = { .revision = 1, .cache_line_size = 8, @@ -1396,6 +1417,8 @@ static void pci_configure_device(struct pci_dev *dev) struct hotplug_params hpp; int ret; + pci_configure_mps(dev); + memset(&hpp, 0, sizeof(hpp)); ret = pci_get_hp_params(dev, &hpp); if (ret) @@ -1791,22 +1814,6 @@ static void pcie_write_mrrs(struct pci_dev *dev) dev_err(&dev->dev, "MRRS was unable to be configured with a safe value. If problems are experienced, try running with pci=pcie_bus_safe\n"); } -static void pcie_bus_detect_mps(struct pci_dev *dev) -{ - struct pci_dev *bridge = dev->bus->self; - int mps, p_mps; - - if (!bridge) - return; - - mps = pcie_get_mps(dev); - p_mps = pcie_get_mps(bridge); - - if (mps != p_mps) - dev_warn(&dev->dev, "Max Payload Size %d, but upstream %s set to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n", - mps, pci_name(bridge), p_mps); -} - static int pcie_bus_configure_set(struct pci_dev *dev, void *data) { int mps, orig_mps; @@ -1814,10 +1821,8 @@ static int pcie_bus_configure_set(struct pci_dev *dev, void *data) if (!pci_is_pcie(dev)) return 0; - if (pcie_bus_config == PCIE_BUS_TUNE_OFF) { - pcie_bus_detect_mps(dev); + if (pcie_bus_config == PCIE_BUS_TUNE_OFF) return 0; - } mps = 128 << *(u8 *)data; orig_mps = pcie_get_mps(dev); -- cgit v1.2.1 From 27d868b5e6cfaee4fec66b388e4085ff94050fa7 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Mon, 24 Aug 2015 08:48:16 -0500 Subject: PCI: Set MPS to match upstream bridge Firmware typically configures the PCIe fabric with a consistent Max Payload Size setting based on the devices present at boot. A hot-added device typically has the power-on default MPS setting (128 bytes), which may not match the fabric. The previous Linux default, in the absence of any "pci=pcie_bus_*" options, was PCIE_BUS_TUNE_OFF, in which we never touch MPS, even for hot-added devices. Add a new default setting, PCIE_BUS_DEFAULT, in which we make sure every device's MPS setting matches the upstream bridge. This makes it more likely that a hot-added device will work in a system with optimized MPS configuration. Note that if we hot-add a device that only supports 128-byte MPS, it still likely won't work because we don't reconfigure the rest of the fabric. Booting with "pci=pcie_bus_peer2peer" is a workaround for this because it sets MPS to 128 for everything. [bhelgaas: changelog, new default, rework for pci_configure_device() path] Tested-by: Keith Busch Tested-by: Jordan Hargrave Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Acked-by: Yinghai Lu --- drivers/pci/pci.c | 2 +- drivers/pci/probe.c | 22 ++++++++++++++++++++-- drivers/pci/quirks.c | 3 ++- 3 files changed, 23 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0008c950452c..b96b4ccc2819 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -81,7 +81,7 @@ unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; unsigned long pci_hotplug_io_size = DEFAULT_HOTPLUG_IO_SIZE; unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE; -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_TUNE_OFF; +enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_DEFAULT; /* * The default CLS is used if arch didn't set CLS explicitly and not diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index eb32395d5897..eebabe3a814e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1278,7 +1278,7 @@ int pci_setup_device(struct pci_dev *dev) static void pci_configure_mps(struct pci_dev *dev) { struct pci_dev *bridge = pci_upstream_bridge(dev); - int mps, p_mps; + int mps, p_mps, rc; if (!pci_is_pcie(dev) || !bridge || !pci_is_pcie(bridge)) return; @@ -1294,6 +1294,23 @@ static void pci_configure_mps(struct pci_dev *dev) mps, pci_name(bridge), p_mps); return; } + + /* + * Fancier MPS configuration is done later by + * pcie_bus_configure_settings() + */ + if (pcie_bus_config != PCIE_BUS_DEFAULT) + return; + + rc = pcie_set_mps(dev, p_mps); + if (rc) { + dev_warn(&dev->dev, "can't set Max Payload Size to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n", + p_mps); + return; + } + + dev_info(&dev->dev, "Max Payload Size set to %d (was %d, max %d)\n", + p_mps, mps, 128 << dev->pcie_mpss); } static struct hpp_type0 pci_default_type0 = { @@ -1821,7 +1838,8 @@ static int pcie_bus_configure_set(struct pci_dev *dev, void *data) if (!pci_is_pcie(dev)) return 0; - if (pcie_bus_config == PCIE_BUS_TUNE_OFF) + if (pcie_bus_config == PCIE_BUS_TUNE_OFF || + pcie_bus_config == PCIE_BUS_DEFAULT) return 0; mps = 128 << *(u8 *)data; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e9fd0e90fa3b..55f2c208bd0e 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2862,7 +2862,8 @@ static void quirk_intel_mc_errata(struct pci_dev *dev) int err; u16 rcc; - if (pcie_bus_config == PCIE_BUS_TUNE_OFF) + if (pcie_bus_config == PCIE_BUS_TUNE_OFF || + pcie_bus_config == PCIE_BUS_DEFAULT) return; /* Intel errata specifies bits to change but does not say what they are. -- cgit v1.2.1 From 91f15fb30c77d4a0d0d9b97e5cec647650853145 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 24 Aug 2015 15:27:11 -0500 Subject: PCI: Disable async suspend/resume for JMicron multi-function SATA/AHCI On multi-function JMicron SATA/PATA/AHCI devices, the PATA controller at function 1 doesn't work if it is powered on before the SATA controller at function 0. The result is that PATA doesn't work after resume, and we print messages like this: pata_jmicron 0000:02:00.1: Refused to change power state, currently in D3 irq 17: nobody cared (try booting with the "irqpoll" option) Async resume was introduced in v3.15 by 76569faa62c4 ("PM / sleep: Asynchronous threads for resume_noirq"). Prior to that, we powered on the functions in order, so this problem shouldn't happen. e6b7e41cdd8c ("ata: Disabling the async PM for JMicron chip 363/361") solved the problem for JMicron 361 and 363 devices. With async suspend disabled, we always power on function 0 before function 1. Barto then reported the same problem with a JMicron 368 (see comment #57 in the bugzilla). Rather than extending the blacklist piecemeal, disable async suspend for all JMicron multi-function SATA/PATA/AHCI devices. This quirk could stay in the ahci and pata_jmicron drivers, but it's likely the problem will occur even if pata_jmicron isn't loaded until after the suspend/resume. Making it a PCI quirk ensures that we'll preserve the power-on order even if the drivers aren't loaded. [bhelgaas: changelog, limit to multi-function, limit to IDE/ATA] Link: https://bugzilla.kernel.org/show_bug.cgi?id=81551 Reported-and-tested-by: Barto Signed-off-by: Zhang Rui Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org # v3.15+ --- drivers/ata/ahci.c | 13 +------------ drivers/ata/pata_jmicron.c | 12 ------------ drivers/pci/quirks.c | 12 ++++++++++++ 3 files changed, 13 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 7e62751abfac..a46660204e3a 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -351,6 +351,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* JMicron 362B and 362C have an AHCI function with IDE class code */ { PCI_VDEVICE(JMICRON, 0x2362), board_ahci_ign_iferr }, { PCI_VDEVICE(JMICRON, 0x236f), board_ahci_ign_iferr }, + /* May need to update quirk_jmicron_async_suspend() for additions */ /* ATI */ { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ @@ -1451,18 +1452,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) else if (pdev->vendor == 0x177d && pdev->device == 0xa01c) ahci_pci_bar = AHCI_PCI_BAR_CAVIUM; - /* - * The JMicron chip 361/363 contains one SATA controller and one - * PATA controller,for powering on these both controllers, we must - * follow the sequence one by one, otherwise one of them can not be - * powered on successfully, so here we disable the async suspend - * method for these chips. - */ - if (pdev->vendor == PCI_VENDOR_ID_JMICRON && - (pdev->device == PCI_DEVICE_ID_JMICRON_JMB363 || - pdev->device == PCI_DEVICE_ID_JMICRON_JMB361)) - device_disable_async_suspend(&pdev->dev); - /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c index 47e418b8c8ba..4d1a5d2c4287 100644 --- a/drivers/ata/pata_jmicron.c +++ b/drivers/ata/pata_jmicron.c @@ -143,18 +143,6 @@ static int jmicron_init_one (struct pci_dev *pdev, const struct pci_device_id *i }; const struct ata_port_info *ppi[] = { &info, NULL }; - /* - * The JMicron chip 361/363 contains one SATA controller and one - * PATA controller,for powering on these both controllers, we must - * follow the sequence one by one, otherwise one of them can not be - * powered on successfully, so here we disable the async suspend - * method for these chips. - */ - if (pdev->vendor == PCI_VENDOR_ID_JMICRON && - (pdev->device == PCI_DEVICE_ID_JMICRON_JMB363 || - pdev->device == PCI_DEVICE_ID_JMICRON_JMB361)) - device_disable_async_suspend(&pdev->dev); - return ata_pci_bmdma_init_one(pdev, ppi, &jmicron_sht, NULL, 0); } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b6af4b0192b8..cbf5b3d617cd 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1570,6 +1570,18 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB3 #endif +static void quirk_jmicron_async_suspend(struct pci_dev *dev) +{ + if (dev->multifunction) { + device_disable_async_suspend(&dev->dev); + dev_info(&dev->dev, "async suspend disabled to avoid multi-function power-on ordering issue\n"); + } +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE, 8, quirk_jmicron_async_suspend); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0, quirk_jmicron_async_suspend); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_JMICRON, 0x2362, quirk_jmicron_async_suspend); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_JMICRON, 0x236f, quirk_jmicron_async_suspend); + #ifdef CONFIG_X86_IO_APIC static void quirk_alder_ioapic(struct pci_dev *pdev) { -- cgit v1.2.1