From 045cc22e53a1a0caaa9a0b6b64246d2690049906 Mon Sep 17 00:00:00 2001 From: "ethan.zhao" Date: Wed, 6 Nov 2013 22:49:13 +0800 Subject: PCI: Clear NumVFs when disabling SR-IOV in sriov_init() When SR-IOV is disabled (VF Enable is cleared), NumVFs is not very useful, so this patch clears it out to prevent confusing lspci output like that below. We already clear NumVFs in sriov_disable(), and this does the same when we disable SR-IOV as part of parsing the SR-IOV capability. $ lspci -vvv -s 13:00.0 13:00.0 Ethernet controller: Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01) Capabilities: [160 v1] Single Root I/O Virtualization (SR-IOV) IOVCtl: Enable- Migration- Interrupt- MSE- ARIHierarchy+ Initial VFs: 64, Total VFs: 64, Number of VFs: 64, ... [bhelgaas: changelog] Signed-off-by: ethan.zhao Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 1fe2d6fb19d5..68311ec849ee 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -441,6 +441,7 @@ static int sriov_init(struct pci_dev *dev, int pos) found: pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); + pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0); pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); if (!offset || (total > 1 && !stride)) -- cgit v1.2.3-70-g09d2 From 2850b05c9644d0f4c9df6cc77d628d7e0598a0cc Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 26 Nov 2013 11:02:52 -0700 Subject: PCI: mvebu: Drop writes to bridge Secondary Status register There are no writable bits in the secondary status register, only RO and RW1C (write-1-to-clear) bits. The driver never sets any of the RW1C bits, so the status register should always be 0, just remove the set from the write path. Someday the RW1C bits should be copied/cleared directly from registers in the HW. [bhelgaas: changelog tweaks] Signed-off-by: Jason Gunthorpe Signed-off-by: Bjorn Helgaas Acked-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index c269e430c760..6f5a20f3cdf6 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -500,7 +500,6 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port, */ bridge->iobase = (value & 0xff) | PCI_IO_RANGE_TYPE_32; bridge->iolimit = ((value >> 8) & 0xff) | PCI_IO_RANGE_TYPE_32; - bridge->secondary_status = value >> 16; mvebu_pcie_handle_iobase_change(port); break; -- cgit v1.2.3-70-g09d2 From 43a16f94445310800e39d54aaa534f2ce7dbe0a2 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 26 Nov 2013 11:02:54 -0700 Subject: PCI: mvebu: Obey bridge PCI_COMMAND_MEM and PCI_COMMAND_IO bits When PCI_COMMAND_MEMORY/PCI_COMMAND_IO are cleared, the bridge should not allocate windows or even look at the window limit/base registers. Otherwise we may set up bogus windows while the PCI core code performs discovery. The core will leave PCI_COMMAND_IO cleared if it doesn't need an IO window. Have mvebu_pcie_handle_*_change respect the bits, and call the change function whenever the bits changes. Tested-by: Thomas Petazzoni Signed-off-by: Jason Gunthorpe Signed-off-by: Bjorn Helgaas Acked-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 6f5a20f3cdf6..94294123e7bd 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -300,7 +300,8 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) /* Are the new iobase/iolimit values invalid? */ if (port->bridge.iolimit < port->bridge.iobase || - port->bridge.iolimitupper < port->bridge.iobaseupper) { + port->bridge.iolimitupper < port->bridge.iobaseupper || + !(port->bridge.command & PCI_COMMAND_IO)) { /* If a window was configured, remove it */ if (port->iowin_base) { @@ -337,7 +338,8 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) { /* Are the new membase/memlimit values invalid? */ - if (port->bridge.memlimit < port->bridge.membase) { + if (port->bridge.memlimit < port->bridge.membase || + !(port->bridge.command & PCI_COMMAND_MEMORY)) { /* If a window was configured, remove it */ if (port->memwin_base) { @@ -485,8 +487,16 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port, switch (where & ~3) { case PCI_COMMAND: + { + u32 old = bridge->command; + bridge->command = value & 0xffff; + if ((old ^ bridge->command) & PCI_COMMAND_IO) + mvebu_pcie_handle_iobase_change(port); + if ((old ^ bridge->command) & PCI_COMMAND_MEMORY) + mvebu_pcie_handle_membase_change(port); break; + } case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1: bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4] = value; -- cgit v1.2.3-70-g09d2 From 641e674d6c0f974162c4c8cc0081c7ffc50b3911 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 26 Nov 2013 11:02:55 -0700 Subject: PCI: mvebu: Support a bridge with no IO port window Make pcie-io-aperture and the IO port MBUS ID in ranges optional. If not provided the bridge reports to Linux that IO space mapping is not supported and refuses to configure an IO MBUS window. This allows both complete disable (do not specify pcie-io-aperture) and per-port disable (do not specify a IO target ranges entry for the port). Most PCIe devices these days do not require IO support to function, so having an option to disable it in the driver is useful. Tested-by: Thomas Petazzoni Signed-off-by: Jason Gunthorpe Signed-off-by: Bjorn Helgaas Acked-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 63 ++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 94294123e7bd..abd560e46757 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -150,6 +150,11 @@ static inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg) return readl(port->base + reg); } +static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) +{ + return port->io_target != -1 && port->io_attr != -1; +} + static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) { return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); @@ -314,6 +319,12 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) return; } + if (!mvebu_has_ioport(port)) { + dev_WARN(&port->pcie->pdev->dev, + "Attempt to set IO when IO is disabled\n"); + return; + } + /* * We read the PCI-to-PCI bridge emulated registers, and * calculate the base address and size of the address decoding @@ -428,9 +439,12 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port, break; case PCI_IO_BASE: - *value = (bridge->secondary_status << 16 | - bridge->iolimit << 8 | - bridge->iobase); + if (!mvebu_has_ioport(port)) + *value = bridge->secondary_status << 16; + else + *value = (bridge->secondary_status << 16 | + bridge->iolimit << 8 | + bridge->iobase); break; case PCI_MEMORY_BASE: @@ -490,6 +504,9 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port, { u32 old = bridge->command; + if (!mvebu_has_ioport(port)) + value &= ~PCI_COMMAND_IO; + bridge->command = value & 0xffff; if ((old ^ bridge->command) & PCI_COMMAND_IO) mvebu_pcie_handle_iobase_change(port); @@ -660,7 +677,9 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) struct mvebu_pcie *pcie = sys_to_pcie(sys); int i; - pci_add_resource_offset(&sys->resources, &pcie->realio, sys->io_offset); + if (resource_size(&pcie->realio) != 0) + pci_add_resource_offset(&sys->resources, &pcie->realio, + sys->io_offset); pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); @@ -761,12 +780,17 @@ static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev, #define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF) static int mvebu_get_tgt_attr(struct device_node *np, int devfn, - unsigned long type, int *tgt, int *attr) + unsigned long type, + unsigned int *tgt, + unsigned int *attr) { const int na = 3, ns = 2; const __be32 *range; int rlen, nranges, rangesz, pna, i; + *tgt = -1; + *attr = -1; + range = of_get_property(np, "ranges", &rlen); if (!range) return -EINVAL; @@ -836,16 +860,15 @@ static int mvebu_pcie_probe(struct platform_device *pdev) } mvebu_mbus_get_pcie_io_aperture(&pcie->io); - if (resource_size(&pcie->io) == 0) { - dev_err(&pdev->dev, "invalid I/O aperture size\n"); - return -EINVAL; - } - pcie->realio.flags = pcie->io.flags; - pcie->realio.start = PCIBIOS_MIN_IO; - pcie->realio.end = min_t(resource_size_t, - IO_SPACE_LIMIT, - resource_size(&pcie->io)); + if (resource_size(&pcie->io) != 0) { + pcie->realio.flags = pcie->io.flags; + pcie->realio.start = PCIBIOS_MIN_IO; + pcie->realio.end = min_t(resource_size_t, + IO_SPACE_LIMIT, + resource_size(&pcie->io)); + } else + pcie->realio = pcie->io; /* Get the bus range */ ret = of_pci_parse_bus_range(np, &pcie->busn); @@ -904,12 +927,12 @@ static int mvebu_pcie_probe(struct platform_device *pdev) continue; } - ret = mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO, - &port->io_target, &port->io_attr); - if (ret < 0) { - dev_err(&pdev->dev, "PCIe%d.%d: cannot get tgt/attr for io window\n", - port->port, port->lane); - continue; + if (resource_size(&pcie->io) != 0) + mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO, + &port->io_target, &port->io_attr); + else { + port->io_target = -1; + port->io_attr = -1; } port->reset_gpio = of_get_named_gpio_flags(child, -- cgit v1.2.3-70-g09d2 From 17bd86c69cea4a8364e2ef8a49947dd9d5e26f13 Mon Sep 17 00:00:00 2001 From: Eric Brower Date: Mon, 18 Nov 2013 14:55:06 -0800 Subject: PCI: Disable Gen2 for Tegra20 and Tegra30 Tegra20 and Tegra30 do not support gen2 PCIe, so correct the register setting to disable it. Signed-off-by: Eric Brower Signed-off-by: Bjorn Helgaas Acked-by: Thierry Reding --- drivers/pci/host/pci-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 0afbbbc55c81..b8ba2f794559 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -805,7 +805,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_PCIE_CONFIG); value = afi_readl(pcie, AFI_FUSE); - value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + value |= AFI_FUSE_PCIE_T0_GEN2_DIS; afi_writel(pcie, value, AFI_FUSE); /* initialize internal PHY, enable up to 16 PCIE lanes */ -- cgit v1.2.3-70-g09d2 From c176d1c71bd18238e5d77d6acc6b991aa6a17f3b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 19 Nov 2013 11:40:28 +0800 Subject: PCI: rcar: Fix rcar_pci_probe() return value check In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Signed-off-by: Bjorn Helgaas Reviewed-by: Jingoo Han --- drivers/pci/host/pci-rcar-gen2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index cbaa5c4397e3..96d118242ae2 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -276,8 +276,8 @@ static int __init rcar_pci_probe(struct platform_device *pdev) cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, cfg_res); - if (!reg) - return -ENODEV; + if (IS_ERR(reg)) + return PTR_ERR(reg); mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!mem_res || !mem_res->start) -- cgit v1.2.3-70-g09d2 From ebb034a4ce9fc37877b3df2b85b9c33d43349586 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 2 Dec 2013 01:39:34 -0200 Subject: PCI: imx6: Remove unneeded check of platform_get_resource() When using devm_ioremap_resource(), we do not need to check the return value of platform_get_resource(), so just remove it. Signed-off-by: Fabio Estevam Signed-off-by: Bjorn Helgaas Acked-by: Marek Vasut --- drivers/pci/host/pci-imx6.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index bd70af8f31ac..5002e2381588 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -426,11 +426,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) "imprecise external abort"); dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!dbi_base) { - dev_err(&pdev->dev, "dbi_base memory resource not found\n"); - return -ENODEV; - } - pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base); if (IS_ERR(pp->dbi_base)) { ret = PTR_ERR(pp->dbi_base); -- cgit v1.2.3-70-g09d2 From be3f48cb21c1ca4907a0822eea406c8dd4a73ddb Mon Sep 17 00:00:00 2001 From: Bjørn Erik Nilsen Date: Fri, 29 Nov 2013 14:35:24 +0100 Subject: PCI: designware: Fix crash in dw_msi_teardown_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 904d0e788993 ("PCI: designware: Add irq_create_mapping()") resulted in pre-allocated irq descs. Problem was that in assign_irq() these descs were explicitly allocated and hence also freed, resulting in a crash. We also need to clear the entire irq range in teardown. With this commit the teardown basically does exactly the opposite of what was done in setup. The crash this fixes looks like: Unable to handle kernel NULL pointer dereference at virtual address 00000020 PC is at dw_msi_teardown_irq+0x40/0x118 LR is at trace_hardirqs_on_caller+0xf4/0x1c0 Backtrace: [<802c401c>] (dw_msi_teardown_irq+0x0/0x118) from [<802c1844>] (arch_teardown_msi_irq+0x3c/0x40) [<802c1808>] (arch_teardown_msi_irq+0x0/0x40) from [<802c1a08>] (default_teardown_msi_irqs+0x68/0x84) [<802c19a0>] (default_teardown_msi_irqs+0x0/0x84) from [<802c1a34>] (arch_teardown_msi_irqs+0x10/0x14) [<802c1a24>] (arch_teardown_msi_irqs+0x0/0x14) from [<802c1ad0>] (free_msi_irqs+0x98/0x144) [<802c1a38>] (free_msi_irqs+0x0/0x144) from [<802c2570>] (pci_disable_msi+0x48/0x60) [<802c2528>] (pci_disable_msi+0x0/0x60) from [<7f0057d4>] (sxdma_irq_free+0x44/0x48 [sxdma]) [bhelgaas: add crash info] Tested-by: Mohit Kumar Signed-off-by: Bjørn Erik Nilsen Signed-off-by: Bjorn Helgaas Acked-by: Marek Vasut Acked-by: Jingoo Han --- drivers/pci/host/pcie-designware.c | 49 ++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index e33b68be0391..61345a18a6d1 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -209,6 +209,25 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) return 0; } +static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, + unsigned int nvec, unsigned int pos) +{ + unsigned int i, res, bit, val; + + i = 0; + while (i < nvec) { + irq_set_msi_desc_off(irq_base, i, NULL); + clear_bit(pos + i, pp->msi_irq_in_use); + /* Disable corresponding interrupt on MSI interrupt controller */ + res = ((pos + i) / 32) * 12; + bit = (pos + i) % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + ++i; + } +} + static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { int res, bit, irq, pos0, pos1, i; @@ -242,11 +261,20 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) if (!irq) goto no_valid_irq; + /* + * irq_create_mapping (called from dw_pcie_host_init) pre-allocates + * descs so there is no need to allocate descs here. We can therefore + * assume that if irq_find_mapping above returns non-zero, then the + * descs are also successfully allocated. + */ + i = 0; while (i < no_irqs) { + if (irq_set_msi_desc_off(irq, i, desc) != 0) { + clear_irq_range(pp, irq, i, pos0); + goto no_valid_irq; + } set_bit(pos0 + i, pp->msi_irq_in_use); - irq_alloc_descs((irq + i), (irq + i), 1, 0); - irq_set_msi_desc(irq + i, desc); /*Enable corresponding interrupt in MSI interrupt controller */ res = ((pos0 + i) / 32) * 12; bit = (pos0 + i) % 32; @@ -266,7 +294,7 @@ no_valid_irq: static void clear_irq(unsigned int irq) { - int res, bit, val, pos; + unsigned int pos, nvec; struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; @@ -281,18 +309,15 @@ static void clear_irq(unsigned int irq) return; } + /* undo what was done in assign_irq */ pos = data->hwirq; + nvec = 1 << msi->msi_attrib.multiple; - irq_free_desc(irq); - - clear_bit(pos, pp->msi_irq_in_use); + clear_irq_range(pp, irq, nvec, pos); - /* Disable corresponding interrupt on MSI interrupt controller */ - res = (pos / 32) * 12; - bit = pos % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + /* all irqs cleared; reset attributes */ + msi->irq = 0; + msi->msi_attrib.multiple = 0; } static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, -- cgit v1.2.3-70-g09d2 From 64989e7399f09b72689e25fb40f2d0d5e073b13a Mon Sep 17 00:00:00 2001 From: Bjørn Erik Nilsen Date: Fri, 29 Nov 2013 14:35:25 +0100 Subject: PCI: designware: Remove redundant call to pci_write_config_word() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit write_msi_msg() does exactly the same so there is no need to explicitly call pci_write_config_word() and do the same twice. Tested-by: Mohit Kumar Signed-off-by: Bjørn Erik Nilsen Signed-off-by: Bjorn Helgaas Acked-by: Marek Vasut Acked-by: Jingoo Han --- drivers/pci/host/pcie-designware.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 61345a18a6d1..5274085ecd4b 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -345,10 +345,10 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, if (irq < 0) return irq; - msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; - msg_ctr |= msgvec << 4; - pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, - msg_ctr); + /* + * write_msi_msg() will update PCI_MSI_FLAGS so there is + * no need to explicitly call pci_write_config_word(). + */ desc->msi_attrib.multiple = msgvec; msg.address_lo = virt_to_phys((void *)pp->msi_data); -- cgit v1.2.3-70-g09d2 From 0b8cfb6aa3aabc96177b1e68ef13d2eb5c686606 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 9 Dec 2013 15:11:25 -0700 Subject: PCI: designware: Use typical "for" loop idiom It's conventional to use "for" rather than "while" for simple iteration. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-designware.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 5274085ecd4b..1c92833a4ed3 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -214,8 +214,7 @@ static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, { unsigned int i, res, bit, val; - i = 0; - while (i < nvec) { + for (i = 0; i < nvec; i++) { irq_set_msi_desc_off(irq_base, i, NULL); clear_bit(pos + i, pp->msi_irq_in_use); /* Disable corresponding interrupt on MSI interrupt controller */ @@ -224,7 +223,6 @@ static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val &= ~(1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); - ++i; } } @@ -268,8 +266,7 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) * descs are also successfully allocated. */ - i = 0; - while (i < no_irqs) { + for (i = 0; i < no_irqs; i++) { if (irq_set_msi_desc_off(irq, i, desc) != 0) { clear_irq_range(pp, irq, i, pos0); goto no_valid_irq; @@ -281,7 +278,6 @@ static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val |= 1 << bit; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); - i++; } *pos = pos0; -- cgit v1.2.3-70-g09d2 From b391bf31584d879d7455e882a5e200388c8be9ac Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 2 Dec 2013 01:39:35 -0200 Subject: PCI: imx6: Remove unneeded 'goto err' There is no need to use 'goto err' as we can directly return the errors. No functional change. Signed-off-by: Fabio Estevam Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pci-imx6.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 5002e2381588..9fc1cb66c64e 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -427,10 +427,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base); - if (IS_ERR(pp->dbi_base)) { - ret = PTR_ERR(pp->dbi_base); - goto err; - } + if (IS_ERR(pp->dbi_base)) + return PTR_ERR(pp->dbi_base); /* Fetch GPIOs */ imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); @@ -444,7 +442,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) "PCIe reset"); if (ret) { dev_err(&pdev->dev, "unable to get reset gpio\n"); - goto err; + return ret; } imx6_pcie->power_on_gpio = of_get_named_gpio(np, "power-on-gpio", 0); @@ -455,7 +453,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) "PCIe power enable"); if (ret) { dev_err(&pdev->dev, "unable to get power-on gpio\n"); - goto err; + return ret; } } @@ -467,7 +465,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) "PCIe wake up"); if (ret) { dev_err(&pdev->dev, "unable to get wake-up gpio\n"); - goto err; + return ret; } } @@ -479,7 +477,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) "PCIe disable endpoint"); if (ret) { dev_err(&pdev->dev, "unable to get disable-ep gpio\n"); - goto err; + return ret; } } @@ -488,32 +486,28 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) if (IS_ERR(imx6_pcie->lvds_gate)) { dev_err(&pdev->dev, "lvds_gate clock select missing or invalid\n"); - ret = PTR_ERR(imx6_pcie->lvds_gate); - goto err; + return PTR_ERR(imx6_pcie->lvds_gate); } imx6_pcie->sata_ref_100m = devm_clk_get(&pdev->dev, "sata_ref_100m"); if (IS_ERR(imx6_pcie->sata_ref_100m)) { dev_err(&pdev->dev, "sata_ref_100m clock source missing or invalid\n"); - ret = PTR_ERR(imx6_pcie->sata_ref_100m); - goto err; + return PTR_ERR(imx6_pcie->sata_ref_100m); } imx6_pcie->pcie_ref_125m = devm_clk_get(&pdev->dev, "pcie_ref_125m"); if (IS_ERR(imx6_pcie->pcie_ref_125m)) { dev_err(&pdev->dev, "pcie_ref_125m clock source missing or invalid\n"); - ret = PTR_ERR(imx6_pcie->pcie_ref_125m); - goto err; + return PTR_ERR(imx6_pcie->pcie_ref_125m); } imx6_pcie->pcie_axi = devm_clk_get(&pdev->dev, "pcie_axi"); if (IS_ERR(imx6_pcie->pcie_axi)) { dev_err(&pdev->dev, "pcie_axi clock source missing or invalid\n"); - ret = PTR_ERR(imx6_pcie->pcie_axi); - goto err; + return PTR_ERR(imx6_pcie->pcie_axi); } /* Grab GPR config register range */ @@ -521,19 +515,15 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); if (IS_ERR(imx6_pcie->iomuxc_gpr)) { dev_err(&pdev->dev, "unable to find iomuxc registers\n"); - ret = PTR_ERR(imx6_pcie->iomuxc_gpr); - goto err; + return PTR_ERR(imx6_pcie->iomuxc_gpr); } ret = imx6_add_pcie_port(pp, pdev); if (ret < 0) - goto err; + return ret; platform_set_drvdata(pdev, imx6_pcie); return 0; - -err: - return ret; } static const struct of_device_id imx6_pcie_of_match[] = { -- cgit v1.2.3-70-g09d2 From fb178d8b2fab3f2a9f203c13ffe80cfd6e01bdf1 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Wed, 4 Dec 2013 20:33:35 +0400 Subject: PCI: rcar: Add runtime PM support If runtime PM is enabled in the kernel config, the PCI clocks are not forced on at start-up, and thus, are never enabled. Use pm_runtime_get_sync() to enable the clocks. While at it, use dev_info() instead of pr_info() since now we have the device pointer available in the PCI setup callback. Signed-off-by: Valentine Barshak Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pci-rcar-gen2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 96d118242ae2..ceec147baec3 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* AHB-PCI Bridge PCI communication registers */ @@ -77,6 +78,7 @@ #define RCAR_PCI_NR_CONTROLLERS 3 struct rcar_pci_priv { + struct device *dev; void __iomem *reg; struct resource io_res; struct resource mem_res; @@ -169,8 +171,11 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) void __iomem *reg = priv->reg; u32 val; + pm_runtime_enable(priv->dev); + pm_runtime_get_sync(priv->dev); + val = ioread32(reg + RCAR_PCI_UNIT_REV_REG); - pr_info("PCI: bus%u revision %x\n", sys->busnr, val); + dev_info(priv->dev, "PCI: bus%u revision %x\n", sys->busnr, val); /* Disable Direct Power Down State and assert reset */ val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD; @@ -301,6 +306,7 @@ static int __init rcar_pci_probe(struct platform_device *pdev) priv->irq = platform_get_irq(pdev, 0); priv->reg = reg; + priv->dev = &pdev->dev; return rcar_pci_add_controller(priv); } -- cgit v1.2.3-70-g09d2 From 84f47190d6be1cb99cd4a680e1018080d93800a8 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 4 Dec 2013 18:30:41 +0100 Subject: PCI: mvebu: Remove duplicate of_clk_get_by_name() call Probably due to a merge conflict resolution gone bad, the PCI clock is got twice. Remove the redundant call of of_clk_get_by_name(). Signed-off-by: Andrew Lunn Signed-off-by: Bjorn Helgaas Reviewed-by: Jingoo Han Acked-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index abd560e46757..6f35df5fedce 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -981,14 +981,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) mvebu_pcie_set_local_dev_nr(port, 1); - port->clk = of_clk_get_by_name(child, NULL); - if (IS_ERR(port->clk)) { - dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n", - port->port, port->lane); - iounmap(port->base); - continue; - } - port->dn = child; spin_lock_init(&port->conf_lock); mvebu_sw_pci_bridge_init(port); -- cgit v1.2.3-70-g09d2 From 40c368c1ef6379db08d2ebd36f5a45efb93cff73 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 5 Dec 2013 19:52:53 +0800 Subject: PCI: Use dev_is_pci() to identify PCI devices Use dev_is_pci() instead of checking bus type directly. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 577074efbe62..e0431f1af33b 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -358,7 +358,7 @@ static void pci_acpi_cleanup(struct device *dev) static bool pci_acpi_bus_match(struct device *dev) { - return dev->bus == &pci_bus_type; + return dev_is_pci(dev); } static struct acpi_bus_type acpi_pci_bus = { -- cgit v1.2.3-70-g09d2 From 6f825b73e249a2b46f0349d1f86adfbb3ea703e0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 12 Nov 2013 20:07:17 +0100 Subject: PCI/portdrv: Remove superfluous name cast device_driver.name is "const char *" Signed-off-by: Geert Uytterhoeven Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 0b6e76604068..ce9d9ae17bfd 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -554,7 +554,7 @@ int pcie_port_service_register(struct pcie_port_service_driver *new) if (pcie_ports_disabled) return -ENODEV; - new->driver.name = (char *)new->name; + new->driver.name = new->name; new->driver.bus = &pcie_port_bus_type; new->driver.probe = pcie_port_probe_service; new->driver.remove = pcie_port_remove_service; -- cgit v1.2.3-70-g09d2 From ac8344c4c0bf74c7efaf962cf2a6404331678ce4 Mon Sep 17 00:00:00 2001 From: DuanZhenzhong Date: Wed, 4 Dec 2013 13:09:16 +0800 Subject: PCI: Drop "irq" param from *_restore_msi_irqs() Change x86_msi.restore_msi_irqs(struct pci_dev *dev, int irq) to x86_msi.restore_msi_irqs(struct pci_dev *dev). restore_msi_irqs() restores multiple MSI-X IRQs, so param 'int irq' is unneeded. This makes code more consistent between vm and bare metal. Dom0 MSI-X restore code can also be optimized as XEN only has a hypercall to restore all MSI-X vectors at one time. Tested-by: Sucheta Chakraborty Signed-off-by: Zhenzhong Duan Signed-off-by: Bjorn Helgaas Acked-by: Konrad Rzeszutek Wilk --- arch/x86/include/asm/pci.h | 2 +- arch/x86/include/asm/x86_init.h | 2 +- arch/x86/kernel/x86_init.c | 4 ++-- arch/x86/pci/xen.c | 2 +- drivers/pci/msi.c | 19 ++++++++++++++----- include/linux/msi.h | 4 ++-- 6 files changed, 21 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 947b5c417e83..0de52c5bf9a2 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -104,7 +104,7 @@ extern void pci_iommu_alloc(void); struct msi_desc; int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); void native_teardown_msi_irq(unsigned int irq); -void native_restore_msi_irqs(struct pci_dev *dev, int irq); +void native_restore_msi_irqs(struct pci_dev *dev); int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, unsigned int irq_base, unsigned int irq_offset); #else diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index 0f1be11e43d2..e45e4da96bf1 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -181,7 +181,7 @@ struct x86_msi_ops { u8 hpet_id); void (*teardown_msi_irq)(unsigned int irq); void (*teardown_msi_irqs)(struct pci_dev *dev); - void (*restore_msi_irqs)(struct pci_dev *dev, int irq); + void (*restore_msi_irqs)(struct pci_dev *dev); int (*setup_hpet_msi)(unsigned int irq, unsigned int id); u32 (*msi_mask_irq)(struct msi_desc *desc, u32 mask, u32 flag); u32 (*msix_mask_irq)(struct msi_desc *desc, u32 flag); diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index 021783b1f46a..e48b674639cc 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -136,9 +136,9 @@ void arch_teardown_msi_irq(unsigned int irq) x86_msi.teardown_msi_irq(irq); } -void arch_restore_msi_irqs(struct pci_dev *dev, int irq) +void arch_restore_msi_irqs(struct pci_dev *dev) { - x86_msi.restore_msi_irqs(dev, irq); + x86_msi.restore_msi_irqs(dev); } u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 5eee4959785d..103e702ec5a7 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -337,7 +337,7 @@ out: return ret; } -static void xen_initdom_restore_msi_irqs(struct pci_dev *dev, int irq) +static void xen_initdom_restore_msi_irqs(struct pci_dev *dev) { int ret = 0; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 3fcd67a16677..51bf0400a889 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -116,7 +116,7 @@ void __weak arch_teardown_msi_irqs(struct pci_dev *dev) return default_teardown_msi_irqs(dev); } -void default_restore_msi_irqs(struct pci_dev *dev, int irq) +static void default_restore_msi_irq(struct pci_dev *dev, int irq) { struct msi_desc *entry; @@ -134,9 +134,9 @@ void default_restore_msi_irqs(struct pci_dev *dev, int irq) write_msi_msg(irq, &entry->msg); } -void __weak arch_restore_msi_irqs(struct pci_dev *dev, int irq) +void __weak arch_restore_msi_irqs(struct pci_dev *dev) { - return default_restore_msi_irqs(dev, irq); + return default_restore_msi_irqs(dev); } static void msi_set_enable(struct pci_dev *dev, int enable) @@ -262,6 +262,15 @@ void unmask_msi_irq(struct irq_data *data) msi_set_mask_bit(data, 0); } +void default_restore_msi_irqs(struct pci_dev *dev) +{ + struct msi_desc *entry; + + list_for_each_entry(entry, &dev->msi_list, list) { + default_restore_msi_irq(dev, entry->irq); + } +} + void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { BUG_ON(entry->dev->current_state != PCI_D0); @@ -430,7 +439,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) pci_intx_for_msi(dev, 0); msi_set_enable(dev, 0); - arch_restore_msi_irqs(dev, dev->irq); + arch_restore_msi_irqs(dev); pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); msi_mask_irq(entry, msi_capable_mask(control), entry->masked); @@ -455,8 +464,8 @@ static void __pci_restore_msix_state(struct pci_dev *dev) control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL; pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); + arch_restore_msi_irqs(dev); list_for_each_entry(entry, &dev->msi_list, list) { - arch_restore_msi_irqs(dev, entry->irq); msix_mask_irq(entry, entry->masked); } diff --git a/include/linux/msi.h b/include/linux/msi.h index 009b02481436..92a2f991262a 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -60,10 +60,10 @@ void arch_teardown_msi_irq(unsigned int irq); int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); void arch_teardown_msi_irqs(struct pci_dev *dev); int arch_msi_check_device(struct pci_dev* dev, int nvec, int type); -void arch_restore_msi_irqs(struct pci_dev *dev, int irq); +void arch_restore_msi_irqs(struct pci_dev *dev); void default_teardown_msi_irqs(struct pci_dev *dev); -void default_restore_msi_irqs(struct pci_dev *dev, int irq); +void default_restore_msi_irqs(struct pci_dev *dev); u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag); -- cgit v1.2.3-70-g09d2 From d2f54d9b3ee32bc006a7ff25d716b32feba4b522 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 27 Nov 2013 15:31:07 -0700 Subject: PCI: Prevent bus conflicts while checking for bridge apertures pci_bridge_check_ranges() determines whether the bridge supports an I/O aperture and a prefetchable memory aperture. Previously, if the I/O aperture was unsupported, disabled, or configured at [io 0x0000-0x0fff], we wrote 0xf0 to PCI_IO_BASE and PCI_IO_LIMIT, which, if the bridge supports it, enables the I/O aperture at [io 0xf000-0xffff]. The enabled aperture may conflict with other devices in the system. Similarly, we wrote 0xfff0 to PCI_PREF_MEMORY_BASE and PCI_PREF_MEMORY_LIMIT, which enables the prefetchable memory aperture at [mem 0xfff00000-0xffffffff], and that may also conflict with other devices. All we need to know is whether the base and limit registers are writable, so we can use values that leave the apertures disabled, e.g., PCI_IO_BASE = 0xf0, PCI_IO_LIMIT = 0xe0, PCI_PREF_MEMORY_BASE = 0xfff0, PCI_PREF_MEMORY_LIMIT = 0xffe0. Writing non-zero values to both the base and limit registers means we detect whether either or both are writable, as we did before. Reported-by: Jason Gunthorpe Based-on-patch-by: Jason Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 219a4106480a..80350299a6ea 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -665,21 +665,23 @@ static void pci_bridge_check_ranges(struct pci_bus *bus) pci_read_config_word(bridge, PCI_IO_BASE, &io); if (!io) { - pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0); + pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); pci_read_config_word(bridge, PCI_IO_BASE, &io); pci_write_config_word(bridge, PCI_IO_BASE, 0x0); } if (io) b_res[0].flags |= IORESOURCE_IO; + /* DECchip 21050 pass 2 errata: the bridge may miss an address disconnect boundary by one PCI data phase. Workaround: do not use prefetching on this device. */ if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) return; + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); if (!pmem) { pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, - 0xfff0fff0); + 0xffe0fff0); pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); } -- cgit v1.2.3-70-g09d2 From 5b764b834ea925b7fc87df4a87a0fde95acbecb9 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 27 Nov 2013 17:24:50 -0700 Subject: PCI: Stop clearing bridge Secondary Status when setting up I/O aperture pci_setup_bridge_io() accessed PCI_IO_BASE and PCI_IO_LIMIT using dword (32-bit) reads and writes, which also access the Secondary Status register. Since the Secondary Status register is in the upper 16 bits of the dword, and we preserved those upper 16 bits, this had the effect of clearing any of the write-1-to-clear bits that happened to be set in the Secondary Status register. That's not what we want, so use word (16-bit) accesses to update only PCI_IO_BASE and PCI_IO_LIMIT. Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 80350299a6ea..2e344a5581ae 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -538,7 +538,8 @@ static void pci_setup_bridge_io(struct pci_bus *bus) struct pci_bus_region region; unsigned long io_mask; u8 io_base_lo, io_limit_lo; - u32 l, io_upper16; + u16 l; + u32 io_upper16; io_mask = PCI_IO_RANGE_MASK; if (bridge->io_window_1k) @@ -548,11 +549,10 @@ static void pci_setup_bridge_io(struct pci_bus *bus) res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); if (res->flags & IORESOURCE_IO) { - pci_read_config_dword(bridge, PCI_IO_BASE, &l); - l &= 0xffff0000; + pci_read_config_word(bridge, PCI_IO_BASE, &l); io_base_lo = (region.start >> 8) & io_mask; io_limit_lo = (region.end >> 8) & io_mask; - l |= ((u32) io_limit_lo << 8) | io_base_lo; + l = ((u16) io_limit_lo << 8) | io_base_lo; /* Set up upper 16 bits of I/O base/limit. */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); dev_info(&bridge->dev, " bridge window %pR\n", res); @@ -564,7 +564,7 @@ static void pci_setup_bridge_io(struct pci_bus *bus) /* Temporarily disable the I/O range before updating PCI_IO_BASE. */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); /* Update lower 16 bits of I/O base/limit. */ - pci_write_config_dword(bridge, PCI_IO_BASE, l); + pci_write_config_word(bridge, PCI_IO_BASE, l); /* Update upper 16 bits of I/O base/limit. */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); } -- cgit v1.2.3-70-g09d2 From 04df008afe0c569fd09400a35b7d3db29067114c Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Fri, 13 Dec 2013 19:39:54 +0100 Subject: EISA: Call put_device() if device_register() fails We need to give up the last reference to edev->dev, so we need to call put_device(). Signed-off-by: Levente Kurusa Signed-off-by: Bjorn Helgaas --- drivers/eisa/eisa-bus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 272a3ec35957..8842cde69177 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -232,8 +232,10 @@ static int __init eisa_init_device(struct eisa_root_device *root, static int __init eisa_register_device(struct eisa_device *edev) { int rc = device_register(&edev->dev); - if (rc) + if (rc) { + put_device(&edev->dev); return rc; + } rc = device_create_file(&edev->dev, &dev_attr_signature); if (rc) -- cgit v1.2.3-70-g09d2 From fab4c256a58bcd6877bca5b6afc97afa3b6664ac Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 13 Dec 2013 14:39:56 -0700 Subject: PCI/AER: Add a TLP header print helper ... and call it instead of duplicating the large printk format statement. No functionality change. Signed-off-by: Borislav Petkov Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_errprint.c | 44 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 2c7c9f5f592c..32efc5e0d2eb 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -124,6 +124,21 @@ static const char *aer_agent_string[] = { "Transmitter ID" }; +static void __print_tlp_header(struct pci_dev *dev, + struct aer_header_log_regs *t) +{ + unsigned char *tlp = (unsigned char *)&t; + + dev_err(&dev->dev, " TLP Header:" + " %02x%02x%02x%02x %02x%02x%02x%02x" + " %02x%02x%02x%02x %02x%02x%02x%02x\n", + *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, + *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), + *(tlp + 11), *(tlp + 10), *(tlp + 9), + *(tlp + 8), *(tlp + 15), *(tlp + 14), + *(tlp + 13), *(tlp + 12)); +} + static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { @@ -178,17 +193,8 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) __aer_print_error(dev, info); - if (info->tlp_header_valid) { - unsigned char *tlp = (unsigned char *) &info->tlp; - dev_err(&dev->dev, " TLP Header:" - " %02x%02x%02x%02x %02x%02x%02x%02x" - " %02x%02x%02x%02x %02x%02x%02x%02x\n", - *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, - *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), - *(tlp + 11), *(tlp + 10), *(tlp + 9), - *(tlp + 8), *(tlp + 15), *(tlp + 14), - *(tlp + 13), *(tlp + 12)); - } + if (info->tlp_header_valid) + __print_tlp_header(dev, &info->tlp); } if (info->id && info->error_dev_num > 1 && info->id == id) @@ -250,18 +256,10 @@ void cper_print_aer(struct pci_dev *dev, int cper_severity, if (aer_severity != AER_CORRECTABLE) dev_err(&dev->dev, "aer_uncor_severity: 0x%08x\n", aer->uncor_severity); - if (tlp_header_valid) { - const unsigned char *tlp; - tlp = (const unsigned char *)&aer->header_log; - dev_err(&dev->dev, "aer_tlp_header:" - " %02x%02x%02x%02x %02x%02x%02x%02x" - " %02x%02x%02x%02x %02x%02x%02x%02x\n", - *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, - *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), - *(tlp + 11), *(tlp + 10), *(tlp + 9), - *(tlp + 8), *(tlp + 15), *(tlp + 14), - *(tlp + 13), *(tlp + 12)); - } + + if (tlp_header_valid) + __print_tlp_header(dev, &aer->header_log); + trace_aer_event(dev_name(&dev->dev), (status & ~mask), aer_severity); } -- cgit v1.2.3-70-g09d2 From 1c38108a086f1cca3700765607059a6d865910c2 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 13 Dec 2013 14:40:03 -0700 Subject: PCI/AER: Clean up error printing code a bit Save one indentation level in aer_print_error() for the generic case where we have info->status of an error, disregard 80 cols rule a bit for the sake of better readability, fix alignment. No functionality change. Signed-off-by: Borislav Petkov Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_errprint.c | 51 ++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 32efc5e0d2eb..34ff7026440c 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -168,39 +168,39 @@ static void __aer_print_error(struct pci_dev *dev, void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { + int layer, agent; int id = ((dev->bus->number << 8) | dev->devfn); - if (info->status == 0) { + if (!info->status) { dev_err(&dev->dev, "PCIe Bus Error: severity=%s, type=Unaccessible, " "id=%04x(Unregistered Agent ID)\n", aer_error_severity_string[info->severity], id); - } else { - int layer, agent; + goto out; + } - layer = AER_GET_LAYER_ERROR(info->severity, info->status); - agent = AER_GET_AGENT(info->severity, info->status); + layer = AER_GET_LAYER_ERROR(info->severity, info->status); + agent = AER_GET_AGENT(info->severity, info->status); - dev_err(&dev->dev, - "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n", - aer_error_severity_string[info->severity], - aer_error_layer[layer], id, aer_agent_string[agent]); + dev_err(&dev->dev, + "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n", + aer_error_severity_string[info->severity], + aer_error_layer[layer], id, aer_agent_string[agent]); - dev_err(&dev->dev, - " device [%04x:%04x] error status/mask=%08x/%08x\n", - dev->vendor, dev->device, - info->status, info->mask); + dev_err(&dev->dev, + " device [%04x:%04x] error status/mask=%08x/%08x\n", + dev->vendor, dev->device, + info->status, info->mask); - __aer_print_error(dev, info); + __aer_print_error(dev, info); - if (info->tlp_header_valid) - __print_tlp_header(dev, &info->tlp); - } + if (info->tlp_header_valid) + __print_tlp_header(dev, &info->tlp); +out: if (info->id && info->error_dev_num > 1 && info->id == id) - dev_err(&dev->dev, - " Error of this Agent(%04x) is reported first\n", - id); + dev_err(&dev->dev, " Error of this Agent(%04x) is reported first\n", id); + trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask), info->severity); } @@ -234,6 +234,7 @@ void cper_print_aer(struct pci_dev *dev, int cper_severity, const char **status_strs; aer_severity = cper_severity_to_aer(cper_severity); + if (aer_severity == AER_CORRECTABLE) { status = aer->cor_status; mask = aer->cor_mask; @@ -246,16 +247,18 @@ void cper_print_aer(struct pci_dev *dev, int cper_severity, status_strs_size = ARRAY_SIZE(aer_uncorrectable_error_string); tlp_header_valid = status & AER_LOG_TLP_MASKS; } + layer = AER_GET_LAYER_ERROR(aer_severity, status); agent = AER_GET_AGENT(aer_severity, status); - dev_err(&dev->dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", - status, mask); + + dev_err(&dev->dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask); cper_print_bits("", status, status_strs, status_strs_size); dev_err(&dev->dev, "aer_layer=%s, aer_agent=%s\n", - aer_error_layer[layer], aer_agent_string[agent]); + aer_error_layer[layer], aer_agent_string[agent]); + if (aer_severity != AER_CORRECTABLE) dev_err(&dev->dev, "aer_uncor_severity: 0x%08x\n", - aer->uncor_severity); + aer->uncor_severity); if (tlp_header_valid) __print_tlp_header(dev, &aer->header_log); -- cgit v1.2.3-70-g09d2 From 8e7f8d0b30d4e3e30007b10eb2916d970b5f8c93 Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Fri, 13 Dec 2013 08:23:16 -0700 Subject: PCI/AER: Ignore non-PCIe AER error sources in aer_hest_parse() aer_set_firmware_first() searches the HEST for an error source descriptor matching the specified PCI device. It uses the apei_hest_parse() iterator to call aer_hest_parse() for every descriptor in the HEST. Previously, aer_hest_parse() incorrectly assumed every descriptor was for a PCIe error source. This patch adds a check to avoid that error. [bhelgaas: factor check into helper, use in aer_hest_parse_aff(), changelog] Signed-off-by: Betty Dall Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_acpi.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index cf611ab2193a..a23995749f1d 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -50,12 +50,24 @@ struct aer_hest_parse_info { int firmware_first; }; +static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr) +{ + if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT || + hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT || + hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE) + return 1; + return 0; +} + static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) { struct aer_hest_parse_info *info = data; struct acpi_hest_aer_common *p; int ff; + if (!hest_source_is_pcie_aer(hest_hdr)) + return 0; + p = (struct acpi_hest_aer_common *)(hest_hdr + 1); ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); if (p->flags & ACPI_HEST_GLOBAL) { @@ -104,15 +116,12 @@ static int aer_hest_parse_aff(struct acpi_hest_header *hest_hdr, void *data) if (aer_firmware_first) return 0; - switch (hest_hdr->type) { - case ACPI_HEST_TYPE_AER_ROOT_PORT: - case ACPI_HEST_TYPE_AER_ENDPOINT: - case ACPI_HEST_TYPE_AER_BRIDGE: - p = (struct acpi_hest_aer_common *)(hest_hdr + 1); - aer_firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); - default: + if (!hest_source_is_pcie_aer(hest_hdr)) return 0; - } + + p = (struct acpi_hest_aer_common *)(hest_hdr + 1); + aer_firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); + return 0; } /** -- cgit v1.2.3-70-g09d2 From 3620437a73b0ba68bac9c4e8d0fc9b809efca521 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 13 Dec 2013 15:42:53 -0700 Subject: PCI/AER: Consolidate HEST error source parsers aer_hest_parse() and aer_hest_parse_aff() are almost identical. We use aer_hest_parse() to check the ACPI_HEST_FIRMWARE_FIRST flag for a specific device, and we use aer_hest_parse_aff() to check to see if any device sets the flag. This drops aer_hest_parse_aff() and enhances aer_hest_parse() so it collects the union of the PCIe ACPI_HEST_FIRMWARE_FIRST flag settings when no specific device is supplied. No functional change. Signed-off-by: Bjorn Helgaas Reviewed-by: Betty Dall --- drivers/pci/pcie/aer/aerdrv_acpi.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index a23995749f1d..4d6991794fa2 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -70,6 +70,17 @@ static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) p = (struct acpi_hest_aer_common *)(hest_hdr + 1); ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); + + /* + * If no specific device is supplied, determine whether + * FIRMWARE_FIRST is set for *any* PCIe device. + */ + if (!info->pci_dev) { + info->firmware_first |= ff; + return 0; + } + + /* Otherwise, check the specific device */ if (p->flags & ACPI_HEST_GLOBAL) { if (hest_match_type(hest_hdr, info->pci_dev)) info->firmware_first = ff; @@ -109,30 +120,20 @@ int pcie_aer_get_firmware_first(struct pci_dev *dev) static bool aer_firmware_first; -static int aer_hest_parse_aff(struct acpi_hest_header *hest_hdr, void *data) -{ - struct acpi_hest_aer_common *p; - - if (aer_firmware_first) - return 0; - - if (!hest_source_is_pcie_aer(hest_hdr)) - return 0; - - p = (struct acpi_hest_aer_common *)(hest_hdr + 1); - aer_firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); - return 0; -} - /** * aer_acpi_firmware_first - Check if APEI should control AER. */ bool aer_acpi_firmware_first(void) { static bool parsed = false; + struct aer_hest_parse_info info = { + .pci_dev = NULL, /* Check all PCIe devices */ + .firmware_first = 0, + }; if (!parsed) { - apei_hest_parse(aer_hest_parse_aff, NULL); + apei_hest_parse(aer_hest_parse, &info); + aer_firmware_first = info.firmware_first; parsed = true; } return aer_firmware_first; -- cgit v1.2.3-70-g09d2 From cd84d34074f56595fbae7a70f8a85b7d089249d1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 9 May 2013 11:26:16 -0600 Subject: PCI: pciehp: Drop pciehp_readw()/pciehp_writew() wrappers The pciehp_readw() and pciehp_writew() wrappers only look up the pci_dev and call the PCIe Capability accessors, so we can make things a little more straightforward by just using the PCIe Capability accessors directly. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 95 ++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 3eea3fdd4b0b..53bb5501d199 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -41,28 +41,9 @@ #include "../pci.h" #include "pciehp.h" -static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value) +static inline struct pci_dev *ctrl_dev(struct controller *ctrl) { - struct pci_dev *dev = ctrl->pcie->port; - return pcie_capability_read_word(dev, reg, value); -} - -static inline int pciehp_readl(struct controller *ctrl, int reg, u32 *value) -{ - struct pci_dev *dev = ctrl->pcie->port; - return pcie_capability_read_dword(dev, reg, value); -} - -static inline int pciehp_writew(struct controller *ctrl, int reg, u16 value) -{ - struct pci_dev *dev = ctrl->pcie->port; - return pcie_capability_write_word(dev, reg, value); -} - -static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) -{ - struct pci_dev *dev = ctrl->pcie->port; - return pcie_capability_write_dword(dev, reg, value); + return ctrl->pcie->port; } /* Power Control Command */ @@ -129,20 +110,24 @@ static inline void pciehp_free_irq(struct controller *ctrl) static int pcie_poll_cmd(struct controller *ctrl) { + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; int err, timeout = 1000; - err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + err = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { - pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC); return 1; } while (timeout > 0) { msleep(10); timeout -= 10; - err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + err = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, + &slot_status); if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { - pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC); return 1; } } @@ -171,13 +156,14 @@ static void pcie_wait_cmd(struct controller *ctrl, int poll) */ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) { + struct pci_dev *pdev = ctrl_dev(ctrl); int retval = 0; u16 slot_status; u16 slot_ctrl; mutex_lock(&ctrl->ctrl_lock); - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); @@ -207,7 +193,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) } } - retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); + retval = pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); goto out; @@ -217,7 +203,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; smp_mb(); - retval = pciehp_writew(ctrl, PCI_EXP_SLTCTL, slot_ctrl); + retval = pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); if (retval) ctrl_err(ctrl, "Cannot write to SLOTCTRL register\n"); @@ -243,10 +229,11 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) static bool check_link_active(struct controller *ctrl) { + struct pci_dev *pdev = ctrl_dev(ctrl); bool ret = false; u16 lnk_status; - if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status)) + if (pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status)) return ret; ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); @@ -311,6 +298,7 @@ static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) int pciehp_check_link_status(struct controller *ctrl) { + struct pci_dev *pdev = ctrl_dev(ctrl); u16 lnk_status; int retval = 0; bool found = false; @@ -330,7 +318,7 @@ int pciehp_check_link_status(struct controller *ctrl) found = pci_bus_check_dev(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0)); - retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); + retval = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); if (retval) { ctrl_err(ctrl, "Cannot read LNKSTATUS register\n"); return retval; @@ -354,10 +342,11 @@ int pciehp_check_link_status(struct controller *ctrl) static int __pciehp_link_set(struct controller *ctrl, bool enable) { + struct pci_dev *pdev = ctrl_dev(ctrl); u16 lnk_ctrl; int retval = 0; - retval = pciehp_readw(ctrl, PCI_EXP_LNKCTL, &lnk_ctrl); + retval = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl); if (retval) { ctrl_err(ctrl, "Cannot read LNKCTRL register\n"); return retval; @@ -368,7 +357,7 @@ static int __pciehp_link_set(struct controller *ctrl, bool enable) else lnk_ctrl |= PCI_EXP_LNKCTL_LD; - retval = pciehp_writew(ctrl, PCI_EXP_LNKCTL, lnk_ctrl); + retval = pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl); if (retval) { ctrl_err(ctrl, "Cannot write LNKCTRL register\n"); return retval; @@ -391,11 +380,12 @@ static int pciehp_link_disable(struct controller *ctrl) int pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; u8 atten_led_state; int retval = 0; - retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); + retval = pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); return retval; @@ -430,11 +420,12 @@ int pciehp_get_attention_status(struct slot *slot, u8 *status) int pciehp_get_power_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; u8 pwr_state; int retval = 0; - retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); + retval = pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); return retval; @@ -462,10 +453,11 @@ int pciehp_get_power_status(struct slot *slot, u8 *status) int pciehp_get_latch_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; int retval; - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); @@ -478,10 +470,11 @@ int pciehp_get_latch_status(struct slot *slot, u8 *status) int pciehp_get_adapter_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; int retval; - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); @@ -494,10 +487,11 @@ int pciehp_get_adapter_status(struct slot *slot, u8 *status) int pciehp_query_power_fault(struct slot *slot) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; int retval; - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "Cannot check for power fault\n"); return retval; @@ -572,13 +566,14 @@ void pciehp_green_led_blink(struct slot *slot) int pciehp_power_on_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_cmd; u16 cmd_mask; u16 slot_status; int retval = 0; /* Clear sticky power-fault bit from previous power failures */ - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); @@ -586,7 +581,7 @@ int pciehp_power_on_slot(struct slot * slot) } slot_status &= PCI_EXP_SLTSTA_PFD; if (slot_status) { - retval = pciehp_writew(ctrl, PCI_EXP_SLTSTA, slot_status); + retval = pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS register\n", @@ -643,6 +638,7 @@ int pciehp_power_off_slot(struct slot * slot) static irqreturn_t pcie_isr(int irq, void *dev_id) { struct controller *ctrl = (struct controller *)dev_id; + struct pci_dev *pdev = ctrl_dev(ctrl); struct slot *slot = ctrl->slot; u16 detected, intr_loc; @@ -653,7 +649,8 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) */ intr_loc = 0; do { - if (pciehp_readw(ctrl, PCI_EXP_SLTSTA, &detected)) { + if (pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, + &detected)) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS\n", __func__); return IRQ_NONE; @@ -666,7 +663,9 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) intr_loc |= detected; if (!intr_loc) return IRQ_NONE; - if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, intr_loc)) { + if (detected && + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + intr_loc)) { ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS\n", __func__); return IRQ_NONE; @@ -758,6 +757,7 @@ static void pcie_disable_notification(struct controller *ctrl) int pciehp_reset_slot(struct slot *slot, int probe) { struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); if (probe) return 0; @@ -771,7 +771,8 @@ int pciehp_reset_slot(struct slot *slot, int probe) pci_reset_bridge_secondary_bus(ctrl->pcie->port); if (HP_SUPR_RM(ctrl)) { - pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC); + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC); pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE); if (pciehp_poll_mode) int_poll_timeout(ctrl->poll_timer.data); @@ -875,9 +876,9 @@ static inline void dbg_ctrl(struct controller *ctrl) EMI(ctrl) ? "yes" : "no"); ctrl_info(ctrl, " Command Completed : %3s\n", NO_CMD_CMPL(ctrl) ? "no" : "yes"); - pciehp_readw(ctrl, PCI_EXP_SLTSTA, ®16); + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, ®16); ctrl_info(ctrl, "Slot Status : 0x%04x\n", reg16); - pciehp_readw(ctrl, PCI_EXP_SLTCTL, ®16); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, ®16); ctrl_info(ctrl, "Slot Control : 0x%04x\n", reg16); } @@ -893,7 +894,7 @@ struct controller *pcie_init(struct pcie_device *dev) goto abort; } ctrl->pcie = dev; - if (pciehp_readl(ctrl, PCI_EXP_SLTCAP, &slot_cap)) { + if (pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap)) { ctrl_err(ctrl, "Cannot read SLOTCAP register\n"); goto abort_ctrl; } @@ -913,7 +914,7 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->no_cmd_complete = 1; /* Check if Data Link Layer Link Active Reporting is implemented */ - if (pciehp_readl(ctrl, PCI_EXP_LNKCAP, &link_cap)) { + if (pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap)) { ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); goto abort_ctrl; } @@ -923,7 +924,7 @@ struct controller *pcie_init(struct pcie_device *dev) } /* Clear all remaining event bits in Slot Status register */ - if (pciehp_writew(ctrl, PCI_EXP_SLTSTA, 0x1f)) + if (pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, 0x1f)) goto abort_ctrl; /* Disable software notification */ -- cgit v1.2.3-70-g09d2 From 1a84b99ccbb954ce201024691babfe4d7d9f506f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 14 Dec 2013 13:06:07 -0700 Subject: PCI: pciehp: Remove error checks when accessing PCIe Capability There's not much point in checking the return value from every config space access because the only likely errors are design-time things like unaligned accesses or invalid register numbers. The checking clutters the code significantly, so this patch removes it. No functional change. Reference: http://lkml.kernel.org/r/CA+55aFzP4xEbcNmZ+MS0SQ3LrULzSq+dBiT_X9U-bPpR-Ukgrw@mail.gmail.com Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 177 +++++++++------------------------------ 1 file changed, 41 insertions(+), 136 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 53bb5501d199..6afdd2358227 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -112,10 +112,10 @@ static int pcie_poll_cmd(struct controller *ctrl) { struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; - int err, timeout = 1000; + int timeout = 1000; - err = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_CC) { pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); return 1; @@ -123,9 +123,8 @@ static int pcie_poll_cmd(struct controller *ctrl) while (timeout > 0) { msleep(10); timeout -= 10; - err = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, - &slot_status); - if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_CC) { pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); return 1; @@ -157,19 +156,12 @@ static void pcie_wait_cmd(struct controller *ctrl, int poll) static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) { struct pci_dev *pdev = ctrl_dev(ctrl); - int retval = 0; u16 slot_status; u16 slot_ctrl; mutex_lock(&ctrl->ctrl_lock); - retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", - __func__); - goto out; - } - + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); if (slot_status & PCI_EXP_SLTSTA_CC) { if (!ctrl->no_cmd_complete) { /* @@ -193,24 +185,17 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) } } - retval = pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); - goto out; - } - + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); slot_ctrl &= ~mask; slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; smp_mb(); - retval = pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); - if (retval) - ctrl_err(ctrl, "Cannot write to SLOTCTRL register\n"); + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); /* * Wait for command completion. */ - if (!retval && !ctrl->no_cmd_complete) { + if (!ctrl->no_cmd_complete) { int poll = 0; /* * if hotplug interrupt is not enabled or command @@ -222,20 +207,17 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) poll = 1; pcie_wait_cmd(ctrl, poll); } - out: mutex_unlock(&ctrl->ctrl_lock); - return retval; + return 0; } static bool check_link_active(struct controller *ctrl) { struct pci_dev *pdev = ctrl_dev(ctrl); - bool ret = false; u16 lnk_status; + bool ret; - if (pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status)) - return ret; - + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); if (ret) @@ -299,9 +281,8 @@ static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) int pciehp_check_link_status(struct controller *ctrl) { struct pci_dev *pdev = ctrl_dev(ctrl); + bool found; u16 lnk_status; - int retval = 0; - bool found = false; /* * Data Link Layer Link Active Reporting must be capable for @@ -318,53 +299,37 @@ int pciehp_check_link_status(struct controller *ctrl) found = pci_bus_check_dev(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0)); - retval = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); - if (retval) { - ctrl_err(ctrl, "Cannot read LNKSTATUS register\n"); - return retval; - } - + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); if ((lnk_status & PCI_EXP_LNKSTA_LT) || !(lnk_status & PCI_EXP_LNKSTA_NLW)) { ctrl_err(ctrl, "Link Training Error occurs \n"); - retval = -1; - return retval; + return -1; } pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status); - if (!found && !retval) - retval = -1; + if (!found) + return -1; - return retval; + return 0; } static int __pciehp_link_set(struct controller *ctrl, bool enable) { struct pci_dev *pdev = ctrl_dev(ctrl); u16 lnk_ctrl; - int retval = 0; - retval = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl); - if (retval) { - ctrl_err(ctrl, "Cannot read LNKCTRL register\n"); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl); if (enable) lnk_ctrl &= ~PCI_EXP_LNKCTL_LD; else lnk_ctrl |= PCI_EXP_LNKCTL_LD; - retval = pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl); - if (retval) { - ctrl_err(ctrl, "Cannot write LNKCTRL register\n"); - return retval; - } + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl); ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl); - - return retval; + return 0; } static int pciehp_link_enable(struct controller *ctrl) @@ -383,14 +348,8 @@ int pciehp_get_attention_status(struct slot *slot, u8 *status) struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; u8 atten_led_state; - int retval = 0; - - retval = pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); @@ -423,13 +382,8 @@ int pciehp_get_power_status(struct slot *slot, u8 *status) struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; u8 pwr_state; - int retval = 0; - retval = pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); @@ -447,55 +401,35 @@ int pciehp_get_power_status(struct slot *slot, u8 *status) break; } - return retval; + return 0; } int pciehp_get_latch_status(struct slot *slot, u8 *status) { - struct controller *ctrl = slot->ctrl; - struct pci_dev *pdev = ctrl_dev(ctrl); + struct pci_dev *pdev = ctrl_dev(slot->ctrl); u16 slot_status; - int retval; - retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", - __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); return 0; } int pciehp_get_adapter_status(struct slot *slot, u8 *status) { - struct controller *ctrl = slot->ctrl; - struct pci_dev *pdev = ctrl_dev(ctrl); + struct pci_dev *pdev = ctrl_dev(slot->ctrl); u16 slot_status; - int retval; - retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", - __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); *status = !!(slot_status & PCI_EXP_SLTSTA_PDS); return 0; } int pciehp_query_power_fault(struct slot *slot) { - struct controller *ctrl = slot->ctrl; - struct pci_dev *pdev = ctrl_dev(ctrl); + struct pci_dev *pdev = ctrl_dev(slot->ctrl); u16 slot_status; - int retval; - retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "Cannot check for power fault\n"); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); return !!(slot_status & PCI_EXP_SLTSTA_PFD); } @@ -570,25 +504,13 @@ int pciehp_power_on_slot(struct slot * slot) u16 slot_cmd; u16 cmd_mask; u16 slot_status; - int retval = 0; + int retval; /* Clear sticky power-fault bit from previous power failures */ - retval = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", - __func__); - return retval; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); slot_status &= PCI_EXP_SLTSTA_PFD; - if (slot_status) { - retval = pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, slot_status); - if (retval) { - ctrl_err(ctrl, - "%s: Cannot write to SLOTSTATUS register\n", - __func__); - return retval; - } - } + if (slot_status) + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, slot_status); ctrl->power_fault_detected = 0; slot_cmd = POWER_ON; @@ -649,12 +571,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) */ intr_loc = 0; do { - if (pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, - &detected)) { - ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS\n", - __func__); - return IRQ_NONE; - } + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected); detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | @@ -663,13 +580,9 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) intr_loc |= detected; if (!intr_loc) return IRQ_NONE; - if (detected && - pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, - intr_loc)) { - ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS\n", - __func__); - return IRQ_NONE; - } + if (detected) + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + intr_loc); } while (detected); ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc); @@ -894,11 +807,7 @@ struct controller *pcie_init(struct pcie_device *dev) goto abort; } ctrl->pcie = dev; - if (pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap)) { - ctrl_err(ctrl, "Cannot read SLOTCAP register\n"); - goto abort_ctrl; - } - + pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); ctrl->slot_cap = slot_cap; mutex_init(&ctrl->ctrl_lock); init_waitqueue_head(&ctrl->queue); @@ -914,18 +823,14 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->no_cmd_complete = 1; /* Check if Data Link Layer Link Active Reporting is implemented */ - if (pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap)) { - ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); - goto abort_ctrl; - } + pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { ctrl_dbg(ctrl, "Link Active Reporting supported\n"); ctrl->link_active_reporting = 1; } /* Clear all remaining event bits in Slot Status register */ - if (pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, 0x1f)) - goto abort_ctrl; + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, 0x1f); /* Disable software notification */ pcie_disable_notification(ctrl); -- cgit v1.2.3-70-g09d2 From 6dae62020f0e6a2ffe424c8cea542fa49d42ec6e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 14 Dec 2013 13:06:16 -0700 Subject: PCI: pciehp: Make various functions void since they can't fail These functions: pcie_enable_notification() pciehp_power_off_slot() pciehp_get_power_status() pciehp_get_attention_status() pciehp_set_attention_status() pciehp_get_latch_status() pciehp_get_adapter_status() pcie_write_cmd() now always return success, so this patch makes them void and drops the error-checking code in their callers. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 14 +++++----- drivers/pci/hotplug/pciehp_core.c | 15 +++++++---- drivers/pci/hotplug/pciehp_ctrl.c | 52 +++++++++++++----------------------- drivers/pci/hotplug/pciehp_hpc.c | 56 +++++++++++---------------------------- drivers/pci/hotplug/pciehp_pci.c | 6 ++--- 5 files changed, 54 insertions(+), 89 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 21e865ded1dc..ffe6a6b336cf 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -140,15 +140,15 @@ struct controller *pcie_init(struct pcie_device *dev); int pcie_init_notification(struct controller *ctrl); int pciehp_enable_slot(struct slot *p_slot); int pciehp_disable_slot(struct slot *p_slot); -int pcie_enable_notification(struct controller *ctrl); +void pcie_enable_notification(struct controller *ctrl); int pciehp_power_on_slot(struct slot *slot); -int pciehp_power_off_slot(struct slot *slot); -int pciehp_get_power_status(struct slot *slot, u8 *status); -int pciehp_get_attention_status(struct slot *slot, u8 *status); +void pciehp_power_off_slot(struct slot *slot); +void pciehp_get_power_status(struct slot *slot, u8 *status); +void pciehp_get_attention_status(struct slot *slot, u8 *status); -int pciehp_set_attention_status(struct slot *slot, u8 status); -int pciehp_get_latch_status(struct slot *slot, u8 *status); -int pciehp_get_adapter_status(struct slot *slot, u8 *status); +void pciehp_set_attention_status(struct slot *slot, u8 status); +void pciehp_get_latch_status(struct slot *slot, u8 *status); +void pciehp_get_adapter_status(struct slot *slot, u8 *status); int pciehp_query_power_fault(struct slot *slot); void pciehp_green_led_on(struct slot *slot); void pciehp_green_led_off(struct slot *slot); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index bbd48bbe4e9b..143a389d81fa 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -160,7 +160,8 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_set_attention_status(slot, status); + pciehp_set_attention_status(slot, status); + return 0; } @@ -192,7 +193,8 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_get_power_status(slot, value); + pciehp_get_power_status(slot, value); + return 0; } static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) @@ -202,7 +204,8 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_get_attention_status(slot, value); + pciehp_get_attention_status(slot, value); + return 0; } static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) @@ -212,7 +215,8 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_get_latch_status(slot, value); + pciehp_get_latch_status(slot, value); + return 0; } static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) @@ -222,7 +226,8 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", __func__, slot_name(slot)); - return pciehp_get_adapter_status(slot, value); + pciehp_get_adapter_status(slot, value); + return 0; } static int reset_slot(struct hotplug_slot *hotplug_slot, int probe) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 38f018679175..6c730b161b33 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -158,11 +158,8 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) { /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ if (POWER_CTRL(ctrl)) { - if (pciehp_power_off_slot(pslot)) { - ctrl_err(ctrl, - "Issue of Slot Power Off command failed\n"); - return; - } + pciehp_power_off_slot(pslot); + /* * After turning power off, we must wait for at least 1 second * before taking any action that relies on power having been @@ -174,13 +171,8 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) if (PWR_LED(ctrl)) pciehp_green_led_off(pslot); - if (ATTN_LED(ctrl)) { - if (pciehp_set_attention_status(pslot, 1)) { - ctrl_err(ctrl, - "Issue of Set Attention Led command failed\n"); - return; - } - } + if (ATTN_LED(ctrl)) + pciehp_set_attention_status(pslot, 1); } /** @@ -243,7 +235,7 @@ err_exit: */ static int remove_board(struct slot *p_slot) { - int retval = 0; + int retval; struct controller *ctrl = p_slot->ctrl; retval = pciehp_unconfigure_device(p_slot); @@ -251,13 +243,8 @@ static int remove_board(struct slot *p_slot) return retval; if (POWER_CTRL(ctrl)) { - /* power off slot */ - retval = pciehp_power_off_slot(p_slot); - if (retval) { - ctrl_err(ctrl, - "Issue of Slot Disable command failed\n"); - return retval; - } + pciehp_power_off_slot(p_slot); + /* * After turning power off, we must wait for at least 1 second * before taking any action that relies on power having been @@ -482,14 +469,14 @@ int pciehp_enable_slot(struct slot *p_slot) int rc; struct controller *ctrl = p_slot->ctrl; - rc = pciehp_get_adapter_status(p_slot, &getstatus); - if (rc || !getstatus) { + pciehp_get_adapter_status(p_slot, &getstatus); + if (!getstatus) { ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); return -ENODEV; } if (MRL_SENS(p_slot->ctrl)) { - rc = pciehp_get_latch_status(p_slot, &getstatus); - if (rc || getstatus) { + pciehp_get_latch_status(p_slot, &getstatus); + if (getstatus) { ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); return -ENODEV; @@ -497,8 +484,8 @@ int pciehp_enable_slot(struct slot *p_slot) } if (POWER_CTRL(p_slot->ctrl)) { - rc = pciehp_get_power_status(p_slot, &getstatus); - if (rc || getstatus) { + pciehp_get_power_status(p_slot, &getstatus); + if (getstatus) { ctrl_info(ctrl, "Already enabled on slot(%s)\n", slot_name(p_slot)); return -EINVAL; @@ -518,15 +505,14 @@ int pciehp_enable_slot(struct slot *p_slot) int pciehp_disable_slot(struct slot *p_slot) { u8 getstatus = 0; - int ret = 0; struct controller *ctrl = p_slot->ctrl; if (!p_slot->ctrl) return 1; if (!HP_SUPR_RM(p_slot->ctrl)) { - ret = pciehp_get_adapter_status(p_slot, &getstatus); - if (ret || !getstatus) { + pciehp_get_adapter_status(p_slot, &getstatus); + if (!getstatus) { ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); return -ENODEV; @@ -534,8 +520,8 @@ int pciehp_disable_slot(struct slot *p_slot) } if (MRL_SENS(p_slot->ctrl)) { - ret = pciehp_get_latch_status(p_slot, &getstatus); - if (ret || getstatus) { + pciehp_get_latch_status(p_slot, &getstatus); + if (getstatus) { ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); return -ENODEV; @@ -543,8 +529,8 @@ int pciehp_disable_slot(struct slot *p_slot) } if (POWER_CTRL(p_slot->ctrl)) { - ret = pciehp_get_power_status(p_slot, &getstatus); - if (ret || !getstatus) { + pciehp_get_power_status(p_slot, &getstatus); + if (!getstatus) { ctrl_info(ctrl, "Already disabled on slot(%s)\n", slot_name(p_slot)); return -EINVAL; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 6afdd2358227..83df74910f9a 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -153,7 +153,7 @@ static void pcie_wait_cmd(struct controller *ctrl, int poll) * @cmd: command value written to slot control register * @mask: bitmask of slot control register to be modified */ -static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) +static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) { struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_status; @@ -208,7 +208,6 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) pcie_wait_cmd(ctrl, poll); } mutex_unlock(&ctrl->ctrl_lock); - return 0; } static bool check_link_active(struct controller *ctrl) @@ -342,7 +341,7 @@ static int pciehp_link_disable(struct controller *ctrl) return __pciehp_link_set(ctrl, false); } -int pciehp_get_attention_status(struct slot *slot, u8 *status) +void pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); @@ -372,11 +371,9 @@ int pciehp_get_attention_status(struct slot *slot, u8 *status) *status = 0xFF; break; } - - return 0; } -int pciehp_get_power_status(struct slot *slot, u8 *status) +void pciehp_get_power_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); @@ -400,28 +397,24 @@ int pciehp_get_power_status(struct slot *slot, u8 *status) *status = 0xFF; break; } - - return 0; } -int pciehp_get_latch_status(struct slot *slot, u8 *status) +void pciehp_get_latch_status(struct slot *slot, u8 *status) { struct pci_dev *pdev = ctrl_dev(slot->ctrl); u16 slot_status; pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); - return 0; } -int pciehp_get_adapter_status(struct slot *slot, u8 *status) +void pciehp_get_adapter_status(struct slot *slot, u8 *status) { struct pci_dev *pdev = ctrl_dev(slot->ctrl); u16 slot_status; pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); *status = !!(slot_status & PCI_EXP_SLTSTA_PDS); - return 0; } int pciehp_query_power_fault(struct slot *slot) @@ -433,7 +426,7 @@ int pciehp_query_power_fault(struct slot *slot) return !!(slot_status & PCI_EXP_SLTSTA_PFD); } -int pciehp_set_attention_status(struct slot *slot, u8 value) +void pciehp_set_attention_status(struct slot *slot, u8 value) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; @@ -451,11 +444,11 @@ int pciehp_set_attention_status(struct slot *slot, u8 value) slot_cmd = 0x0080; break; default: - return -EINVAL; + return; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); - return pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); } void pciehp_green_led_on(struct slot *slot) @@ -515,11 +508,7 @@ int pciehp_power_on_slot(struct slot * slot) slot_cmd = POWER_ON; cmd_mask = PCI_EXP_SLTCTL_PCC; - retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); - if (retval) { - ctrl_err(ctrl, "Write %x command failed!\n", slot_cmd); - return retval; - } + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); @@ -530,12 +519,11 @@ int pciehp_power_on_slot(struct slot * slot) return retval; } -int pciehp_power_off_slot(struct slot * slot) +void pciehp_power_off_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; u16 cmd_mask; - int retval; /* Disable the link at first */ pciehp_link_disable(ctrl); @@ -547,14 +535,9 @@ int pciehp_power_off_slot(struct slot * slot) slot_cmd = POWER_OFF; cmd_mask = PCI_EXP_SLTCTL_PCC; - retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); - if (retval) { - ctrl_err(ctrl, "Write command failed!\n"); - return retval; - } + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); - return 0; } static irqreturn_t pcie_isr(int irq, void *dev_id) @@ -617,7 +600,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) return IRQ_HANDLED; } -int pcie_enable_notification(struct controller *ctrl) +void pcie_enable_notification(struct controller *ctrl) { u16 cmd, mask; @@ -643,22 +626,18 @@ int pcie_enable_notification(struct controller *ctrl) PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); - if (pcie_write_cmd(ctrl, cmd, mask)) { - ctrl_err(ctrl, "Cannot enable software notification\n"); - return -1; - } - return 0; + pcie_write_cmd(ctrl, cmd, mask); } static void pcie_disable_notification(struct controller *ctrl) { u16 mask; + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_DLLSCE); - if (pcie_write_cmd(ctrl, 0, mask)) - ctrl_warn(ctrl, "Cannot disable software notification\n"); + pcie_write_cmd(ctrl, 0, mask); } /* @@ -698,10 +677,7 @@ int pcie_init_notification(struct controller *ctrl) { if (pciehp_request_irq(ctrl)) return -1; - if (pcie_enable_notification(ctrl)) { - pciehp_free_irq(ctrl); - return -1; - } + pcie_enable_notification(ctrl); ctrl->notification_enabled = 1; return 0; } diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 0e0d0f7f63fd..198355112ee7 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -78,7 +78,7 @@ int pciehp_configure_device(struct slot *p_slot) int pciehp_unconfigure_device(struct slot *p_slot) { - int ret, rc = 0; + int rc = 0; u8 bctl = 0; u8 presence = 0; struct pci_dev *dev, *temp; @@ -88,9 +88,7 @@ int pciehp_unconfigure_device(struct slot *p_slot) ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", __func__, pci_domain_nr(parent), parent->number); - ret = pciehp_get_adapter_status(p_slot, &presence); - if (ret) - presence = 0; + pciehp_get_adapter_status(p_slot, &presence); /* * Stopping an SR-IOV PF device removes all the associated VFs, -- cgit v1.2.3-70-g09d2 From afe2478f2e3fffc45a1be24ebfc23945a66a80fd Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 14 Dec 2013 13:06:36 -0700 Subject: PCI: pciehp: Announce slot capabilities (slot #, button, LEDs, etc) We already have the vendor/device IDs from pci_setup_device(), so drop that info and print things that will be more useful for debugging: the slot number and presence of button/indicators/link active reporting/etc. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 83df74910f9a..55109d6d8a90 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -771,6 +771,8 @@ static inline void dbg_ctrl(struct controller *ctrl) ctrl_info(ctrl, "Slot Control : 0x%04x\n", reg16); } +#define FLAG(x,y) (((x) & (y)) ? '+' : '-') + struct controller *pcie_init(struct pcie_device *dev) { struct controller *ctrl; @@ -811,9 +813,16 @@ struct controller *pcie_init(struct pcie_device *dev) /* Disable software notification */ pcie_disable_notification(ctrl); - ctrl_info(ctrl, "HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", - pdev->vendor, pdev->device, pdev->subsystem_vendor, - pdev->subsystem_device); + ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n", + (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, + FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), + FLAG(slot_cap, PCI_EXP_SLTCAP_AIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_PIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), + FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP), + FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), + FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC)); if (pcie_init_slot(ctrl)) goto abort_ctrl; -- cgit v1.2.3-70-g09d2 From 2f2ed41cf43e22686349ecdf4ca247ea3831af55 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 14 Dec 2013 13:06:40 -0700 Subject: PCI: pciehp: Simplify "Power Fault Detected" checking/clearing It's simpler to test the PCI_EXP_SLTSTA_PFD bit directly and to write the constant back to PCI_EXP_SLTSTA. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 55109d6d8a90..cb3100af7e8c 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -501,9 +501,9 @@ int pciehp_power_on_slot(struct slot * slot) /* Clear sticky power-fault bit from previous power failures */ pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); - slot_status &= PCI_EXP_SLTSTA_PFD; - if (slot_status) - pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, slot_status); + if (slot_status & PCI_EXP_SLTSTA_PFD) + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PFD); ctrl->power_fault_detected = 0; slot_cmd = POWER_ON; -- cgit v1.2.3-70-g09d2 From df72648c4d61d6d0ce033c0467a9fabec670fe46 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 14 Dec 2013 13:06:47 -0700 Subject: PCI: pciehp: Use symbolic constants, not hard-coded bitmask Use the PCI_EXP_SLTSTA definitions, not 0x1f, when clearing Slot Status bits. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index cb3100af7e8c..915bb35f9180 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -808,7 +808,10 @@ struct controller *pcie_init(struct pcie_device *dev) } /* Clear all remaining event bits in Slot Status register */ - pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, 0x1f); + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | + PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_CC); /* Disable software notification */ pcie_disable_notification(ctrl); -- cgit v1.2.3-70-g09d2 From e7b4f0d7841b188423b641cab71d20b1a05234e9 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 14 Dec 2013 13:06:53 -0700 Subject: PCI: pciehp: Use symbolic constants for Slot Control fields Add symbolic constants for the PCIe Slot Control indicator and power control fields defined by spec and use them instead of open-coded hex constants. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_hpc.c | 86 +++++++++++++--------------------------- include/uapi/linux/pci_regs.h | 8 ++++ 2 files changed, 36 insertions(+), 58 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 915bb35f9180..05d421cf935e 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -46,10 +46,6 @@ static inline struct pci_dev *ctrl_dev(struct controller *ctrl) return ctrl->pcie->port; } -/* Power Control Command */ -#define POWER_ON 0 -#define POWER_OFF PCI_EXP_SLTCTL_PCC - static irqreturn_t pcie_isr(int irq, void *dev_id); static void start_int_poll_timer(struct controller *ctrl, int sec); @@ -346,25 +342,19 @@ void pciehp_get_attention_status(struct slot *slot, u8 *status) struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; - u8 atten_led_state; pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); - atten_led_state = (slot_ctrl & PCI_EXP_SLTCTL_AIC) >> 6; - - switch (atten_led_state) { - case 0: - *status = 0xFF; /* Reserved */ - break; - case 1: + switch (slot_ctrl & PCI_EXP_SLTCTL_AIC) { + case PCI_EXP_SLTCTL_ATTN_IND_ON: *status = 1; /* On */ break; - case 2: + case PCI_EXP_SLTCTL_ATTN_IND_BLINK: *status = 2; /* Blink */ break; - case 3: + case PCI_EXP_SLTCTL_ATTN_IND_OFF: *status = 0; /* Off */ break; default: @@ -378,20 +368,17 @@ void pciehp_get_power_status(struct slot *slot, u8 *status) struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); u16 slot_ctrl; - u8 pwr_state; pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl); - pwr_state = (slot_ctrl & PCI_EXP_SLTCTL_PCC) >> 10; - - switch (pwr_state) { - case 0: - *status = 1; + switch (slot_ctrl & PCI_EXP_SLTCTL_PCC) { + case PCI_EXP_SLTCTL_PWR_ON: + *status = 1; /* On */ break; - case 1: - *status = 0; + case PCI_EXP_SLTCTL_PWR_OFF: + *status = 0; /* Off */ break; default: *status = 0xFF; @@ -430,72 +417,59 @@ void pciehp_set_attention_status(struct slot *slot, u8 value) { struct controller *ctrl = slot->ctrl; u16 slot_cmd; - u16 cmd_mask; - cmd_mask = PCI_EXP_SLTCTL_AIC; switch (value) { case 0 : /* turn off */ - slot_cmd = 0x00C0; + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF; break; case 1: /* turn on */ - slot_cmd = 0x0040; + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_ON; break; case 2: /* turn blink */ - slot_cmd = 0x0080; + slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_BLINK; break; default: return; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC); } void pciehp_green_led_on(struct slot *slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; - slot_cmd = 0x0100; - cmd_mask = PCI_EXP_SLTCTL_PIC; - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_ON); } void pciehp_green_led_off(struct slot *slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; - slot_cmd = 0x0300; - cmd_mask = PCI_EXP_SLTCTL_PIC; - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_OFF); } void pciehp_green_led_blink(struct slot *slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; - slot_cmd = 0x0200; - cmd_mask = PCI_EXP_SLTCTL_PIC; - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_IND_BLINK); } int pciehp_power_on_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); - u16 slot_cmd; - u16 cmd_mask; u16 slot_status; int retval; @@ -506,11 +480,10 @@ int pciehp_power_on_slot(struct slot * slot) PCI_EXP_SLTSTA_PFD); ctrl->power_fault_detected = 0; - slot_cmd = POWER_ON; - cmd_mask = PCI_EXP_SLTCTL_PCC; - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_ON, PCI_EXP_SLTCTL_PCC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_ON); retval = pciehp_link_enable(ctrl); if (retval) @@ -522,8 +495,6 @@ int pciehp_power_on_slot(struct slot * slot) void pciehp_power_off_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; - u16 slot_cmd; - u16 cmd_mask; /* Disable the link at first */ pciehp_link_disable(ctrl); @@ -533,11 +504,10 @@ void pciehp_power_off_slot(struct slot * slot) else msleep(1000); - slot_cmd = POWER_OFF; - cmd_mask = PCI_EXP_SLTCTL_PCC; - pcie_write_cmd(ctrl, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, - pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_OFF); } static irqreturn_t pcie_isr(int irq, void *dev_id) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 4a98e85438a7..6d03ba42ab23 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -518,8 +518,16 @@ #define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */ #define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ #define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */ +#define PCI_EXP_SLTCTL_ATTN_IND_ON 0x0040 /* Attention Indicator on */ +#define PCI_EXP_SLTCTL_ATTN_IND_BLINK 0x0080 /* Attention Indicator blinking */ +#define PCI_EXP_SLTCTL_ATTN_IND_OFF 0x00c0 /* Attention Indicator off */ #define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */ +#define PCI_EXP_SLTCTL_PWR_IND_ON 0x0100 /* Power Indicator on */ +#define PCI_EXP_SLTCTL_PWR_IND_BLINK 0x0200 /* Power Indicator blinking */ +#define PCI_EXP_SLTCTL_PWR_IND_OFF 0x0300 /* Power Indicator off */ #define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */ +#define PCI_EXP_SLTCTL_PWR_ON 0x0000 /* Power On */ +#define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */ #define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ #define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ #define PCI_EXP_SLTSTA 26 /* Slot Status */ -- cgit v1.2.3-70-g09d2 From af9ab791e34147952a8b97c998fa257d8292c5d6 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sun, 15 Dec 2013 17:23:54 -0700 Subject: PCI: pciehp: Move Attention & Power Indicator support tests to accessors Previously, the caller checked ATTN_LED() or PWR_LED() to see whether the slot has indicators before setting the indicator state. That clutters the caller unnecessarily, so this moves the test inside the callees. The test may not even be necessary; per spec it should be harmless to try to turn on a non-existent LED. But checking first does avoid unnecessary hotplug commands. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_ctrl.c | 42 ++++++++++++--------------------------- drivers/pci/hotplug/pciehp_hpc.c | 12 +++++++++++ 2 files changed, 25 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 6c730b161b33..50628487597d 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -168,11 +168,8 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) msleep(1000); } - if (PWR_LED(ctrl)) - pciehp_green_led_off(pslot); - - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(pslot, 1); + pciehp_green_led_off(pslot); + pciehp_set_attention_status(pslot, 1); } /** @@ -195,8 +192,7 @@ static int board_added(struct slot *p_slot) return retval; } - if (PWR_LED(ctrl)) - pciehp_green_led_blink(p_slot); + pciehp_green_led_blink(p_slot); /* Check link training status */ retval = pciehp_check_link_status(ctrl); @@ -219,9 +215,7 @@ static int board_added(struct slot *p_slot) goto err_exit; } - if (PWR_LED(ctrl)) - pciehp_green_led_on(p_slot); - + pciehp_green_led_on(p_slot); return 0; err_exit: @@ -254,9 +248,7 @@ static int remove_board(struct slot *p_slot) } /* turn off Green LED */ - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); - + pciehp_green_led_off(p_slot); return 0; } @@ -292,7 +284,7 @@ static void pciehp_power_thread(struct work_struct *work) break; case POWERON_STATE: mutex_unlock(&p_slot->lock); - if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl)) + if (pciehp_enable_slot(p_slot)) pciehp_green_led_off(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; @@ -359,11 +351,8 @@ static void handle_button_press_event(struct slot *p_slot) "press.\n", slot_name(p_slot)); } /* blink green LED and turn off amber */ - if (PWR_LED(ctrl)) - pciehp_green_led_blink(p_slot); - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 0); - + pciehp_green_led_blink(p_slot); + pciehp_set_attention_status(p_slot, 0); queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: @@ -376,14 +365,11 @@ static void handle_button_press_event(struct slot *p_slot) ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot)); cancel_delayed_work(&p_slot->work); if (p_slot->state == BLINKINGOFF_STATE) { - if (PWR_LED(ctrl)) - pciehp_green_led_on(p_slot); + pciehp_green_led_on(p_slot); } else { - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); + pciehp_green_led_off(p_slot); } - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 0); + pciehp_set_attention_status(p_slot, 0); ctrl_info(ctrl, "PCI slot #%s - action canceled " "due to button press\n", slot_name(p_slot)); p_slot->state = STATIC_STATE; @@ -443,10 +429,8 @@ static void interrupt_event_handler(struct work_struct *work) case INT_POWER_FAULT: if (!POWER_CTRL(ctrl)) break; - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 1); - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); + pciehp_set_attention_status(p_slot, 1); + pciehp_green_led_off(p_slot); break; case INT_PRESENCE_ON: case INT_PRESENCE_OFF: diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 05d421cf935e..14acfccb7670 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -418,6 +418,9 @@ void pciehp_set_attention_status(struct slot *slot, u8 value) struct controller *ctrl = slot->ctrl; u16 slot_cmd; + if (!ATTN_LED(ctrl)) + return; + switch (value) { case 0 : /* turn off */ slot_cmd = PCI_EXP_SLTCTL_ATTN_IND_OFF; @@ -440,6 +443,9 @@ void pciehp_green_led_on(struct slot *slot) { struct controller *ctrl = slot->ctrl; + if (!PWR_LED(ctrl)) + return; + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, @@ -450,6 +456,9 @@ void pciehp_green_led_off(struct slot *slot) { struct controller *ctrl = slot->ctrl; + if (!PWR_LED(ctrl)) + return; + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, @@ -460,6 +469,9 @@ void pciehp_green_led_blink(struct slot *slot) { struct controller *ctrl = slot->ctrl; + if (!PWR_LED(ctrl)) + return; + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PIC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, -- cgit v1.2.3-70-g09d2 From 157e876ffe0b28821a0d82d8ac944fe7363bbe87 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 17 Dec 2013 16:43:39 -0700 Subject: PCI: Add pci_wait_for_pending() (refactor pci_wait_for_pending_transaction()) We currently have two instance of this loop which waits for a pending bit to clear in a status dword. Generalize the function for future users. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 54 ++++++++++++++++++++++++++++++----------------------- include/linux/pci.h | 1 + 2 files changed, 32 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 33120d156668..bb257b975a8f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -430,6 +430,32 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) return best; } +/** + * pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos + * @dev: the PCI device to operate on + * @pos: config space offset of status word + * @mask: mask of bit(s) to care about in status word + * + * Return 1 when mask bit(s) in status word clear, 0 otherwise. + */ +int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask) +{ + int i; + + /* Wait for Transaction Pending bit clean */ + for (i = 0; i < 4; i++) { + u16 status; + if (i) + msleep((1 << (i - 1)) * 100); + + pci_read_config_word(dev, pos, &status); + if (!(status & mask)) + return 1; + } + + return 0; +} + /** * pci_restore_bars - restore a devices BAR values (e.g. after wake-up) * @dev: PCI device to have its BARs restored @@ -3204,20 +3230,10 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary); */ int pci_wait_for_pending_transaction(struct pci_dev *dev) { - int i; - u16 status; - - /* Wait for Transaction Pending bit clean */ - for (i = 0; i < 4; i++) { - if (i) - msleep((1 << (i - 1)) * 100); - - pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status); - if (!(status & PCI_EXP_DEVSTA_TRPND)) - return 1; - } + if (!pci_is_pcie(dev)) + return 1; - return 0; + return pci_wait_for_pending(dev, PCI_EXP_DEVSTA, PCI_EXP_DEVSTA_TRPND); } EXPORT_SYMBOL(pci_wait_for_pending_transaction); @@ -3244,10 +3260,8 @@ static int pcie_flr(struct pci_dev *dev, int probe) static int pci_af_flr(struct pci_dev *dev, int probe) { - int i; int pos; u8 cap; - u8 status; pos = pci_find_capability(dev, PCI_CAP_ID_AF); if (!pos) @@ -3261,14 +3275,8 @@ static int pci_af_flr(struct pci_dev *dev, int probe) return 0; /* Wait for Transaction Pending bit clean */ - for (i = 0; i < 4; i++) { - if (i) - msleep((1 << (i - 1)) * 100); - - pci_read_config_byte(dev, pos + PCI_AF_STATUS, &status); - if (!(status & PCI_AF_STATUS_TP)) - goto clear; - } + if (pci_wait_for_pending(dev, PCI_AF_STATUS, PCI_AF_STATUS_TP)) + goto clear; dev_err(&dev->dev, "transaction is not cleared; " "proceeding with reset anyway\n"); diff --git a/include/linux/pci.h b/include/linux/pci.h index 1084a15175e0..211ce43ba483 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -938,6 +938,7 @@ bool pci_check_and_unmask_intx(struct pci_dev *dev); void pci_msi_off(struct pci_dev *dev); int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size); int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask); +int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask); int pci_wait_for_pending_transaction(struct pci_dev *dev); int pcix_get_max_mmrbc(struct pci_dev *dev); int pcix_get_mmrbc(struct pci_dev *dev); -- cgit v1.2.3-70-g09d2 From fd0f7f73ca96bb0f8723b5e59759ad43bab88954 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 17 Dec 2013 16:43:45 -0700 Subject: PCI: Add support for save/restore of extended capabilities Current save/restore is specific to standard capabilities. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 43 +++++++++++++++++++++++++++++++++++-------- include/linux/pci.h | 9 ++++++++- 2 files changed, 43 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bb257b975a8f..8f2be7ec705c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -861,18 +861,28 @@ EXPORT_SYMBOL(pci_choose_state); #define PCI_EXP_SAVE_REGS 7 -static struct pci_cap_saved_state *pci_find_saved_cap( - struct pci_dev *pci_dev, char cap) +static struct pci_cap_saved_state *_pci_find_saved_cap(struct pci_dev *pci_dev, + u16 cap, bool extended) { struct pci_cap_saved_state *tmp; hlist_for_each_entry(tmp, &pci_dev->saved_cap_space, next) { - if (tmp->cap.cap_nr == cap) + if (tmp->cap.cap_extended == extended && tmp->cap.cap_nr == cap) return tmp; } return NULL; } +struct pci_cap_saved_state *pci_find_saved_cap(struct pci_dev *dev, char cap) +{ + return _pci_find_saved_cap(dev, cap, false); +} + +struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev, u16 cap) +{ + return _pci_find_saved_cap(dev, cap, true); +} + static int pci_save_pcie_state(struct pci_dev *dev) { int i = 0; @@ -1113,7 +1123,7 @@ int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state) while (cap->size) { struct pci_cap_saved_state *tmp; - tmp = pci_find_saved_cap(dev, cap->cap_nr); + tmp = _pci_find_saved_cap(dev, cap->cap_nr, cap->cap_extended); if (!tmp || tmp->cap.size != cap->size) return -EINVAL; @@ -2047,18 +2057,24 @@ static void pci_add_saved_cap(struct pci_dev *pci_dev, } /** - * pci_add_cap_save_buffer - allocate buffer for saving given capability registers + * _pci_add_cap_save_buffer - allocate buffer for saving given + * capability registers * @dev: the PCI device * @cap: the capability to allocate the buffer for + * @extended: Standard or Extended capability ID * @size: requested size of the buffer */ -static int pci_add_cap_save_buffer( - struct pci_dev *dev, char cap, unsigned int size) +static int _pci_add_cap_save_buffer(struct pci_dev *dev, u16 cap, + bool extended, unsigned int size) { int pos; struct pci_cap_saved_state *save_state; - pos = pci_find_capability(dev, cap); + if (extended) + pos = pci_find_ext_capability(dev, cap); + else + pos = pci_find_capability(dev, cap); + if (pos <= 0) return 0; @@ -2067,12 +2083,23 @@ static int pci_add_cap_save_buffer( return -ENOMEM; save_state->cap.cap_nr = cap; + save_state->cap.cap_extended = extended; save_state->cap.size = size; pci_add_saved_cap(dev, save_state); return 0; } +int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size) +{ + return _pci_add_cap_save_buffer(dev, cap, false, size); +} + +int pci_add_ext_cap_save_buffer(struct pci_dev *dev, u16 cap, unsigned int size) +{ + return _pci_add_cap_save_buffer(dev, cap, true, size); +} + /** * pci_allocate_cap_save_buffers - allocate buffers for saving capabilities * @dev: the PCI device diff --git a/include/linux/pci.h b/include/linux/pci.h index 211ce43ba483..1bb75b0c4c6f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -224,7 +224,8 @@ enum pci_bus_speed { }; struct pci_cap_saved_data { - char cap_nr; + u16 cap_nr; + bool cap_extended; unsigned int size; u32 data[0]; }; @@ -977,6 +978,12 @@ struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev); int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state); int pci_load_and_free_saved_state(struct pci_dev *dev, struct pci_saved_state **state); +struct pci_cap_saved_state *pci_find_saved_cap(struct pci_dev *dev, char cap); +struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev, + u16 cap); +int pci_add_cap_save_buffer(struct pci_dev *dev, char cap, unsigned int size); +int pci_add_ext_cap_save_buffer(struct pci_dev *dev, + u16 cap, unsigned int size); int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state); int pci_set_power_state(struct pci_dev *dev, pci_power_t state); pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); -- cgit v1.2.3-70-g09d2 From 425c1b223dac456d00a61fd6b451b6d1cf00d065 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 17 Dec 2013 16:43:51 -0700 Subject: PCI: Add Virtual Channel to save/restore support While we don't really have any infrastructure for making use of VC support, the system BIOS can configure the topology to non-default VC values prior to boot. This may be due to silicon bugs, desire to reserve traffic classes, or perhaps just BIOS bugs. When we reset devices, the VC configuration may return to default values, which can be incompatible with devices upstream. For instance, Nvidia GRID cards provide a PCIe switch and some number of GPUs, all supporting VC. The power-on default for VC is to support TC0-7 across VC0, however some platforms will only enable TC0/VC0 mapping across the topology. When we do a secondary bus reset on the downstream switch port, the GPU is reset to a TC0-7/VC0 mapping while the opposite end of the link only enables TC0/VC0. If the GPU attempts to use TC1-7, it fails. This patch attempts to provide complete support for VC save/restore, even beyond the minimally required use case above. This includes save/restore and reload of the arbitration table, save/restore and reload of the port arbitration tables, and re-enabling of the channels for VC, VC9, and MFVC capabilities. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/Makefile | 2 +- drivers/pci/pci.c | 5 + drivers/pci/vc.c | 434 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 5 + include/uapi/linux/pci_regs.h | 25 ++- 5 files changed, 466 insertions(+), 5 deletions(-) create mode 100644 drivers/pci/vc.c (limited to 'drivers') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6ebf5bf8e7a7..17d2b07ee67c 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -4,7 +4,7 @@ obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ - irq.o vpd.o setup-bus.o + irq.o vpd.o setup-bus.o vc.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSFS) += slot.o diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f2be7ec705c..508e560b7d2a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -984,6 +984,8 @@ pci_save_state(struct pci_dev *dev) return i; if ((i = pci_save_pcix_state(dev)) != 0) return i; + if ((i = pci_save_vc_state(dev)) != 0) + return i; return 0; } @@ -1046,6 +1048,7 @@ void pci_restore_state(struct pci_dev *dev) /* PCI Express register must be restored first */ pci_restore_pcie_state(dev); pci_restore_ats_state(dev); + pci_restore_vc_state(dev); pci_restore_config_space(dev); @@ -2118,6 +2121,8 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) if (error) dev_err(&dev->dev, "unable to preallocate PCI-X save buffer\n"); + + pci_allocate_vc_save_buffers(dev); } void pci_free_cap_save_buffers(struct pci_dev *dev) diff --git a/drivers/pci/vc.c b/drivers/pci/vc.c new file mode 100644 index 000000000000..c10567e4f14f --- /dev/null +++ b/drivers/pci/vc.c @@ -0,0 +1,434 @@ +/* + * PCI Virtual Channel support + * + * Copyright (C) 2013 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +/** + * pci_vc_save_restore_dwords - Save or restore a series of dwords + * @dev: device + * @pos: starting config space position + * @buf: buffer to save to or restore from + * @dwords: number of dwords to save/restore + * @save: whether to save or restore + */ +static void pci_vc_save_restore_dwords(struct pci_dev *dev, int pos, + u32 *buf, int dwords, bool save) +{ + int i; + + for (i = 0; i < dwords; i++, buf++) { + if (save) + pci_read_config_dword(dev, pos + (i * 4), buf); + else + pci_write_config_dword(dev, pos + (i * 4), *buf); + } +} + +/** + * pci_vc_load_arb_table - load and wait for VC arbitration table + * @dev: device + * @pos: starting position of VC capability (VC/VC9/MFVC) + * + * Set Load VC Arbitration Table bit requesting hardware to apply the VC + * Arbitration Table (previously loaded). When the VC Arbitration Table + * Status clears, hardware has latched the table into VC arbitration logic. + */ +static void pci_vc_load_arb_table(struct pci_dev *dev, int pos) +{ + u16 ctrl; + + pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL, &ctrl); + pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL, + ctrl | PCI_VC_PORT_CTRL_LOAD_TABLE); + if (pci_wait_for_pending(dev, pos + PCI_VC_PORT_STATUS, + PCI_VC_PORT_STATUS_TABLE)) + return; + + dev_err(&dev->dev, "VC arbitration table failed to load\n"); +} + +/** + * pci_vc_load_port_arb_table - Load and wait for VC port arbitration table + * @dev: device + * @pos: starting position of VC capability (VC/VC9/MFVC) + * @res: VC resource number, ie. VCn (0-7) + * + * Set Load Port Arbitration Table bit requesting hardware to apply the Port + * Arbitration Table (previously loaded). When the Port Arbitration Table + * Status clears, hardware has latched the table into port arbitration logic. + */ +static void pci_vc_load_port_arb_table(struct pci_dev *dev, int pos, int res) +{ + int ctrl_pos, status_pos; + u32 ctrl; + + ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF); + status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF); + + pci_read_config_dword(dev, ctrl_pos, &ctrl); + pci_write_config_dword(dev, ctrl_pos, + ctrl | PCI_VC_RES_CTRL_LOAD_TABLE); + + if (pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_TABLE)) + return; + + dev_err(&dev->dev, "VC%d port arbitration table failed to load\n", res); +} + +/** + * pci_vc_enable - Enable virtual channel + * @dev: device + * @pos: starting position of VC capability (VC/VC9/MFVC) + * @res: VC res number, ie. VCn (0-7) + * + * A VC is enabled by setting the enable bit in matching resource control + * registers on both sides of a link. We therefore need to find the opposite + * end of the link. To keep this simple we enable from the downstream device. + * RC devices do not have an upstream device, nor does it seem that VC9 do + * (spec is unclear). Once we find the upstream device, match the VC ID to + * get the correct resource, disable and enable on both ends. + */ +static void pci_vc_enable(struct pci_dev *dev, int pos, int res) +{ + int ctrl_pos, status_pos, id, pos2, evcc, i, ctrl_pos2, status_pos2; + u32 ctrl, header, reg1, ctrl2; + struct pci_dev *link = NULL; + + /* Enable VCs from the downstream device */ + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) + return; + + ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF); + status_pos = pos + PCI_VC_RES_STATUS + (res * PCI_CAP_VC_PER_VC_SIZEOF); + + pci_read_config_dword(dev, ctrl_pos, &ctrl); + id = ctrl & PCI_VC_RES_CTRL_ID; + + pci_read_config_dword(dev, pos, &header); + + /* If there is no opposite end of the link, skip to enable */ + if (PCI_EXT_CAP_ID(header) == PCI_EXT_CAP_ID_VC9 || + pci_is_root_bus(dev->bus)) + goto enable; + + pos2 = pci_find_ext_capability(dev->bus->self, PCI_EXT_CAP_ID_VC); + if (!pos2) + goto enable; + + pci_read_config_dword(dev->bus->self, pos2 + PCI_VC_PORT_REG1, ®1); + evcc = reg1 & PCI_VC_REG1_EVCC; + + /* VC0 is hardwired enabled, so we can start with 1 */ + for (i = 1; i < evcc + 1; i++) { + ctrl_pos2 = pos2 + PCI_VC_RES_CTRL + + (i * PCI_CAP_VC_PER_VC_SIZEOF); + status_pos2 = pos2 + PCI_VC_RES_STATUS + + (i * PCI_CAP_VC_PER_VC_SIZEOF); + pci_read_config_dword(dev->bus->self, ctrl_pos2, &ctrl2); + if ((ctrl2 & PCI_VC_RES_CTRL_ID) == id) { + link = dev->bus->self; + break; + } + } + + if (!link) + goto enable; + + /* Disable if enabled */ + if (ctrl2 & PCI_VC_RES_CTRL_ENABLE) { + ctrl2 &= ~PCI_VC_RES_CTRL_ENABLE; + pci_write_config_dword(link, ctrl_pos2, ctrl2); + } + + /* Enable on both ends */ + ctrl2 |= PCI_VC_RES_CTRL_ENABLE; + pci_write_config_dword(link, ctrl_pos2, ctrl2); +enable: + ctrl |= PCI_VC_RES_CTRL_ENABLE; + pci_write_config_dword(dev, ctrl_pos, ctrl); + + if (!pci_wait_for_pending(dev, status_pos, PCI_VC_RES_STATUS_NEGO)) + dev_err(&dev->dev, "VC%d negotiation stuck pending\n", id); + + if (link && !pci_wait_for_pending(link, status_pos2, + PCI_VC_RES_STATUS_NEGO)) + dev_err(&link->dev, "VC%d negotiation stuck pending\n", id); +} + +/** + * pci_vc_do_save_buffer - Size, save, or restore VC state + * @dev: device + * @pos: starting position of VC capability (VC/VC9/MFVC) + * @save_state: buffer for save/restore + * @name: for error message + * @save: if provided a buffer, this indicates what to do with it + * + * Walking Virtual Channel config space to size, save, or restore it + * is complicated, so we do it all from one function to reduce code and + * guarantee ordering matches in the buffer. When called with NULL + * @save_state, return the size of the necessary save buffer. When called + * with a non-NULL @save_state, @save determines whether we save to the + * buffer or restore from it. + */ +static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos, + struct pci_cap_saved_state *save_state, + bool save) +{ + u32 reg1; + char evcc, lpevcc, parb_size; + int i, len = 0; + u8 *buf = save_state ? (u8 *)save_state->cap.data : NULL; + + /* Sanity check buffer size for save/restore */ + if (buf && save_state->cap.size != + pci_vc_do_save_buffer(dev, pos, NULL, save)) { + dev_err(&dev->dev, + "VC save buffer size does not match @0x%x\n", pos); + return -ENOMEM; + } + + pci_read_config_dword(dev, pos + PCI_VC_PORT_REG1, ®1); + /* Extended VC Count (not counting VC0) */ + evcc = reg1 & PCI_VC_REG1_EVCC; + /* Low Priority Extended VC Count (not counting VC0) */ + lpevcc = (reg1 & PCI_VC_REG1_LPEVCC) >> 4; + /* Port Arbitration Table Entry Size (bits) */ + parb_size = 1 << ((reg1 & PCI_VC_REG1_ARB_SIZE) >> 10); + + /* + * Port VC Control Register contains VC Arbitration Select, which + * cannot be modified when more than one LPVC is in operation. We + * therefore save/restore it first, as only VC0 should be enabled + * after device reset. + */ + if (buf) { + if (save) + pci_read_config_word(dev, pos + PCI_VC_PORT_CTRL, + (u16 *)buf); + else + pci_write_config_word(dev, pos + PCI_VC_PORT_CTRL, + *(u16 *)buf); + buf += 2; + } + len += 2; + + /* + * If we have any Low Priority VCs and a VC Arbitration Table Offset + * in Port VC Capability Register 2 then save/restore it next. + */ + if (lpevcc) { + u32 reg2; + int vcarb_offset; + + pci_read_config_dword(dev, pos + PCI_VC_PORT_REG2, ®2); + vcarb_offset = ((reg2 & PCI_VC_REG2_ARB_OFF) >> 24) * 16; + + if (vcarb_offset) { + int size, vcarb_phases = 0; + + if (reg2 & PCI_VC_REG2_128_PHASE) + vcarb_phases = 128; + else if (reg2 & PCI_VC_REG2_64_PHASE) + vcarb_phases = 64; + else if (reg2 & PCI_VC_REG2_32_PHASE) + vcarb_phases = 32; + + /* Fixed 4 bits per phase per lpevcc (plus VC0) */ + size = ((lpevcc + 1) * vcarb_phases * 4) / 8; + + if (size && buf) { + pci_vc_save_restore_dwords(dev, + pos + vcarb_offset, + (u32 *)buf, + size / 4, save); + /* + * On restore, we need to signal hardware to + * re-load the VC Arbitration Table. + */ + if (!save) + pci_vc_load_arb_table(dev, pos); + + buf += size; + } + len += size; + } + } + + /* + * In addition to each VC Resource Control Register, we may have a + * Port Arbitration Table attached to each VC. The Port Arbitration + * Table Offset in each VC Resource Capability Register tells us if + * it exists. The entry size is global from the Port VC Capability + * Register1 above. The number of phases is determined per VC. + */ + for (i = 0; i < evcc + 1; i++) { + u32 cap; + int parb_offset; + + pci_read_config_dword(dev, pos + PCI_VC_RES_CAP + + (i * PCI_CAP_VC_PER_VC_SIZEOF), &cap); + parb_offset = ((cap & PCI_VC_RES_CAP_ARB_OFF) >> 24) * 16; + if (parb_offset) { + int size, parb_phases = 0; + + if (cap & PCI_VC_RES_CAP_256_PHASE) + parb_phases = 256; + else if (cap & (PCI_VC_RES_CAP_128_PHASE | + PCI_VC_RES_CAP_128_PHASE_TB)) + parb_phases = 128; + else if (cap & PCI_VC_RES_CAP_64_PHASE) + parb_phases = 64; + else if (cap & PCI_VC_RES_CAP_32_PHASE) + parb_phases = 32; + + size = (parb_size * parb_phases) / 8; + + if (size && buf) { + pci_vc_save_restore_dwords(dev, + pos + parb_offset, + (u32 *)buf, + size / 4, save); + buf += size; + } + len += size; + } + + /* VC Resource Control Register */ + if (buf) { + int ctrl_pos = pos + PCI_VC_RES_CTRL + + (i * PCI_CAP_VC_PER_VC_SIZEOF); + if (save) + pci_read_config_dword(dev, ctrl_pos, + (u32 *)buf); + else { + u32 tmp, ctrl = *(u32 *)buf; + /* + * For an FLR case, the VC config may remain. + * Preserve enable bit, restore the rest. + */ + pci_read_config_dword(dev, ctrl_pos, &tmp); + tmp &= PCI_VC_RES_CTRL_ENABLE; + tmp |= ctrl & ~PCI_VC_RES_CTRL_ENABLE; + pci_write_config_dword(dev, ctrl_pos, tmp); + /* Load port arbitration table if used */ + if (ctrl & PCI_VC_RES_CTRL_ARB_SELECT) + pci_vc_load_port_arb_table(dev, pos, i); + /* Re-enable if needed */ + if ((ctrl ^ tmp) & PCI_VC_RES_CTRL_ENABLE) + pci_vc_enable(dev, pos, i); + } + buf += 4; + } + len += 4; + } + + return buf ? 0 : len; +} + +static struct { + u16 id; + const char *name; +} vc_caps[] = { { PCI_EXT_CAP_ID_MFVC, "MFVC" }, + { PCI_EXT_CAP_ID_VC, "VC" }, + { PCI_EXT_CAP_ID_VC9, "VC9" } }; + +/** + * pci_save_vc_state - Save VC state to pre-allocate save buffer + * @dev: device + * + * For each type of VC capability, VC/VC9/MFVC, find the capability and + * save it to the pre-allocated save buffer. + */ +int pci_save_vc_state(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { + int pos, ret; + struct pci_cap_saved_state *save_state; + + pos = pci_find_ext_capability(dev, vc_caps[i].id); + if (!pos) + continue; + + save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id); + if (!save_state) { + dev_err(&dev->dev, "%s buffer not found in %s\n", + vc_caps[i].name, __func__); + return -ENOMEM; + } + + ret = pci_vc_do_save_buffer(dev, pos, save_state, true); + if (ret) { + dev_err(&dev->dev, "%s save unsuccessful %s\n", + vc_caps[i].name, __func__); + return ret; + } + } + + return 0; +} + +/** + * pci_restore_vc_state - Restore VC state from save buffer + * @dev: device + * + * For each type of VC capability, VC/VC9/MFVC, find the capability and + * restore it from the previously saved buffer. + */ +void pci_restore_vc_state(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { + int pos; + struct pci_cap_saved_state *save_state; + + pos = pci_find_ext_capability(dev, vc_caps[i].id); + save_state = pci_find_saved_ext_cap(dev, vc_caps[i].id); + if (!save_state || !pos) + continue; + + pci_vc_do_save_buffer(dev, pos, save_state, false); + } +} + +/** + * pci_allocate_vc_save_buffers - Allocate save buffers for VC caps + * @dev: device + * + * For each type of VC capability, VC/VC9/MFVC, find the capability, size + * it, and allocate a buffer for save/restore. + */ + +void pci_allocate_vc_save_buffers(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vc_caps); i++) { + int len, pos = pci_find_ext_capability(dev, vc_caps[i].id); + + if (!pos) + continue; + + len = pci_vc_do_save_buffer(dev, pos, NULL, false); + if (pci_add_ext_cap_save_buffer(dev, vc_caps[i].id, len)) + dev_err(&dev->dev, + "unable to preallocate %s save buffer\n", + vc_caps[i].name); + } +} diff --git a/include/linux/pci.h b/include/linux/pci.h index 1bb75b0c4c6f..ef34ad76532c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1005,6 +1005,11 @@ static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, return __pci_enable_wake(dev, state, false, enable); } +/* PCI Virtual Channel */ +int pci_save_vc_state(struct pci_dev *dev); +void pci_restore_vc_state(struct pci_dev *dev); +void pci_allocate_vc_save_buffers(struct pci_dev *dev); + #define PCI_EXP_IDO_REQUEST (1<<0) #define PCI_EXP_IDO_COMPLETION (1<<1) void pci_enable_ido(struct pci_dev *dev, unsigned long type); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 4a98e85438a7..5eefacd93e18 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -678,16 +678,33 @@ /* Virtual Channel */ #define PCI_VC_PORT_REG1 4 -#define PCI_VC_REG1_EVCC 0x7 /* extended VC count */ +#define PCI_VC_REG1_EVCC 0x00000007 /* extended VC count */ +#define PCI_VC_REG1_LPEVCC 0x00000070 /* low prio extended VC count */ +#define PCI_VC_REG1_ARB_SIZE 0x00000c00 #define PCI_VC_PORT_REG2 8 -#define PCI_VC_REG2_32_PHASE 0x2 -#define PCI_VC_REG2_64_PHASE 0x4 -#define PCI_VC_REG2_128_PHASE 0x8 +#define PCI_VC_REG2_32_PHASE 0x00000002 +#define PCI_VC_REG2_64_PHASE 0x00000004 +#define PCI_VC_REG2_128_PHASE 0x00000008 +#define PCI_VC_REG2_ARB_OFF 0xff000000 #define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_CTRL_LOAD_TABLE 0x00000001 #define PCI_VC_PORT_STATUS 14 +#define PCI_VC_PORT_STATUS_TABLE 0x00000001 #define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CAP_32_PHASE 0x00000002 +#define PCI_VC_RES_CAP_64_PHASE 0x00000004 +#define PCI_VC_RES_CAP_128_PHASE 0x00000008 +#define PCI_VC_RES_CAP_128_PHASE_TB 0x00000010 +#define PCI_VC_RES_CAP_256_PHASE 0x00000020 +#define PCI_VC_RES_CAP_ARB_OFF 0xff000000 #define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_CTRL_LOAD_TABLE 0x00010000 +#define PCI_VC_RES_CTRL_ARB_SELECT 0x000e0000 +#define PCI_VC_RES_CTRL_ID 0x07000000 +#define PCI_VC_RES_CTRL_ENABLE 0x80000000 #define PCI_VC_RES_STATUS 26 +#define PCI_VC_RES_STATUS_TABLE 0x00000001 +#define PCI_VC_RES_STATUS_NEGO 0x00000002 #define PCI_CAP_VC_BASE_SIZEOF 0x10 #define PCI_CAP_VC_PER_VC_SIZEOF 0x0C -- cgit v1.2.3-70-g09d2 From 274127a1fdbad3c0d64e813521f4a0ef96cfc70e Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 17 Dec 2013 16:43:57 -0700 Subject: PCI: Rename PCI_VC_PORT_REG1/2 to PCI_VC_PORT_CAP1/2 These are set of two capability registers, it's pretty much given that they're registers, so reflect their purpose in the name. Suggested-by: Bjorn Helgaas Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/vc.c | 28 ++++++++++++++-------------- drivers/vfio/pci/vfio_pci_config.c | 12 ++++++------ include/uapi/linux/pci_regs.h | 18 +++++++++--------- 3 files changed, 29 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/vc.c b/drivers/pci/vc.c index c10567e4f14f..7e1304d2e389 100644 --- a/drivers/pci/vc.c +++ b/drivers/pci/vc.c @@ -104,7 +104,7 @@ static void pci_vc_load_port_arb_table(struct pci_dev *dev, int pos, int res) static void pci_vc_enable(struct pci_dev *dev, int pos, int res) { int ctrl_pos, status_pos, id, pos2, evcc, i, ctrl_pos2, status_pos2; - u32 ctrl, header, reg1, ctrl2; + u32 ctrl, header, cap1, ctrl2; struct pci_dev *link = NULL; /* Enable VCs from the downstream device */ @@ -129,8 +129,8 @@ static void pci_vc_enable(struct pci_dev *dev, int pos, int res) if (!pos2) goto enable; - pci_read_config_dword(dev->bus->self, pos2 + PCI_VC_PORT_REG1, ®1); - evcc = reg1 & PCI_VC_REG1_EVCC; + pci_read_config_dword(dev->bus->self, pos2 + PCI_VC_PORT_CAP1, &cap1); + evcc = cap1 & PCI_VC_CAP1_EVCC; /* VC0 is hardwired enabled, so we can start with 1 */ for (i = 1; i < evcc + 1; i++) { @@ -188,7 +188,7 @@ static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos, struct pci_cap_saved_state *save_state, bool save) { - u32 reg1; + u32 cap1; char evcc, lpevcc, parb_size; int i, len = 0; u8 *buf = save_state ? (u8 *)save_state->cap.data : NULL; @@ -201,13 +201,13 @@ static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos, return -ENOMEM; } - pci_read_config_dword(dev, pos + PCI_VC_PORT_REG1, ®1); + pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP1, &cap1); /* Extended VC Count (not counting VC0) */ - evcc = reg1 & PCI_VC_REG1_EVCC; + evcc = cap1 & PCI_VC_CAP1_EVCC; /* Low Priority Extended VC Count (not counting VC0) */ - lpevcc = (reg1 & PCI_VC_REG1_LPEVCC) >> 4; + lpevcc = (cap1 & PCI_VC_CAP1_LPEVCC) >> 4; /* Port Arbitration Table Entry Size (bits) */ - parb_size = 1 << ((reg1 & PCI_VC_REG1_ARB_SIZE) >> 10); + parb_size = 1 << ((cap1 & PCI_VC_CAP1_ARB_SIZE) >> 10); /* * Port VC Control Register contains VC Arbitration Select, which @@ -231,20 +231,20 @@ static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos, * in Port VC Capability Register 2 then save/restore it next. */ if (lpevcc) { - u32 reg2; + u32 cap2; int vcarb_offset; - pci_read_config_dword(dev, pos + PCI_VC_PORT_REG2, ®2); - vcarb_offset = ((reg2 & PCI_VC_REG2_ARB_OFF) >> 24) * 16; + pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP2, &cap2); + vcarb_offset = ((cap2 & PCI_VC_CAP2_ARB_OFF) >> 24) * 16; if (vcarb_offset) { int size, vcarb_phases = 0; - if (reg2 & PCI_VC_REG2_128_PHASE) + if (cap2 & PCI_VC_CAP2_128_PHASE) vcarb_phases = 128; - else if (reg2 & PCI_VC_REG2_64_PHASE) + else if (cap2 & PCI_VC_CAP2_64_PHASE) vcarb_phases = 64; - else if (reg2 & PCI_VC_REG2_32_PHASE) + else if (cap2 & PCI_VC_CAP2_32_PHASE) vcarb_phases = 32; /* Fixed 4 bits per phase per lpevcc (plus VC0) */ diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index ffd0632c3cbc..83cd1574c810 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -975,20 +975,20 @@ static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos) int ret, evcc, phases, vc_arb; int len = PCI_CAP_VC_BASE_SIZEOF; - ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp); + ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_CAP1, &tmp); if (ret) return pcibios_err_to_errno(ret); - evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */ - ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp); + evcc = tmp & PCI_VC_CAP1_EVCC; /* extended vc count */ + ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_CAP2, &tmp); if (ret) return pcibios_err_to_errno(ret); - if (tmp & PCI_VC_REG2_128_PHASE) + if (tmp & PCI_VC_CAP2_128_PHASE) phases = 128; - else if (tmp & PCI_VC_REG2_64_PHASE) + else if (tmp & PCI_VC_CAP2_64_PHASE) phases = 64; - else if (tmp & PCI_VC_REG2_32_PHASE) + else if (tmp & PCI_VC_CAP2_32_PHASE) phases = 32; else phases = 0; diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 5eefacd93e18..d0160cc83fcf 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -677,15 +677,15 @@ #define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ /* Virtual Channel */ -#define PCI_VC_PORT_REG1 4 -#define PCI_VC_REG1_EVCC 0x00000007 /* extended VC count */ -#define PCI_VC_REG1_LPEVCC 0x00000070 /* low prio extended VC count */ -#define PCI_VC_REG1_ARB_SIZE 0x00000c00 -#define PCI_VC_PORT_REG2 8 -#define PCI_VC_REG2_32_PHASE 0x00000002 -#define PCI_VC_REG2_64_PHASE 0x00000004 -#define PCI_VC_REG2_128_PHASE 0x00000008 -#define PCI_VC_REG2_ARB_OFF 0xff000000 +#define PCI_VC_PORT_CAP1 4 +#define PCI_VC_CAP1_EVCC 0x00000007 /* extended VC count */ +#define PCI_VC_CAP1_LPEVCC 0x00000070 /* low prio extended VC count */ +#define PCI_VC_CAP1_ARB_SIZE 0x00000c00 +#define PCI_VC_PORT_CAP2 8 +#define PCI_VC_CAP2_32_PHASE 0x00000002 +#define PCI_VC_CAP2_64_PHASE 0x00000004 +#define PCI_VC_CAP2_128_PHASE 0x00000008 +#define PCI_VC_CAP2_ARB_OFF 0xff000000 #define PCI_VC_PORT_CTRL 12 #define PCI_VC_PORT_CTRL_LOAD_TABLE 0x00000001 #define PCI_VC_PORT_STATUS 14 -- cgit v1.2.3-70-g09d2 From c4a0a5d964e90b93eb4101c3927b788e083e530f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 18 Dec 2013 13:53:32 -0700 Subject: PCI: Move device_del() from pci_stop_dev() to pci_destroy_dev() After commit bcdde7e221a8 (sysfs: make __sysfs_remove_dir() recursive) I'm seeing traces analogous to the one below in Thunderbolt testing: WARNING: CPU: 3 PID: 76 at /scratch/rafael/work/linux-pm/fs/sysfs/group.c:214 sysfs_remove_group+0x59/0xe0() sysfs group ffffffff81c6c500 not found for kobject '0000:08' Modules linked in: ... CPU: 3 PID: 76 Comm: kworker/u16:7 Not tainted 3.13.0-rc1+ #76 Hardware name: Acer Aspire S5-391/Venus , BIOS V1.02 05/29/2012 Workqueue: kacpi_hotplug acpi_hotplug_work_fn 0000000000000009 ffff8801644b9ac8 ffffffff816b23bf 0000000000000007 ffff8801644b9b18 ffff8801644b9b08 ffffffff81046607 ffff88016925b800 0000000000000000 ffffffff81c6c500 ffff88016924f928 ffff88016924f800 Call Trace: [] dump_stack+0x4e/0x71 [] warn_slowpath_common+0x87/0xb0 [] warn_slowpath_fmt+0x41/0x50 [] ? sysfs_get_dirent_ns+0x6f/0x80 [] sysfs_remove_group+0x59/0xe0 [] dpm_sysfs_remove+0x3b/0x50 [] device_del+0x58/0x1c0 [] device_unregister+0x48/0x60 [] pci_remove_bus+0x6e/0x80 [] pci_remove_bus_device+0x38/0x110 [] pci_remove_bus_device+0x4d/0x110 [] pci_stop_and_remove_bus_device+0x19/0x20 [] disable_slot+0x20/0xe0 [] acpiphp_check_bridge+0xa8/0xd0 [] hotplug_event+0x17d/0x220 [] hotplug_event_work+0x30/0x70 [] acpi_hotplug_work_fn+0x18/0x24 [] process_one_work+0x261/0x450 [] worker_thread+0x21e/0x370 [] ? rescuer_thread+0x300/0x300 [] kthread+0xd2/0xe0 [] ? flush_kthread_worker+0x70/0x70 [] ret_from_fork+0x7c/0xb0 [] ? flush_kthread_worker+0x70/0x70 (Mika Westerberg sees them too in his tests). Some investigation documented in kernel bug #65281 led me to the conclusion that the source of the problem is the device_del() in pci_stop_dev() as it now causes the sysfs directory of the device to be removed recursively along with all of its subdirectories. That includes the sysfs directory of the device's subordinate bus (dev->subordinate) and its "power" group. Consequently, when pci_remove_bus() is called for dev->subordinate in pci_remove_bus_device(), it calls device_unregister(&bus->dev), but at this point the sysfs directory of bus->dev doesn't exist any more and its "power" group doesn't exist either. Thus, when dpm_sysfs_remove() called from device_del() tries to remove that group, it triggers the above warning. That indicates a logical mistake in the design of pci_stop_and_remove_bus_device(), which causes bus device objects to be left behind their parents (bridge device objects) and can be fixed by moving the device_del() from pci_stop_dev() into pci_destroy_dev(), so pci_remove_bus() can be called for the device's subordinate bus before the device itself is unregistered from the hierarchy. Still, the driver, if any, should be detached from the device in pci_stop_dev(), so use device_release_driver() directly from there. References: https://bugzilla.kernel.org/show_bug.cgi?id=65281#c6 Reported-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pci/remove.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 1576851028db..cc9337a71529 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -24,7 +24,7 @@ static void pci_stop_dev(struct pci_dev *dev) if (dev->is_added) { pci_proc_detach_device(dev); pci_remove_sysfs_dev_files(dev); - device_del(&dev->dev); + device_release_driver(&dev->dev); dev->is_added = 0; } @@ -34,6 +34,8 @@ static void pci_stop_dev(struct pci_dev *dev) static void pci_destroy_dev(struct pci_dev *dev) { + device_del(&dev->dev); + down_write(&pci_bus_sem); list_del(&dev->bus_list); up_write(&pci_bus_sem); -- cgit v1.2.3-70-g09d2 From e3b439e1d315aff59c1b4f0fc43c5cd2d82b4138 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 30 Nov 2013 14:40:27 -0800 Subject: PCI: Use device_release_driver() in pci_stop_root_bus() To be consistent with 4bff6749905d ("PCI: Move device_del() from pci_stop_dev() to pci_destroy_dev()", this changes pci_stop_root_bus() to use device_release_driver() instead of device_del(). This also changes pci_remove_root_bus() to use device_unregister() instead of put_device() so it corresponds with the device_register() call in pci_create_root_bus(). [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/remove.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index cc9337a71529..692f4c39ac48 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -128,7 +128,7 @@ void pci_stop_root_bus(struct pci_bus *bus) pci_stop_bus_device(child); /* stop the host bridge */ - device_del(&host_bridge->dev); + device_release_driver(&host_bridge->dev); } void pci_remove_root_bus(struct pci_bus *bus) @@ -147,5 +147,5 @@ void pci_remove_root_bus(struct pci_bus *bus) host_bridge->bus = NULL; /* remove the host bridge */ - put_device(&host_bridge->dev); + device_unregister(&host_bridge->dev); } -- cgit v1.2.3-70-g09d2 From ef37702eb3cae2ec35d56ddd4f096cd47fec3f0d Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 30 Nov 2013 14:40:28 -0800 Subject: PCI: Move pci_proc_attach_device() to pci_bus_add_device() 4f535093cf8f ("PCI: Put pci_dev in device tree as early as possible") moved pci_proc_attach_device() from pci_bus_add_device() to pci_device_add(). This moves it back to pci_bus_add_device(), essentially reverting that part of 4f535093cf8f. This makes it symmetric with pci_stop_dev(), where we call pci_proc_detach_device() and pci_remove_sysfs_dev_files() and set dev->is_added = 0. [bhelgaas: changelog, create sysfs then attach proc for symmetry] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 1 + drivers/pci/probe.c | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index fc1b74013743..a037d81f21ed 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -176,6 +176,7 @@ int pci_bus_add_device(struct pci_dev *dev) */ pci_fixup_device(pci_fixup_final, dev); pci_create_sysfs_dev_files(dev); + pci_proc_attach_device(dev); dev->match_driver = true; retval = device_attach(&dev->dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 38e403dddf6e..173a9cf4593e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1381,8 +1381,6 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dev->match_driver = false; ret = device_add(&dev->dev); WARN_ON(ret < 0); - - pci_proc_attach_device(dev); } struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) -- cgit v1.2.3-70-g09d2 From ef83b0781a73f9efcb1228256bfdfb97fc9533a8 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 30 Nov 2013 14:40:29 -0800 Subject: PCI: Remove from bus_list and release resources in pci_release_dev() Previously we removed the pci_dev from the bus_list and released its resources in pci_destroy_dev(). But that's too early: it's possible to call pci_destroy_dev() twice for the same device (e.g., via sysfs), and that will cause an oops when we try to remove it from bus_list the second time. We should remove it from the bus_list only when the last reference to the pci_dev has been released, i.e., in pci_release_dev(). [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 21 +++++++++++++++++++-- drivers/pci/remove.c | 19 ------------------- 2 files changed, 19 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 173a9cf4593e..12ec56c9a913 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1154,6 +1154,18 @@ static void pci_release_capabilities(struct pci_dev *dev) pci_free_cap_save_buffers(dev); } +static void pci_free_resources(struct pci_dev *dev) +{ + int i; + + pci_cleanup_rom(dev); + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *res = dev->resource + i; + if (res->parent) + release_resource(res); + } +} + /** * pci_release_dev - free a pci device structure when all users of it are finished. * @dev: device that's been disconnected @@ -1163,9 +1175,14 @@ static void pci_release_capabilities(struct pci_dev *dev) */ static void pci_release_dev(struct device *dev) { - struct pci_dev *pci_dev; + struct pci_dev *pci_dev = to_pci_dev(dev); + + down_write(&pci_bus_sem); + list_del(&pci_dev->bus_list); + up_write(&pci_bus_sem); + + pci_free_resources(pci_dev); - pci_dev = to_pci_dev(dev); pci_release_capabilities(pci_dev); pci_release_of_node(pci_dev); pcibios_release_device(pci_dev); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 692f4c39ac48..f452148e6d55 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -3,20 +3,6 @@ #include #include "pci.h" -static void pci_free_resources(struct pci_dev *dev) -{ - int i; - - msi_remove_pci_irq_vectors(dev); - - pci_cleanup_rom(dev); - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *res = dev->resource + i; - if (res->parent) - release_resource(res); - } -} - static void pci_stop_dev(struct pci_dev *dev) { pci_pme_active(dev, false); @@ -36,11 +22,6 @@ static void pci_destroy_dev(struct pci_dev *dev) { device_del(&dev->dev); - down_write(&pci_bus_sem); - list_del(&dev->bus_list); - up_write(&pci_bus_sem); - - pci_free_resources(dev); put_device(&dev->dev); } -- cgit v1.2.3-70-g09d2 From c28f8a1f2b5ed24d48ca6827d0ae499c2e48e8c9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 12 Dec 2013 22:49:58 +0100 Subject: PCI: imx6: Make reset-gpio optional Some boards do not have a PCIe reset GPIO. To avoid probe failure on these boards, make the reset GPIO optional as well. [bhelgaas: whitespace fixes] Signed-off-by: Marek Vasut Signed-off-by: Bjorn Helgaas Reviewed-by: Jingoo Han Acked-by: Shawn Guo Cc: Frank Li Cc: Harro Haan Cc: Mohit KUMAR Cc: Pratyush Anand Cc: Richard Zhu Cc: Sascha Hauer Cc: Sean Cross Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Tim Harvey Cc: Troy Kisky Cc: Yinghai Lu --- .../devicetree/bindings/pci/designware-pcie.txt | 2 ++ drivers/pci/host/pci-imx6.c | 27 +++++++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt index d5d26d443693..d6fae13ff062 100644 --- a/Documentation/devicetree/bindings/pci/designware-pcie.txt +++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt @@ -19,6 +19,8 @@ Required properties: to define the mapping of the PCIe interface to interrupt numbers. - num-lanes: number of lanes to use + +Optional properties: - reset-gpio: gpio pin number of power good signal Optional properties for fsl,imx6q-pcie diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 9fc1cb66c64e..1176bddee1cc 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -214,9 +214,12 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp) regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); - gpio_set_value(imx6_pcie->reset_gpio, 0); - msleep(100); - gpio_set_value(imx6_pcie->reset_gpio, 1); + /* Some boards don't have PCIe reset GPIO. */ + if (gpio_is_valid(imx6_pcie->reset_gpio)) { + gpio_set_value(imx6_pcie->reset_gpio, 0); + msleep(100); + gpio_set_value(imx6_pcie->reset_gpio, 1); + } return 0; } @@ -432,17 +435,13 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) /* Fetch GPIOs */ imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); - if (!gpio_is_valid(imx6_pcie->reset_gpio)) { - dev_err(&pdev->dev, "no reset-gpio defined\n"); - ret = -ENODEV; - } - ret = devm_gpio_request_one(&pdev->dev, - imx6_pcie->reset_gpio, - GPIOF_OUT_INIT_LOW, - "PCIe reset"); - if (ret) { - dev_err(&pdev->dev, "unable to get reset gpio\n"); - return ret; + if (gpio_is_valid(imx6_pcie->reset_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio, + GPIOF_OUT_INIT_LOW, "PCIe reset"); + if (ret) { + dev_err(&pdev->dev, "unable to get reset gpio\n"); + return ret; + } } imx6_pcie->power_on_gpio = of_get_named_gpio(np, "power-on-gpio", 0); -- cgit v1.2.3-70-g09d2 From 7f9f40c01cce0c0e0ced34af2a2fd8353cc606c3 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 12 Dec 2013 22:49:59 +0100 Subject: PCI: imx6: Report "link up" only after link training completes While waiting for the PHY to report the PCIe link is up, we might hit a situation where the link training is still in progress, while the PHY already reports the link is up. Add additional check for this condition. Signed-off-by: Marek Vasut Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo Cc: Frank Li Cc: Harro Haan Cc: Jingoo Han Cc: Mohit KUMAR Cc: Pratyush Anand Cc: Richard Zhu Cc: Sascha Hauer Cc: Sean Cross Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Tim Harvey Cc: Troy Kisky Cc: Yinghai Lu --- drivers/pci/host/pci-imx6.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 1176bddee1cc..5634a33ea642 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -48,6 +48,8 @@ struct imx6_pcie { #define PL_OFFSET 0x700 #define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) #define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29) +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP (1 << 4) #define PCIE_PHY_CTRL (PL_OFFSET + 0x114) #define PCIE_PHY_CTRL_DATA_LOC 0 @@ -338,10 +340,17 @@ static int imx6_pcie_link_up(struct pcie_port *pp) { u32 rc, ltssm, rx_valid, temp; - /* link is debug bit 36, debug register 1 starts at bit 32 */ - rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << (36 - 32)); - if (rc) - return -EAGAIN; + /* + * Test if the PHY reports that the link is up and also that + * the link training finished. It might happen that the PHY + * reports the link is already up, but the link training bit + * is still set, so make sure to check the training is done + * as well here. + */ + rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); + if ((rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) && + !(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) + return 1; /* * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. -- cgit v1.2.3-70-g09d2 From 982aa234512f6a88932a5ece7f7becbf4b08ece7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 12 Dec 2013 22:50:00 +0100 Subject: PCI: imx6: Factor out PHY reset Split the PCIe PHY reset from the link up function to make the code a little more structured. No functional change. Signed-off-by: Marek Vasut Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo Cc: Frank Li Cc: Harro Haan Cc: Jingoo Han Cc: Mohit KUMAR Cc: Pratyush Anand Cc: Richard Zhu Cc: Sascha Hauer Cc: Sean Cross Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Tim Harvey Cc: Troy Kisky Cc: Yinghai Lu --- drivers/pci/host/pci-imx6.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 5634a33ea642..25dde2c7b445 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -336,9 +336,26 @@ static void imx6_pcie_host_init(struct pcie_port *pp) return; } +static void imx6_pcie_reset_phy(struct pcie_port *pp) +{ + uint32_t temp; + + pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp); + temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | + PHY_RX_OVRD_IN_LO_RX_PLL_EN); + pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp); + + usleep_range(2000, 3000); + + pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp); + temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | + PHY_RX_OVRD_IN_LO_RX_PLL_EN); + pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, temp); +} + static int imx6_pcie_link_up(struct pcie_port *pp) { - u32 rc, ltssm, rx_valid, temp; + u32 rc, ltssm, rx_valid; /* * Test if the PHY reports that the link is up and also that @@ -370,21 +387,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp) dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); - pcie_phy_read(pp->dbi_base, - PHY_RX_OVRD_IN_LO, &temp); - temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN - | PHY_RX_OVRD_IN_LO_RX_PLL_EN); - pcie_phy_write(pp->dbi_base, - PHY_RX_OVRD_IN_LO, temp); - - usleep_range(2000, 3000); - - pcie_phy_read(pp->dbi_base, - PHY_RX_OVRD_IN_LO, &temp); - temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN - | PHY_RX_OVRD_IN_LO_RX_PLL_EN); - pcie_phy_write(pp->dbi_base, - PHY_RX_OVRD_IN_LO, temp); + imx6_pcie_reset_phy(pp); return 0; } -- cgit v1.2.3-70-g09d2 From 66a60f934701e282e596c97a3df22e4e5a7c2d68 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 12 Dec 2013 22:50:01 +0100 Subject: PCI: imx6: Factor out link up wait loop Split the function that waits for the PCIe link to come up from the rest if the host init function. We will find this change useful in the subsequent patch, since this will be called twice then. No functional change. [bhelgaas: remove useless "return;"] Signed-off-by: Marek Vasut Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo Cc: Frank Li Cc: Harro Haan Cc: Jingoo Han Cc: Mohit KUMAR Cc: Pratyush Anand Cc: Richard Zhu Cc: Sascha Hauer Cc: Sean Cross Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Tim Harvey Cc: Troy Kisky Cc: Yinghai Lu --- drivers/pci/host/pci-imx6.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 25dde2c7b445..d81da4556c19 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -304,6 +304,25 @@ static void imx6_pcie_init_phy(struct pcie_port *pp) IMX6Q_GPR8_TX_SWING_LOW, 127 << 25); } +static int imx6_pcie_wait_for_link(struct pcie_port *pp) +{ + int count = 200; + + while (!dw_pcie_link_up(pp)) { + usleep_range(100, 1000); + if (--count) + continue; + + dev_err(pp->dev, "phy link never came up\n"); + dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", + readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), + readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); + return -EINVAL; + } + + return 0; +} + static void imx6_pcie_host_init(struct pcie_port *pp) { int count = 0; @@ -320,20 +339,7 @@ static void imx6_pcie_host_init(struct pcie_port *pp) regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); - while (!dw_pcie_link_up(pp)) { - usleep_range(100, 1000); - count++; - if (count >= 200) { - dev_err(pp->dev, "phy link never came up\n"); - dev_dbg(pp->dev, - "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", - readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), - readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); - break; - } - } - - return; + imx6_pcie_wait_for_link(pp); } static void imx6_pcie_reset_phy(struct pcie_port *pp) -- cgit v1.2.3-70-g09d2 From fa33a6d87eac1ab1457e632d32b7b4b74172e699 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 12 Dec 2013 22:50:02 +0100 Subject: PCI: imx6: Start link in Gen1 before negotiating for Gen2 mode This patch first forces the link into Gen1 mode before starting up the link and, only after the link is up, start negotiating possible Gen2 mode operation. This is because without such sequence, some PCIe switches are not detected at all. Signed-off-by: Marek Vasut Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo Cc: Frank Li Cc: Harro Haan Cc: Jingoo Han Cc: Mohit KUMAR Cc: Pratyush Anand Cc: Richard Zhu Cc: Sascha Hauer Cc: Sean Cross Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Tim Harvey Cc: Troy Kisky Cc: Yinghai Lu --- drivers/pci/host/pci-imx6.c | 78 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index d81da4556c19..d34678dc5a14 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -44,6 +44,12 @@ struct imx6_pcie { void __iomem *mem_base; }; +/* PCIe Root Complex registers (memory-mapped) */ +#define PCIE_RC_LCR 0x7c +#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1 +#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2 +#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf + /* PCIe Port Logic registers (memory-mapped) */ #define PL_OFFSET 0x700 #define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) @@ -61,6 +67,9 @@ struct imx6_pcie { #define PCIE_PHY_STAT (PL_OFFSET + 0x110) #define PCIE_PHY_STAT_ACK_LOC 16 +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) + /* PHY registers (not memory-mapped) */ #define PCIE_PHY_RX_ASIC_OUT 0x100D @@ -323,11 +332,71 @@ static int imx6_pcie_wait_for_link(struct pcie_port *pp) return 0; } -static void imx6_pcie_host_init(struct pcie_port *pp) +static int imx6_pcie_start_link(struct pcie_port *pp) { - int count = 0; struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + uint32_t tmp; + int ret, count; + + /* + * Force Gen1 operation when starting the link. In case the link is + * started in Gen2 mode, there is a possibility the devices on the + * bus will not be detected at all. This happens with PCIe switches. + */ + tmp = readl(pp->dbi_base + PCIE_RC_LCR); + tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; + tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; + writel(tmp, pp->dbi_base + PCIE_RC_LCR); + + /* Start LTSSM. */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); + + ret = imx6_pcie_wait_for_link(pp); + if (ret) + return ret; + + /* Allow Gen2 mode after the link is up. */ + tmp = readl(pp->dbi_base + PCIE_RC_LCR); + tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; + tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; + writel(tmp, pp->dbi_base + PCIE_RC_LCR); + + /* + * Start Directed Speed Change so the best possible speed both link + * partners support can be negotiated. + */ + tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp |= PORT_LOGIC_SPEED_CHANGE; + writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + + count = 200; + while (count--) { + tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + /* Test if the speed change finished. */ + if (!(tmp & PORT_LOGIC_SPEED_CHANGE)) + break; + usleep_range(100, 1000); + } + + /* Make sure link training is finished as well! */ + if (count) + ret = imx6_pcie_wait_for_link(pp); + else + ret = -EINVAL; + if (ret) { + dev_err(pp->dev, "Failed to bring link up!\n"); + } else { + tmp = readl(pp->dbi_base + 0x80); + dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf); + } + + return ret; +} + +static void imx6_pcie_host_init(struct pcie_port *pp) +{ imx6_pcie_assert_core_reset(pp); imx6_pcie_init_phy(pp); @@ -336,10 +405,7 @@ static void imx6_pcie_host_init(struct pcie_port *pp) dw_pcie_setup_rc(pp); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); - - imx6_pcie_wait_for_link(pp); + imx6_pcie_start_link(pp); } static void imx6_pcie_reset_phy(struct pcie_port *pp) -- cgit v1.2.3-70-g09d2 From bc9ef770047a931787e1b1e092dbe3993ce4e0fb Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 12 Dec 2013 22:50:03 +0100 Subject: PCI: imx6: Fix bugs in PCIe startup code LTSSM shouldn't be set once in assert_core_reset(). Move peripheral reset just before LTSSM start. Signed-off-by: Richard Zhu Signed-off-by: Bjorn Helgaas Acked-by: Shawn Guo Cc: Frank Li Cc: Harro Haan Cc: Jingoo Han Cc: Mohit KUMAR Cc: Pratyush Anand Cc: Richard Zhu Cc: Sascha Hauer Cc: Sean Cross Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Tim Harvey Cc: Troy Kisky Cc: Yinghai Lu --- drivers/pci/host/pci-imx6.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index d34678dc5a14..e8663a8c3406 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -220,18 +220,9 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp) regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); - /* Some boards don't have PCIe reset GPIO. */ - if (gpio_is_valid(imx6_pcie->reset_gpio)) { - gpio_set_value(imx6_pcie->reset_gpio, 0); - msleep(100); - gpio_set_value(imx6_pcie->reset_gpio, 1); - } - return 0; } @@ -275,6 +266,12 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) /* allow the clocks to stabilize */ usleep_range(200, 500); + /* Some boards don't have PCIe reset GPIO. */ + if (gpio_is_valid(imx6_pcie->reset_gpio)) { + gpio_set_value(imx6_pcie->reset_gpio, 0); + msleep(100); + gpio_set_value(imx6_pcie->reset_gpio, 1); + } return 0; err_pcie_axi: -- cgit v1.2.3-70-g09d2 From 339135ff1b5fca9a323ca7af67cebefedf50d4e7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 19 Dec 2013 14:34:59 +0530 Subject: PCI: mvebu: Remove redundant of_match_ptr mvebu_pcie_of_match_table is always compiled in. Hence of_match_ptr is not required. Signed-off-by: Sachin Kamat Signed-off-by: Bjorn Helgaas Acked-by: Thomas Petazzoni --- drivers/pci/host/pci-mvebu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 6f35df5fedce..afd2af04980c 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -1007,8 +1007,7 @@ static struct platform_driver mvebu_pcie_driver = { .driver = { .owner = THIS_MODULE, .name = "mvebu-pcie", - .of_match_table = - of_match_ptr(mvebu_pcie_of_match_table), + .of_match_table = mvebu_pcie_of_match_table, /* driver unloading/unbinding currently not supported */ .suppress_bind_attrs = true, }, -- cgit v1.2.3-70-g09d2 From 8f3acca9acec1503f6b374faef2d1013cbf502af Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 19 Dec 2013 14:20:09 -0700 Subject: PCI/portdrv: Cleanup error paths Make the straightline path the normal no-error path. Check for errors and return them directly, instead of checking for success and putting the normal path in an "if" body. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_core.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 0b6e76604068..fc86d323fecc 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -344,11 +344,13 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq) device_enable_async_suspend(device); retval = device_register(device); - if (retval) + if (retval) { kfree(pcie); - else - get_device(device); - return retval; + return retval; + } + + get_device(device); + return 0; } /** @@ -498,12 +500,12 @@ static int pcie_port_probe_service(struct device *dev) pciedev = to_pcie_device(dev); status = driver->probe(pciedev); - if (!status) { - dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", - driver->name); - get_device(dev); - } - return status; + if (status) + return status; + + dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", driver->name); + get_device(dev); + return 0; } /** -- cgit v1.2.3-70-g09d2 From f39862058e1f278e0495cd9ea57de571e74aa1fe Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Thu, 19 Dec 2013 14:22:35 -0700 Subject: PCI/portdrv: Add put_device() after device_register() failure This is required so that we give up the last reference to the device. Removed the kfree() as put_device will result in release_pcie_device() being called and hence the container of the device will be kfree'd. [bhelgaas: fix conflict after my previous cleanup] Signed-off-by: Levente Kurusa Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index fc86d323fecc..9811eea53511 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -345,7 +345,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq) retval = device_register(device); if (retval) { - kfree(pcie); + put_device(device); return retval; } -- cgit v1.2.3-70-g09d2 From e75f34ce6633549486a044d64b2a79240d4113a8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 19 Dec 2013 14:24:13 -0700 Subject: PCI/portdrv: Remove extra get_device()/put_device() for pcie_device Previously pcie_device_init() called get_device() if device_register() for the new pcie_device succeeded, and remove_iter() called put_device() when removing before unregistering the device. But device_register() already increments the reference count in device_add(), so we don't need to do it again here. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 9811eea53511..6a6e54909335 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -349,7 +349,6 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq) return retval; } - get_device(device); return 0; } @@ -456,10 +455,8 @@ int pcie_port_device_resume(struct device *dev) static int remove_iter(struct device *dev, void *data) { - if (dev->bus == &pcie_port_bus_type) { - put_device(dev); + if (dev->bus == &pcie_port_bus_type) device_unregister(dev); - } return 0; } -- cgit v1.2.3-70-g09d2 From 1c51b50c2995f543d145d3bce78029ac9f8ca6b3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 19 Dec 2013 12:30:17 -0800 Subject: PCI/MSI: Export MSI mode using attributes, not kobjects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCI MSI sysfs code is a mess with kobjects for things that don't really need to be kobjects. This patch creates attributes dynamically for the MSI interrupts instead of using kobjects. Note, this removes a directory from sysfs. Old MSI kobjects: pci_device └── msi_irqs    └── 40    └── mode New MSI attributes: pci_device └── msi_irqs    └── 40 As there was only one file "mode" with the kobject model, the interrupt number is now a file that returns the "mode" of the interrupt (msi vs. msix). Signed-off-by: Greg Kroah-Hartman Signed-off-by: Bjorn Helgaas Acked-by: Neil Horman --- Documentation/ABI/testing/sysfs-bus-pci | 11 +-- drivers/pci/msi.c | 166 +++++++++++++++++--------------- include/linux/pci.h | 2 +- 3 files changed, 96 insertions(+), 83 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index 5210a51c90fd..a3c5a6685036 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -70,18 +70,15 @@ Date: September, 2011 Contact: Neil Horman Description: The /sys/devices/.../msi_irqs directory contains a variable set - of sub-directories, with each sub-directory being named after a - corresponding msi irq vector allocated to that device. Each - numbered sub-directory N contains attributes of that irq. - Note that this directory is not created for device drivers which - do not support msi irqs + of files, with each file being named after a corresponding msi + irq vector allocated to that device. -What: /sys/bus/pci/devices/.../msi_irqs//mode +What: /sys/bus/pci/devices/.../msi_irqs/ Date: September 2011 Contact: Neil Horman Description: This attribute indicates the mode that the irq vector named by - the parent directory is in (msi vs. msix) + the file is in (msi vs. msix) What: /sys/bus/pci/devices/.../remove Date: January 2009 diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 3fcd67a16677..f88fa1277802 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -363,6 +363,9 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) static void free_msi_irqs(struct pci_dev *dev) { struct msi_desc *entry, *tmp; + struct attribute **msi_attrs; + struct device_attribute *dev_attr; + int count = 0; list_for_each_entry(entry, &dev->msi_list, list) { int i, nvec; @@ -398,6 +401,22 @@ static void free_msi_irqs(struct pci_dev *dev) list_del(&entry->list); kfree(entry); } + + if (dev->msi_irq_groups) { + sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups); + msi_attrs = dev->msi_irq_groups[0]->attrs; + list_for_each_entry(entry, &dev->msi_list, list) { + dev_attr = container_of(msi_attrs[count], + struct device_attribute, attr); + kfree(dev_attr->attr.name); + kfree(dev_attr); + ++count; + } + kfree(msi_attrs); + kfree(dev->msi_irq_groups[0]); + kfree(dev->msi_irq_groups); + dev->msi_irq_groups = NULL; + } } static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) @@ -471,94 +490,95 @@ void pci_restore_msi_state(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_restore_msi_state); - -#define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr) -#define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj) - -struct msi_attribute { - struct attribute attr; - ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr, - char *buf); - ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr, - const char *buf, size_t count); -}; - -static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr, +static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi"); -} - -static ssize_t msi_irq_attr_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct msi_attribute *attribute = to_msi_attr(attr); - struct msi_desc *entry = to_msi_desc(kobj); - - if (!attribute->show) - return -EIO; - - return attribute->show(entry, attribute, buf); -} - -static const struct sysfs_ops msi_irq_sysfs_ops = { - .show = msi_irq_attr_show, -}; - -static struct msi_attribute mode_attribute = - __ATTR(mode, S_IRUGO, show_msi_mode, NULL); - - -static struct attribute *msi_irq_default_attrs[] = { - &mode_attribute.attr, - NULL -}; + struct pci_dev *pdev = to_pci_dev(dev); + struct msi_desc *entry; + unsigned long irq; + int retval; -static void msi_kobj_release(struct kobject *kobj) -{ - struct msi_desc *entry = to_msi_desc(kobj); + retval = kstrtoul(attr->attr.name, 10, &irq); + if (retval) + return retval; - pci_dev_put(entry->dev); + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->irq == irq) { + return sprintf(buf, "%s\n", + entry->msi_attrib.is_msix ? "msix" : "msi"); + } + } + return -ENODEV; } -static struct kobj_type msi_irq_ktype = { - .release = msi_kobj_release, - .sysfs_ops = &msi_irq_sysfs_ops, - .default_attrs = msi_irq_default_attrs, -}; - static int populate_msi_sysfs(struct pci_dev *pdev) { + struct attribute **msi_attrs; + struct attribute *msi_attr; + struct device_attribute *msi_dev_attr; + struct attribute_group *msi_irq_group; + const struct attribute_group **msi_irq_groups; struct msi_desc *entry; - struct kobject *kobj; - int ret; + int ret = -ENOMEM; + int num_msi = 0; int count = 0; - pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); - if (!pdev->msi_kset) - return -ENOMEM; + /* Determine how many msi entries we have */ + list_for_each_entry(entry, &pdev->msi_list, list) { + ++num_msi; + } + if (!num_msi) + return 0; + /* Dynamically create the MSI attributes for the PCI device */ + msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL); + if (!msi_attrs) + return -ENOMEM; list_for_each_entry(entry, &pdev->msi_list, list) { - kobj = &entry->kobj; - kobj->kset = pdev->msi_kset; - pci_dev_get(pdev); - ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, - "%u", entry->irq); - if (ret) - goto out_unroll; - - count++; + char *name = kmalloc(20, GFP_KERNEL); + msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL); + if (!msi_dev_attr) + goto error_attrs; + sprintf(name, "%d", entry->irq); + sysfs_attr_init(&msi_dev_attr->attr); + msi_dev_attr->attr.name = name; + msi_dev_attr->attr.mode = S_IRUGO; + msi_dev_attr->show = msi_mode_show; + msi_attrs[count] = &msi_dev_attr->attr; + ++count; } + msi_irq_group = kzalloc(sizeof(*msi_irq_group), GFP_KERNEL); + if (!msi_irq_group) + goto error_attrs; + msi_irq_group->name = "msi_irqs"; + msi_irq_group->attrs = msi_attrs; + + msi_irq_groups = kzalloc(sizeof(void *) * 2, GFP_KERNEL); + if (!msi_irq_groups) + goto error_irq_group; + msi_irq_groups[0] = msi_irq_group; + + ret = sysfs_create_groups(&pdev->dev.kobj, msi_irq_groups); + if (ret) + goto error_irq_groups; + pdev->msi_irq_groups = msi_irq_groups; + return 0; -out_unroll: - list_for_each_entry(entry, &pdev->msi_list, list) { - if (!count) - break; - kobject_del(&entry->kobj); - kobject_put(&entry->kobj); - count--; +error_irq_groups: + kfree(msi_irq_groups); +error_irq_group: + kfree(msi_irq_group); +error_attrs: + count = 0; + msi_attr = msi_attrs[count]; + while (msi_attr) { + msi_dev_attr = container_of(msi_attr, struct device_attribute, attr); + kfree(msi_attr->name); + kfree(msi_dev_attr); + ++count; + msi_attr = msi_attrs[count]; } return ret; } @@ -925,8 +945,6 @@ void pci_disable_msi(struct pci_dev *dev) pci_msi_shutdown(dev); free_msi_irqs(dev); - kset_unregister(dev->msi_kset); - dev->msi_kset = NULL; } EXPORT_SYMBOL(pci_disable_msi); @@ -1023,8 +1041,6 @@ void pci_disable_msix(struct pci_dev *dev) pci_msix_shutdown(dev); free_msi_irqs(dev); - kset_unregister(dev->msi_kset); - dev->msi_kset = NULL; } EXPORT_SYMBOL(pci_disable_msix); diff --git a/include/linux/pci.h b/include/linux/pci.h index 1084a15175e0..36a5b1828f91 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -351,7 +351,7 @@ struct pci_dev { struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ #ifdef CONFIG_PCI_MSI struct list_head msi_list; - struct kset *msi_kset; + const struct attribute_group **msi_irq_groups; #endif struct pci_vpd *vpd; #ifdef CONFIG_PCI_ATS -- cgit v1.2.3-70-g09d2 From ca1658921b63e5771423603367c5bee528acc977 Mon Sep 17 00:00:00 2001 From: Harro Haan Date: Thu, 12 Dec 2013 19:29:03 +0100 Subject: PCI: designware: Fix missing MSI IRQs The interrupts were cleared after the IRQ handler was called. This means that new interrupts that occur after the handler handled the previous IRQ but before the interrupt is cleared will be missed. Tested-by: Marek Vasut Tested-by: Matthias Mann Signed-off-by: Harro Haan Signed-off-by: Bjorn Helgaas Acked-by: Jingoo Han Acked-by: Mohit Kumar Cc: Richard Zhu Cc: Shawn Guo Cc: Pratyush Anand Cc: Tim Harvey Cc: Juergen Beisert Cc: Arnd Bergmann Cc: Siva Reddy Kallam Cc: Srikanth T Shivanand Cc: Sean Cross --- drivers/pci/host/pcie-designware.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 1c92833a4ed3..c85cac009583 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -167,11 +167,13 @@ void dw_handle_msi_irq(struct pcie_port *pp) while ((pos = find_next_bit(&val, 32, pos)) != 32) { irq = irq_find_mapping(pp->irq_domain, i * 32 + pos); + dw_pcie_wr_own_conf(pp, + PCIE_MSI_INTR0_STATUS + i * 12, + 4, 1 << pos); generic_handle_irq(irq); pos++; } } - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val); } } -- cgit v1.2.3-70-g09d2 From a01ef59e131b78b0fa7af235ea958bd17e5e86ca Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Wed, 11 Dec 2013 15:08:32 +0530 Subject: PCI: designware: Add dw_pcie prefix before cfg_read/write The cfg_read/write functions are DesignWare-specific. Add dw_pcie prefix to avoid collision in global name space. Tested-by: Jingoo Han Signed-off-by: Pratyush Anand Signed-off-by: Bjorn Helgaas Reviewed-by: Jagannadha Sutradharudu Teki Acked-by: Jingoo Han --- drivers/pci/host/pci-exynos.c | 5 +++-- drivers/pci/host/pcie-designware.c | 23 ++++++++++++++--------- drivers/pci/host/pcie-designware.h | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index 24beed38ddc7..3de6bfbbe8e9 100644 --- a/drivers/pci/host/pci-exynos.c +++ b/drivers/pci/host/pci-exynos.c @@ -468,7 +468,7 @@ static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, int ret; exynos_pcie_sideband_dbi_r_mode(pp, true); - ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); + ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); exynos_pcie_sideband_dbi_r_mode(pp, false); return ret; } @@ -479,7 +479,8 @@ static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, int ret; exynos_pcie_sideband_dbi_w_mode(pp, true); - ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size, val); + ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), + where, size, val); exynos_pcie_sideband_dbi_w_mode(pp, false); return ret; } diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index c85cac009583..fc97f5ace5b5 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -74,7 +74,7 @@ static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) return sys->private_data; } -int cfg_read(void __iomem *addr, int where, int size, u32 *val) +int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) { *val = readl(addr); @@ -88,7 +88,7 @@ int cfg_read(void __iomem *addr, int where, int size, u32 *val) return PCIBIOS_SUCCESSFUL; } -int cfg_write(void __iomem *addr, int where, int size, u32 val) +int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val) { if (size == 4) writel(val, addr); @@ -126,7 +126,8 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, if (pp->ops->rd_own_conf) ret = pp->ops->rd_own_conf(pp, where, size, val); else - ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); + ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, + size, val); return ret; } @@ -139,8 +140,8 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, if (pp->ops->wr_own_conf) ret = pp->ops->wr_own_conf(pp, where, size, val); else - ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size, - val); + ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where, + size, val); return ret; } @@ -574,11 +575,13 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, if (bus->parent->number == pp->root_bus_nr) { dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = cfg_read(pp->va_cfg0_base + address, where, size, val); + ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, + val); dw_pcie_prog_viewport_mem_outbound(pp); } else { dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = cfg_read(pp->va_cfg1_base + address, where, size, val); + ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, + val); dw_pcie_prog_viewport_io_outbound(pp); } @@ -597,11 +600,13 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, if (bus->parent->number == pp->root_bus_nr) { dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = cfg_write(pp->va_cfg0_base + address, where, size, val); + ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, + val); dw_pcie_prog_viewport_mem_outbound(pp); } else { dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = cfg_write(pp->va_cfg1_base + address, where, size, val); + ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, + val); dw_pcie_prog_viewport_io_outbound(pp); } diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index c15379be2372..3063b3594d88 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -66,8 +66,8 @@ struct pcie_host_ops { void (*host_init)(struct pcie_port *pp); }; -int cfg_read(void __iomem *addr, int where, int size, u32 *val); -int cfg_write(void __iomem *addr, int where, int size, u32 val); +int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); +int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val); void dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); -- cgit v1.2.3-70-g09d2 From fce8591f73c6a30c231f220d1092362aae0b985c Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Wed, 11 Dec 2013 15:08:33 +0530 Subject: PCI: designware: Fix I/O transfers by using CPU (not realio) address pp->io_base, which is the input of the outbound IO address translation unit, should be the CPU address. It was incorrectly programmed to the realio address. We should pass global_io_offset rather than sys->io_offset to pci_ioremap_io(), so we map the new window into the first available spot in the Linux view of the I/O space. We must also pass CPU address instead of realio address to pci_ioremap_io(). This patch fixes above issue. It has been tested with Lecroy PTC in AIC mode and Pericom PI7C9X2G303EL PCIe switch, which does not work otherwise. Tested-by: Mohit Kumar Tested-by: Tim Harvey Signed-off-by: Pratyush Anand Signed-off-by: Bjorn Helgaas Reviewed-by: Marek Vasut Acked-by: Arnd Bergmann Acked-by: Jingoo Han Cc: Richard Zhu --- drivers/pci/host/pcie-designware.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index fc97f5ace5b5..4a08d30548ce 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -418,6 +418,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) + global_io_offset); pp->config.io_size = resource_size(&pp->io); pp->config.io_bus_addr = range.pci_addr; + pp->io_base = range.cpu_addr; } if (restype == IORESOURCE_MEM) { of_pci_range_to_resource(&range, np, &pp->mem); @@ -443,7 +444,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->cfg0_base = pp->cfg.start; pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; - pp->io_base = pp->io.start; pp->mem_base = pp->mem.start; pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, @@ -613,7 +613,6 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, return ret; } - static int dw_pcie_valid_config(struct pcie_port *pp, struct pci_bus *bus, int dev) { @@ -707,7 +706,7 @@ static int dw_pcie_setup(int nr, struct pci_sys_data *sys) if (global_io_offset < SZ_1M && pp->config.io_size > 0) { sys->io_offset = global_io_offset - pp->config.io_bus_addr; - pci_ioremap_io(sys->io_offset, pp->io.start); + pci_ioremap_io(global_io_offset, pp->io_base); global_io_offset += SZ_64K; pci_add_resource_offset(&sys->resources, &pp->io, sys->io_offset); -- cgit v1.2.3-70-g09d2 From 2adc7907bac2c72535894732c4b41f9210f9e577 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 16 Dec 2013 09:34:56 +0100 Subject: PCI/MSI: Return msix_capability_init() failure if populate_msi_sysfs() fails If populate_msi_sysfs() function failed msix_capability_init() must return the error code, but it returns the success instead. This update fixes the described misbehaviour. Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas Reviewed-by: Tejun Heo --- drivers/pci/msi.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index f88fa1277802..a581381e02f2 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -749,7 +749,7 @@ static int msix_capability_init(struct pci_dev *dev, ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); if (ret) - goto error; + goto out_avail; /* * Some devices require MSI-X to be enabled before we can touch the @@ -762,10 +762,8 @@ static int msix_capability_init(struct pci_dev *dev, msix_program_entries(dev, entries); ret = populate_msi_sysfs(dev); - if (ret) { - ret = 0; - goto error; - } + if (ret) + goto out_free; /* Set MSI-X enabled bits and unmask the function */ pci_intx_for_msi(dev, 0); @@ -776,7 +774,7 @@ static int msix_capability_init(struct pci_dev *dev, return 0; -error: +out_avail: if (ret < 0) { /* * If we had some success, report the number of irqs @@ -793,6 +791,7 @@ error: ret = avail; } +out_free: free_msi_irqs(dev); return ret; -- cgit v1.2.3-70-g09d2 From 52179dc9edc3b7a2b3bb01cbb1b6c96f6d05fc73 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 16 Dec 2013 09:34:58 +0100 Subject: PCI/MSI: Make pci_enable_msi/msix() 'nvec' argument type as int Make pci_enable_msi_block(), pci_enable_msi_block_auto() and pci_enable_msix() consistent with regard to the type of 'nvec' argument. Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas Reviewed-by: Tejun Heo --- Documentation/PCI/MSI-HOWTO.txt | 2 +- drivers/pci/msi.c | 4 ++-- include/linux/pci.h | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index a09178086c30..a4d174e95413 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -129,7 +129,7 @@ call to succeed. 4.2.3 pci_enable_msi_block_auto -int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *count) +int pci_enable_msi_block_auto(struct pci_dev *dev, int *count) This variation on pci_enable_msi() call allows a device driver to request the maximum possible number of MSIs. The MSI specification only allows diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index a581381e02f2..ce0d4eb91a22 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -855,7 +855,7 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) * updates the @dev's irq member to the lowest new interrupt number; the * other interrupt numbers allocated to this device are consecutive. */ -int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) +int pci_enable_msi_block(struct pci_dev *dev, int nvec) { int status, maxvec; u16 msgctl; @@ -886,7 +886,7 @@ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) } EXPORT_SYMBOL(pci_enable_msi_block); -int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) +int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec) { int ret, nvec; u16 msgctl; diff --git a/include/linux/pci.h b/include/linux/pci.h index 3c95efd12f3c..7c34c3913bcb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1154,13 +1154,13 @@ struct msix_entry { #ifndef CONFIG_PCI_MSI -static inline int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) +static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec) { return -ENOSYS; } static inline int -pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) +pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec) { return -ENOSYS; } @@ -1195,8 +1195,8 @@ static inline int pci_msi_enabled(void) return 0; } #else -int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); -int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec); +int pci_enable_msi_block(struct pci_dev *dev, int nvec); +int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec); void pci_msi_shutdown(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev); int pci_msix_table_size(struct pci_dev *dev); -- cgit v1.2.3-70-g09d2 From fc2798502f860b18f3c7121e4dc659d3d9d28d74 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 9 Dec 2013 22:54:40 -0800 Subject: PCI: Convert pcibios_resource_to_bus() to take a pci_bus, not a pci_dev These interfaces: pcibios_resource_to_bus(struct pci_dev *dev, *bus_region, *resource) pcibios_bus_to_resource(struct pci_dev *dev, *resource, *bus_region) took a pci_dev, but they really depend only on the pci_bus. And we want to use them in resource allocation paths where we have the bus but not a device, so this patch converts them to take the pci_bus instead of the pci_dev: pcibios_resource_to_bus(struct pci_bus *bus, *bus_region, *resource) pcibios_bus_to_resource(struct pci_bus *bus, *resource, *bus_region) In fact, with standard PCI-PCI bridges, they only depend on the host bridge, because that's the only place address translation occurs, but we aren't going that far yet. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/alpha/kernel/pci-sysfs.c | 4 ++-- arch/powerpc/kernel/pci-common.c | 4 ++-- arch/powerpc/kernel/pci_of_scan.c | 4 ++-- arch/sparc/kernel/pci.c | 6 +++--- drivers/pci/host-bridge.c | 19 ++++++++----------- drivers/pci/probe.c | 18 +++++++++--------- drivers/pci/quirks.c | 2 +- drivers/pci/rom.c | 2 +- drivers/pci/setup-bus.c | 16 ++++++++-------- drivers/pci/setup-res.c | 2 +- drivers/pcmcia/i82092.c | 2 +- drivers/pcmcia/yenta_socket.c | 6 +++--- drivers/scsi/sym53c8xx_2/sym_glue.c | 5 +++-- drivers/video/arkfb.c | 2 +- drivers/video/s3fb.c | 2 +- drivers/video/vt8623fb.c | 2 +- include/linux/pci.h | 4 ++-- 17 files changed, 49 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 2b183b0d3207..99e8d4796c96 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -83,7 +83,7 @@ static int pci_mmap_resource(struct kobject *kobj, if (iomem_is_exclusive(res->start)) return -EINVAL; - pcibios_resource_to_bus(pdev, &bar, res); + pcibios_resource_to_bus(pdev->bus, &bar, res); vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0)); mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; @@ -139,7 +139,7 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) long dense_offset; unsigned long sparse_size; - pcibios_resource_to_bus(pdev, &bar, &pdev->resource[num]); + pcibios_resource_to_bus(pdev->bus, &bar, &pdev->resource[num]); /* All core logic chips have 4G sparse address space, except CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index a1e3e40ca3fd..d9476c1fc959 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -835,7 +835,7 @@ static void pcibios_fixup_resources(struct pci_dev *dev) * at 0 as unset as well, except if PCI_PROBE_ONLY is also set * since in that case, we don't want to re-assign anything */ - pcibios_resource_to_bus(dev, ®, res); + pcibios_resource_to_bus(dev->bus, ®, res); if (pci_has_flag(PCI_REASSIGN_ALL_RSRC) || (reg.start == 0 && !pci_has_flag(PCI_PROBE_ONLY))) { /* Only print message if not re-assigning */ @@ -886,7 +886,7 @@ static int pcibios_uninitialized_bridge_resource(struct pci_bus *bus, /* Job is a bit different between memory and IO */ if (res->flags & IORESOURCE_MEM) { - pcibios_resource_to_bus(dev, ®ion, res); + pcibios_resource_to_bus(dev->bus, ®ion, res); /* If the BAR is non-0 then it's probably been initialized */ if (region.start != 0) diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index ac0b034f9ae0..83c26d829991 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -111,7 +111,7 @@ static void of_pci_parse_addrs(struct device_node *node, struct pci_dev *dev) res->name = pci_name(dev); region.start = base; region.end = base + size - 1; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); } } @@ -280,7 +280,7 @@ void of_scan_pci_bridge(struct pci_dev *dev) res->flags = flags; region.start = of_read_number(&ranges[1], 2); region.end = region.start + size - 1; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); } sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), bus->number); diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index cb021453de2a..7de8d1f590b7 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -392,7 +392,7 @@ static void apb_fake_ranges(struct pci_dev *dev, res->flags = IORESOURCE_IO; region.start = (first << 21); region.end = (last << 21) + ((1 << 21) - 1); - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); pci_read_config_byte(dev, APB_MEM_ADDRESS_MAP, &map); apb_calc_first_last(map, &first, &last); @@ -400,7 +400,7 @@ static void apb_fake_ranges(struct pci_dev *dev, res->flags = IORESOURCE_MEM; region.start = (first << 29); region.end = (last << 29) + ((1 << 29) - 1); - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); } static void pci_of_scan_bus(struct pci_pbm_info *pbm, @@ -491,7 +491,7 @@ static void of_scan_pci_bridge(struct pci_pbm_info *pbm, res->flags = flags; region.start = GET_64BIT(ranges, 1); region.end = region.start + size - 1; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); } after_ranges: sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index a68dc613a5be..06ace6248c61 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -9,22 +9,19 @@ #include "pci.h" -static struct pci_bus *find_pci_root_bus(struct pci_dev *dev) +static struct pci_bus *find_pci_root_bus(struct pci_bus *bus) { - struct pci_bus *bus; - - bus = dev->bus; while (bus->parent) bus = bus->parent; return bus; } -static struct pci_host_bridge *find_pci_host_bridge(struct pci_dev *dev) +static struct pci_host_bridge *find_pci_host_bridge(struct pci_bus *bus) { - struct pci_bus *bus = find_pci_root_bus(dev); + struct pci_bus *root_bus = find_pci_root_bus(bus); - return to_pci_host_bridge(bus->bridge); + return to_pci_host_bridge(root_bus->bridge); } void pci_set_host_bridge_release(struct pci_host_bridge *bridge, @@ -40,10 +37,10 @@ static bool resource_contains(struct resource *res1, struct resource *res2) return res1->start <= res2->start && res1->end >= res2->end; } -void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, +void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res) { - struct pci_host_bridge *bridge = find_pci_host_bridge(dev); + struct pci_host_bridge *bridge = find_pci_host_bridge(bus); struct pci_host_bridge_window *window; resource_size_t offset = 0; @@ -68,10 +65,10 @@ static bool region_contains(struct pci_bus_region *region1, return region1->start <= region2->start && region1->end >= region2->end; } -void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, +void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, struct pci_bus_region *region) { - struct pci_host_bridge *bridge = find_pci_host_bridge(dev); + struct pci_host_bridge *bridge = find_pci_host_bridge(bus); struct pci_host_bridge_window *window; resource_size_t offset = 0; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 38e403dddf6e..f049e3f53fcc 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -269,8 +269,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, region.end = l + sz; } - pcibios_bus_to_resource(dev, res, ®ion); - pcibios_resource_to_bus(dev, &inverted_region, res); + pcibios_bus_to_resource(dev->bus, res, ®ion); + pcibios_resource_to_bus(dev->bus, &inverted_region, res); /* * If "A" is a BAR value (a bus address), "bus_to_resource(A)" is @@ -364,7 +364,7 @@ static void pci_read_bridge_io(struct pci_bus *child) res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; region.start = base; region.end = limit + io_granularity - 1; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -386,7 +386,7 @@ static void pci_read_bridge_mmio(struct pci_bus *child) res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; region.start = base; region.end = limit + 0xfffff; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -436,7 +436,7 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child) res->flags |= IORESOURCE_MEM_64; region.start = base; region.end = limit + 0xfffff; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -1084,24 +1084,24 @@ int pci_setup_device(struct pci_dev *dev) region.end = 0x1F7; res = &dev->resource[0]; res->flags = LEGACY_IO_RESOURCE; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); region.start = 0x3F6; region.end = 0x3F6; res = &dev->resource[1]; res->flags = LEGACY_IO_RESOURCE; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); } if ((progif & 4) == 0) { region.start = 0x170; region.end = 0x177; res = &dev->resource[2]; res->flags = LEGACY_IO_RESOURCE; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); region.start = 0x376; region.end = 0x376; res = &dev->resource[3]; res->flags = LEGACY_IO_RESOURCE; - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); } } break; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b3b1b9aa8863..4ad6bf6c107b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -343,7 +343,7 @@ static void quirk_io_region(struct pci_dev *dev, int port, /* Convert from PCI bus to resource space */ bus_region.start = region; bus_region.end = region + size - 1; - pcibios_bus_to_resource(dev, res, &bus_region); + pcibios_bus_to_resource(dev->bus, res, &bus_region); if (!pci_claim_resource(dev, nr)) dev_info(&dev->dev, "quirk: %pR claimed by %s\n", res, name); diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index c5d0a08a8747..5d595724e5f4 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -31,7 +31,7 @@ int pci_enable_rom(struct pci_dev *pdev) if (!res->flags) return -1; - pcibios_resource_to_bus(pdev, ®ion, res); + pcibios_resource_to_bus(pdev->bus, ®ion, res); pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr); rom_addr &= ~PCI_ROM_ADDRESS_MASK; rom_addr |= region.start | PCI_ROM_ADDRESS_ENABLE; diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 219a4106480a..79339822d80e 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -475,7 +475,7 @@ void pci_setup_cardbus(struct pci_bus *bus) &bus->busn_res); res = bus->resource[0]; - pcibios_resource_to_bus(bridge, ®ion, res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_IO) { /* * The IO resource is allocated a range twice as large as it @@ -489,7 +489,7 @@ void pci_setup_cardbus(struct pci_bus *bus) } res = bus->resource[1]; - pcibios_resource_to_bus(bridge, ®ion, res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_IO) { dev_info(&bridge->dev, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_IO_BASE_1, @@ -499,7 +499,7 @@ void pci_setup_cardbus(struct pci_bus *bus) } res = bus->resource[2]; - pcibios_resource_to_bus(bridge, ®ion, res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_MEM) { dev_info(&bridge->dev, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0, @@ -509,7 +509,7 @@ void pci_setup_cardbus(struct pci_bus *bus) } res = bus->resource[3]; - pcibios_resource_to_bus(bridge, ®ion, res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_MEM) { dev_info(&bridge->dev, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1, @@ -546,7 +546,7 @@ static void pci_setup_bridge_io(struct pci_bus *bus) /* Set up the top and bottom of the PCI I/O segment for this bus. */ res = bus->resource[0]; - pcibios_resource_to_bus(bridge, ®ion, res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_IO) { pci_read_config_dword(bridge, PCI_IO_BASE, &l); l &= 0xffff0000; @@ -578,7 +578,7 @@ static void pci_setup_bridge_mmio(struct pci_bus *bus) /* Set up the top and bottom of the PCI Memory segment for this bus. */ res = bus->resource[1]; - pcibios_resource_to_bus(bridge, ®ion, res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_MEM) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; @@ -604,7 +604,7 @@ static void pci_setup_bridge_mmio_pref(struct pci_bus *bus) /* Set up PREF base/limit. */ bu = lu = 0; res = bus->resource[2]; - pcibios_resource_to_bus(bridge, ®ion, res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_PREFETCH) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; @@ -1422,7 +1422,7 @@ static int iov_resources_unassigned(struct pci_dev *dev, void *data) if (!r->flags) continue; - pcibios_resource_to_bus(dev, ®ion, r); + pcibios_resource_to_bus(dev->bus, ®ion, r); if (!region.start) { *unassigned = true; return 1; /* return early from pci_walk_bus() */ diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 83c4d3bc47ab..5c060b152ce6 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -52,7 +52,7 @@ void pci_update_resource(struct pci_dev *dev, int resno) if (res->flags & IORESOURCE_PCI_FIXED) return; - pcibios_resource_to_bus(dev, ®ion, res); + pcibios_resource_to_bus(dev->bus, ®ion, res); new = region.start | (res->flags & PCI_REGION_FLAG_MASK); if (res->flags & IORESOURCE_IO) diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index 519c4d6003a6..7d47456429a1 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -608,7 +608,7 @@ static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_ enter("i82092aa_set_mem_map"); - pcibios_resource_to_bus(sock_info->dev, ®ion, mem->res); + pcibios_resource_to_bus(sock_info->dev->bus, ®ion, mem->res); map = mem->map; if (map > 4) { diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index dc18a3a5e010..8485761e76af 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -445,7 +445,7 @@ static int yenta_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map * unsigned int start, stop, card_start; unsigned short word; - pcibios_resource_to_bus(socket->dev, ®ion, mem->res); + pcibios_resource_to_bus(socket->dev->bus, ®ion, mem->res); map = mem->map; start = region.start; @@ -709,7 +709,7 @@ static int yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type region.start = config_readl(socket, addr_start) & mask; region.end = config_readl(socket, addr_end) | ~mask; if (region.start && region.end > region.start && !override_bios) { - pcibios_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev->bus, res, ®ion); if (pci_claim_resource(dev, PCI_BRIDGE_RESOURCES + nr) == 0) return 0; dev_printk(KERN_INFO, &dev->dev, @@ -1033,7 +1033,7 @@ static void yenta_config_init(struct yenta_socket *socket) struct pci_dev *dev = socket->dev; struct pci_bus_region region; - pcibios_resource_to_bus(socket->dev, ®ion, &dev->resource[0]); + pcibios_resource_to_bus(socket->dev->bus, ®ion, &dev->resource[0]); config_writel(socket, CB_LEGACY_MODE_BASE, 0); config_writel(socket, PCI_BASE_ADDRESS_0, region.start); diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index bac55f7f69f9..6d3ee1ab6362 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -1531,7 +1531,7 @@ static int sym_iomap_device(struct sym_device *device) struct pci_bus_region bus_addr; int i = 2; - pcibios_resource_to_bus(pdev, &bus_addr, &pdev->resource[1]); + pcibios_resource_to_bus(pdev->bus, &bus_addr, &pdev->resource[1]); device->mmio_base = bus_addr.start; if (device->chip.features & FE_RAM) { @@ -1541,7 +1541,8 @@ static int sym_iomap_device(struct sym_device *device) */ if (!pdev->resource[i].flags) i++; - pcibios_resource_to_bus(pdev, &bus_addr, &pdev->resource[i]); + pcibios_resource_to_bus(pdev->bus, &bus_addr, + &pdev->resource[i]); device->ram_base = bus_addr.start; } diff --git a/drivers/video/arkfb.c b/drivers/video/arkfb.c index a6b29bd4a12a..adc4ea2cc5a0 100644 --- a/drivers/video/arkfb.c +++ b/drivers/video/arkfb.c @@ -1014,7 +1014,7 @@ static int ark_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) vga_res.flags = IORESOURCE_IO; - pcibios_bus_to_resource(dev, &vga_res, &bus_reg); + pcibios_bus_to_resource(dev->bus, &vga_res, &bus_reg); par->state.vgabase = (void __iomem *) vga_res.start; diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c index 968b2997175a..9a3f8f1c6aab 100644 --- a/drivers/video/s3fb.c +++ b/drivers/video/s3fb.c @@ -1180,7 +1180,7 @@ static int s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) vga_res.flags = IORESOURCE_IO; - pcibios_bus_to_resource(dev, &vga_res, &bus_reg); + pcibios_bus_to_resource(dev->bus, &vga_res, &bus_reg); par->state.vgabase = (void __iomem *) vga_res.start; diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c index 8bc6e0958a09..5c7cbc6c6236 100644 --- a/drivers/video/vt8623fb.c +++ b/drivers/video/vt8623fb.c @@ -729,7 +729,7 @@ static int vt8623_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) vga_res.flags = IORESOURCE_IO; - pcibios_bus_to_resource(dev, &vga_res, &bus_reg); + pcibios_bus_to_resource(dev->bus, &vga_res, &bus_reg); par->state.vgabase = (void __iomem *) vga_res.start; diff --git a/include/linux/pci.h b/include/linux/pci.h index 7d5555270491..bf32412704a7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -737,9 +737,9 @@ void pci_fixup_cardbus(struct pci_bus *); /* Generic PCI functions used internally */ -void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, +void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res); -void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, +void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, struct pci_bus_region *region); void pcibios_scan_specific_bus(int busn); struct pci_bus *pci_find_bus(int domain, int busnr); -- cgit v1.2.3-70-g09d2 From 31e45ec3a4e73dcbeb51e03ab559812ba3e82cc2 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 26 Dec 2013 16:52:41 +0100 Subject: PCI: mvebu: Call pci_ioremap_io() at startup instead of dynamically The mvebu PCI host controller driver uses an emulated PCI-to-PCI bridge to leverage the core PCI kernel enumeration logic to dynamically create and remove the MBus windows needed to access the memory and I/O regions of each PCI interface. In the context of this PCI-to-PCI bridge emulation, the driver emulates all reads and writes to the PCI bridge registers. Upon a write to the registers configuring the I/O base and limit, the driver was creating the MBus window and calling pci_ioremap_io() to setup the mapping. However, it turns out that accesses to these registers are made in an IRQ disabled context, while pci_ioremap_io() is a potentially sleeping function. Not only this is wrong, but it is causing fairly loud warnings at boot time when the appropriate kernel hacking options are enabled. This patch solves this by moving the pci_ioremap_io() call to the startup of the driver. At this point, we don't know how many PCI interfaces will be enabled, so we are simply remapping the entire PCI I/O space to virtual addresses. This is reasonable since this I/O space is limited to 1 MB in size, and also because the MBus windows continue to be created in a dynamic fashion only when devices need them. Signed-off-by: Thomas Petazzoni Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pci-mvebu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index afd2af04980c..18057905689a 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -342,8 +342,6 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) mvebu_mbus_add_window_remap_by_id(port->io_target, port->io_attr, port->iowin_base, port->iowin_size, iobase); - - pci_ioremap_io(iobase, port->iowin_base); } static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) @@ -988,6 +986,10 @@ static int mvebu_pcie_probe(struct platform_device *pdev) } pcie->nports = i; + + for (i = 0; i < (IO_SPACE_LIMIT - SZ_64K); i += SZ_64K) + pci_ioremap_io(i, pcie->io.start + i); + mvebu_pcie_msi_enable(pcie); mvebu_pcie_enable(pcie); -- cgit v1.2.3-70-g09d2 From 06489002a175680e18b4c0dd0beb6aff2a6d3781 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 27 Dec 2013 09:34:36 +0900 Subject: PCI: mvebu: Use max_t() instead of max(resource_size_t,) Use max_t() instead of max(resource_size_t,) in order to fix the following checkpatch warning. WARNING: max() should probably be max_t(resource_size_t, SZ_64K, size) WARNING: max() should probably be max_t(resource_size_t, SZ_1M, size) Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas Acked-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 18057905689a..7889feb632ed 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -728,9 +728,9 @@ static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, * aligned on their size */ if (res->flags & IORESOURCE_IO) - return round_up(start, max((resource_size_t)SZ_64K, size)); + return round_up(start, max_t(resource_size_t, SZ_64K, size)); else if (res->flags & IORESOURCE_MEM) - return round_up(start, max((resource_size_t)SZ_1M, size)); + return round_up(start, max_t(resource_size_t, SZ_1M, size)); else return start; } -- cgit v1.2.3-70-g09d2 From 58275f2f0a6acd750b0acdc62d6457fb3e0f264e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 27 Dec 2013 09:30:25 +0900 Subject: PCI: designware: Fix indent code style Fix indent code style and replace 'MSI interrupt controller' of comment with 'MSI controller' to fix the following checkpatch issues: ERROR: code indent should use tabs where possible WARNING: please, no spaces at the start of a line WARNING: line over 80 characters Signed-off-by: Jingoo Han Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-designware.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 4a08d30548ce..17ce88f79d2b 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -213,14 +213,14 @@ static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) } static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, - unsigned int nvec, unsigned int pos) + unsigned int nvec, unsigned int pos) { unsigned int i, res, bit, val; for (i = 0; i < nvec; i++) { irq_set_msi_desc_off(irq_base, i, NULL); clear_bit(pos + i, pp->msi_irq_in_use); - /* Disable corresponding interrupt on MSI interrupt controller */ + /* Disable corresponding interrupt on MSI controller */ res = ((pos + i) / 32) * 12; bit = (pos + i) % 32; dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); -- cgit v1.2.3-70-g09d2 From 774104399459fe4c4195bdc657a898e292334137 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 2 Jan 2014 16:05:57 -0800 Subject: PCI: Convert ioapic to be builtin only, not modular Convert pci/ioapic.c to be builtin only, with no module option, so we can support IO-APIC hotplug. Also make it depend on X86_IO_APIC. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/Kconfig | 3 ++- drivers/pci/ioapic.c | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index b6a99f7a9b20..893503fa1782 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -105,9 +105,10 @@ config PCI_PASID If unsure, say N. config PCI_IOAPIC - tristate "PCI IO-APIC hotplug support" if X86 + bool "PCI IO-APIC hotplug support" if X86 depends on PCI depends on ACPI + depends on X86_IO_APIC default !X86 config PCI_LABEL diff --git a/drivers/pci/ioapic.c b/drivers/pci/ioapic.c index 50ce68098298..2c2930ea06ad 100644 --- a/drivers/pci/ioapic.c +++ b/drivers/pci/ioapic.c @@ -113,6 +113,10 @@ static struct pci_driver ioapic_driver = { .remove = ioapic_remove, }; -module_pci_driver(ioapic_driver); +static int __init ioapic_init(void) +{ + return pci_register_driver(&ioapic_driver); +} +module_init(ioapic_init); MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From d1ac1d2622e8f0fd2a25127a8649d135b54db8a9 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 30 Dec 2013 08:28:13 +0100 Subject: PCI/MSI: Add pci_msi_vec_count() Device drivers can use this interface to obtain the maximum number of MSI interrupts the device supports and use that number, e.g., in a subsequent call to pci_enable_msi_block(). Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas Reviewed-by: Tejun Heo --- Documentation/PCI/MSI-HOWTO.txt | 15 +++++++++++++++ drivers/pci/msi.c | 41 +++++++++++++++++++++++++++++++++-------- include/linux/pci.h | 6 ++++++ 3 files changed, 54 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index a4d174e95413..a8b41788dfde 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -169,6 +169,21 @@ on any interrupt for which it previously called request_irq(). Failure to do so results in a BUG_ON(), leaving the device with MSI enabled and thus leaking its vector. +4.2.5 pci_msi_vec_count + +int pci_msi_vec_count(struct pci_dev *dev) + +This function could be used to retrieve the number of MSI vectors the +device requested (via the Multiple Message Capable register). The MSI +specification only allows the returned value to be a power of two, +up to a maximum of 2^5 (32). + +If this function returns a negative number, it indicates the device is +not capable of sending MSIs. + +If this function returns a positive number, it indicates the maximum +number of MSI interrupt vectors that could be allocated. + 4.3 Using MSI-X The MSI-X capability is much more flexible than the MSI capability. diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index ce0d4eb91a22..ba6d0a9bdd39 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -842,6 +842,31 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) return 0; } +/** + * pci_msi_vec_count - Return the number of MSI vectors a device can send + * @dev: device to report about + * + * This function returns the number of MSI vectors a device requested via + * Multiple Message Capable register. It returns a negative errno if the + * device is not capable sending MSI interrupts. Otherwise, the call succeeds + * and returns a power of two, up to a maximum of 2^5 (32), according to the + * MSI specification. + **/ +int pci_msi_vec_count(struct pci_dev *dev) +{ + int ret; + u16 msgctl; + + if (!dev->msi_cap) + return -EINVAL; + + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); + ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); + + return ret; +} +EXPORT_SYMBOL(pci_msi_vec_count); + /** * pci_enable_msi_block - configure device's MSI capability structure * @dev: device to configure @@ -858,13 +883,13 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) int pci_enable_msi_block(struct pci_dev *dev, int nvec) { int status, maxvec; - u16 msgctl; - if (!dev->msi_cap || dev->current_state != PCI_D0) + if (dev->current_state != PCI_D0) return -EINVAL; - pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); - maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); + maxvec = pci_msi_vec_count(dev); + if (maxvec < 0) + return maxvec; if (nvec > maxvec) return maxvec; @@ -889,13 +914,13 @@ EXPORT_SYMBOL(pci_enable_msi_block); int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec) { int ret, nvec; - u16 msgctl; - if (!dev->msi_cap || dev->current_state != PCI_D0) + if (dev->current_state != PCI_D0) return -EINVAL; - pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); - ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); + ret = pci_msi_vec_count(dev); + if (ret < 0) + return ret; if (maxvec) *maxvec = ret; diff --git a/include/linux/pci.h b/include/linux/pci.h index 7c34c3913bcb..6691de093f1c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1154,6 +1154,11 @@ struct msix_entry { #ifndef CONFIG_PCI_MSI +static inline int pci_msi_vec_count(struct pci_dev *dev) +{ + return -ENOSYS; +} + static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec) { return -ENOSYS; @@ -1195,6 +1200,7 @@ static inline int pci_msi_enabled(void) return 0; } #else +int pci_msi_vec_count(struct pci_dev *dev); int pci_enable_msi_block(struct pci_dev *dev, int nvec); int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec); void pci_msi_shutdown(struct pci_dev *dev); -- cgit v1.2.3-70-g09d2 From 7b92b4f61ec49cb1a5813298f35258bd7ecd3667 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 30 Dec 2013 08:28:14 +0100 Subject: PCI/MSI: Remove pci_enable_msi_block_auto() The new pci_msi_vec_count() interface makes pci_enable_msi_block_auto() superfluous. Drivers can use pci_msi_vec_count() to learn the maximum number of MSIs supported by the device, and then call pci_enable_msi_block(). pci_enable_msi_block_auto() was introduced recently, and its only user is the AHCI driver, which is also updated by this change. Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas Acked-by: Tejun Heo --- Documentation/PCI/MSI-HOWTO.txt | 39 +++++----------------------- drivers/ata/ahci.c | 56 +++++++++++++++++++++++++---------------- drivers/pci/msi.c | 25 ------------------ include/linux/pci.h | 7 ------ 4 files changed, 41 insertions(+), 86 deletions(-) (limited to 'drivers') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index a8b41788dfde..aa4ad987510d 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -127,49 +127,22 @@ on the number of vectors that can be allocated; pci_enable_msi_block() returns as soon as it finds any constraint that doesn't allow the call to succeed. -4.2.3 pci_enable_msi_block_auto - -int pci_enable_msi_block_auto(struct pci_dev *dev, int *count) - -This variation on pci_enable_msi() call allows a device driver to request -the maximum possible number of MSIs. The MSI specification only allows -interrupts to be allocated in powers of two, up to a maximum of 2^5 (32). - -If this function returns a positive number, it indicates that it has -succeeded and the returned value is the number of allocated interrupts. In -this case, the function enables MSI on this device and updates dev->irq to -be the lowest of the new interrupts assigned to it. The other interrupts -assigned to the device are in the range dev->irq to dev->irq + returned -value - 1. - -If this function returns a negative number, it indicates an error and -the driver should not attempt to request any more MSI interrupts for -this device. - -If the device driver needs to know the number of interrupts the device -supports it can pass the pointer count where that number is stored. The -device driver must decide what action to take if pci_enable_msi_block_auto() -succeeds, but returns a value less than the number of interrupts supported. -If the device driver does not need to know the number of interrupts -supported, it can set the pointer count to NULL. - -4.2.4 pci_disable_msi +4.2.3 pci_disable_msi void pci_disable_msi(struct pci_dev *dev) This function should be used to undo the effect of pci_enable_msi() or -pci_enable_msi_block() or pci_enable_msi_block_auto(). Calling it restores -dev->irq to the pin-based interrupt number and frees the previously -allocated message signaled interrupt(s). The interrupt may subsequently be -assigned to another device, so drivers should not cache the value of -dev->irq. +pci_enable_msi_block(). Calling it restores dev->irq to the pin-based +interrupt number and frees the previously allocated message signaled +interrupt(s). The interrupt may subsequently be assigned to another +device, so drivers should not cache the value of dev->irq. Before calling this function, a device driver must always call free_irq() on any interrupt for which it previously called request_irq(). Failure to do so results in a BUG_ON(), leaving the device with MSI enabled and thus leaking its vector. -4.2.5 pci_msi_vec_count +4.2.4 pci_msi_vec_count int pci_msi_vec_count(struct pci_dev *dev) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index e2903d03180e..8516f4d47893 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1095,26 +1095,40 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) {} #endif -int ahci_init_interrupts(struct pci_dev *pdev, struct ahci_host_priv *hpriv) +int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, + struct ahci_host_priv *hpriv) { - int rc; - unsigned int maxvec; + int rc, nvec; - if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) { - rc = pci_enable_msi_block_auto(pdev, &maxvec); - if (rc > 0) { - if ((rc == maxvec) || (rc == 1)) - return rc; - /* - * Assume that advantage of multipe MSIs is negated, - * so fallback to single MSI mode to save resources - */ - pci_disable_msi(pdev); - if (!pci_enable_msi(pdev)) - return 1; - } - } + if (hpriv->flags & AHCI_HFLAG_NO_MSI) + goto intx; + + rc = pci_msi_vec_count(pdev); + if (rc < 0) + goto intx; + + /* + * If number of MSIs is less than number of ports then Sharing Last + * Message mode could be enforced. In this case assume that advantage + * of multipe MSIs is negated and use single MSI mode instead. + */ + if (rc < n_ports) + goto single_msi; + + nvec = rc; + rc = pci_enable_msi_block(pdev, nvec); + if (rc) + goto intx; + return nvec; + +single_msi: + rc = pci_enable_msi(pdev); + if (rc) + goto intx; + return 1; + +intx: pci_intx(pdev, 1); return 0; } @@ -1281,10 +1295,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; - n_msis = ahci_init_interrupts(pdev, hpriv); - if (n_msis > 1) - hpriv->flags |= AHCI_HFLAG_MULTI_MSI; - /* save initial config */ ahci_pci_save_initial_config(pdev, hpriv); @@ -1339,6 +1349,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) */ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + n_msis = ahci_init_interrupts(pdev, n_ports, hpriv); + if (n_msis > 1) + hpriv->flags |= AHCI_HFLAG_MULTI_MSI; + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); if (!host) return -ENOMEM; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index ba6d0a9bdd39..76507ab13beb 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -911,31 +911,6 @@ int pci_enable_msi_block(struct pci_dev *dev, int nvec) } EXPORT_SYMBOL(pci_enable_msi_block); -int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec) -{ - int ret, nvec; - - if (dev->current_state != PCI_D0) - return -EINVAL; - - ret = pci_msi_vec_count(dev); - if (ret < 0) - return ret; - - if (maxvec) - *maxvec = ret; - - do { - nvec = ret; - ret = pci_enable_msi_block(dev, nvec); - } while (ret > 0); - - if (ret < 0) - return ret; - return nvec; -} -EXPORT_SYMBOL(pci_enable_msi_block_auto); - void pci_msi_shutdown(struct pci_dev *dev) { struct msi_desc *desc; diff --git a/include/linux/pci.h b/include/linux/pci.h index 6691de093f1c..86dcf006adcc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1164,12 +1164,6 @@ static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec) return -ENOSYS; } -static inline int -pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec) -{ - return -ENOSYS; -} - static inline void pci_msi_shutdown(struct pci_dev *dev) { } static inline void pci_disable_msi(struct pci_dev *dev) @@ -1202,7 +1196,6 @@ static inline int pci_msi_enabled(void) #else int pci_msi_vec_count(struct pci_dev *dev); int pci_enable_msi_block(struct pci_dev *dev, int nvec); -int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec); void pci_msi_shutdown(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev); int pci_msix_table_size(struct pci_dev *dev); -- cgit v1.2.3-70-g09d2 From ff1aa430a2fa43189e89c7ddd559f0bee2298288 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 30 Dec 2013 08:28:15 +0100 Subject: PCI/MSI: Add pci_msix_vec_count() This creates an MSI-X counterpart for pci_msi_vec_count(). Device drivers can use this function to obtain maximum number of MSI-X interrupts the device supports and use that number in a subsequent call to pci_enable_msix(). pci_msix_vec_count() supersedes pci_msix_table_size() and returns a negative errno if device does not support MSI-X interrupts. After this update, callers must always check the returned value. The only user of pci_msix_table_size() was the PCI-Express port driver, which is also updated by this change. Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas Reviewed-by: Tejun Heo --- Documentation/PCI/MSI-HOWTO.txt | 13 +++++++++++++ drivers/pci/msi.c | 18 +++++++++++++----- drivers/pci/pcie/portdrv_core.c | 7 ++++--- include/linux/pci.h | 6 +++--- 4 files changed, 33 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index aa4ad987510d..b58f4a4d14bb 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -243,6 +243,19 @@ MSI-X Table. This address is mapped by the PCI subsystem, and should not be accessed directly by the device driver. If the driver wishes to mask or unmask an interrupt, it should call disable_irq() / enable_irq(). +4.3.4 pci_msix_vec_count + +int pci_msix_vec_count(struct pci_dev *dev) + +This function could be used to retrieve number of entries in the device +MSI-X table. + +If this function returns a negative number, it indicates the device is +not capable of sending MSI-Xs. + +If this function returns a positive number, it indicates the maximum +number of MSI-X interrupt vectors that could be allocated. + 4.4 Handling devices implementing both MSI and MSI-X capabilities If a device implements both MSI and MSI-X capabilities, it can diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 76507ab13beb..bd18ecf74c55 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -948,19 +948,25 @@ void pci_disable_msi(struct pci_dev *dev) EXPORT_SYMBOL(pci_disable_msi); /** - * pci_msix_table_size - return the number of device's MSI-X table entries + * pci_msix_vec_count - return the number of device's MSI-X table entries * @dev: pointer to the pci_dev data structure of MSI-X device function - */ -int pci_msix_table_size(struct pci_dev *dev) + + * This function returns the number of device's MSI-X table entries and + * therefore the number of MSI-X vectors device is capable of sending. + * It returns a negative errno if the device is not capable of sending MSI-X + * interrupts. + **/ +int pci_msix_vec_count(struct pci_dev *dev) { u16 control; if (!dev->msix_cap) - return 0; + return -EINVAL; pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); return msix_table_size(control); } +EXPORT_SYMBOL(pci_msix_vec_count); /** * pci_enable_msix - configure device's MSI-X capability structure @@ -989,7 +995,9 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) if (status) return status; - nr_entries = pci_msix_table_size(dev); + nr_entries = pci_msix_vec_count(dev); + if (nr_entries < 0) + return nr_entries; if (nvec > nr_entries) return nr_entries; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 0b6e76604068..4ab719826dd7 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -79,9 +79,10 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) u16 reg16; u32 reg32; - nr_entries = pci_msix_table_size(dev); - if (!nr_entries) - return -EINVAL; + nr_entries = pci_msix_vec_count(dev); + if (nr_entries < 0) + return nr_entries; + BUG_ON(!nr_entries); if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES) nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES; diff --git a/include/linux/pci.h b/include/linux/pci.h index 86dcf006adcc..cf6125ba649d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1169,9 +1169,9 @@ static inline void pci_msi_shutdown(struct pci_dev *dev) static inline void pci_disable_msi(struct pci_dev *dev) { } -static inline int pci_msix_table_size(struct pci_dev *dev) +static inline int pci_msix_vec_count(struct pci_dev *dev) { - return 0; + return -ENOSYS; } static inline int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) @@ -1198,7 +1198,7 @@ int pci_msi_vec_count(struct pci_dev *dev); int pci_enable_msi_block(struct pci_dev *dev, int nvec); void pci_msi_shutdown(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev); -int pci_msix_table_size(struct pci_dev *dev); +int pci_msix_vec_count(struct pci_dev *dev); int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec); void pci_msix_shutdown(struct pci_dev *dev); void pci_disable_msix(struct pci_dev *dev); -- cgit v1.2.3-70-g09d2 From 302a2523c277bea0bbe8340312b09507905849ed Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 30 Dec 2013 08:28:16 +0100 Subject: PCI/MSI: Add pci_enable_msi_range() and pci_enable_msix_range() This adds pci_enable_msi_range(), which supersedes the pci_enable_msi() and pci_enable_msi_block() MSI interfaces. It also adds pci_enable_msix_range(), which supersedes the pci_enable_msix() MSI-X interface. The old interfaces have three categories of return values: negative: failure; caller should not retry positive: failure; value indicates number of interrupts that *could* have been allocated, and caller may retry with a smaller request zero: success; at least as many interrupts allocated as requested It is error-prone to handle these three cases correctly in drivers. The new functions return either a negative error code or a number of successfully allocated MSI/MSI-X interrupts, which is expected to lead to clearer device driver code. pci_enable_msi(), pci_enable_msi_block() and pci_enable_msix() still exist unchanged, but are deprecated and may be removed after callers are updated. [bhelgaas: tweak changelog] Suggested-by: Ben Hutchings Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas Reviewed-by: Tejun Heo --- Documentation/PCI/MSI-HOWTO.txt | 261 ++++++++++++++++++++++++++++------------ drivers/pci/msi.c | 74 ++++++++++++ include/linux/pci.h | 14 +++ 3 files changed, 274 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index b58f4a4d14bb..a8d01005f480 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -82,67 +82,97 @@ Most of the hard work is done for the driver in the PCI layer. It simply has to request that the PCI layer set up the MSI capability for this device. -4.2.1 pci_enable_msi +4.2.1 pci_enable_msi_range -int pci_enable_msi(struct pci_dev *dev) +int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) -A successful call allocates ONE interrupt to the device, regardless -of how many MSIs the device supports. The device is switched from -pin-based interrupt mode to MSI mode. The dev->irq number is changed -to a new number which represents the message signaled interrupt; -consequently, this function should be called before the driver calls -request_irq(), because an MSI is delivered via a vector that is -different from the vector of a pin-based interrupt. +This function allows a device driver to request any number of MSI +interrupts within specified range from 'minvec' to 'maxvec'. -4.2.2 pci_enable_msi_block +If this function returns a positive number it indicates the number of +MSI interrupts that have been successfully allocated. In this case +the device is switched from pin-based interrupt mode to MSI mode and +updates dev->irq to be the lowest of the new interrupts assigned to it. +The other interrupts assigned to the device are in the range dev->irq +to dev->irq + returned value - 1. Device driver can use the returned +number of successfully allocated MSI interrupts to further allocate +and initialize device resources. -int pci_enable_msi_block(struct pci_dev *dev, int count) +If this function returns a negative number, it indicates an error and +the driver should not attempt to request any more MSI interrupts for +this device. -This variation on the above call allows a device driver to request multiple -MSIs. The MSI specification only allows interrupts to be allocated in -powers of two, up to a maximum of 2^5 (32). +This function should be called before the driver calls request_irq(), +because MSI interrupts are delivered via vectors that are different +from the vector of a pin-based interrupt. -If this function returns 0, it has succeeded in allocating at least as many -interrupts as the driver requested (it may have allocated more in order -to satisfy the power-of-two requirement). In this case, the function -enables MSI on this device and updates dev->irq to be the lowest of -the new interrupts assigned to it. The other interrupts assigned to -the device are in the range dev->irq to dev->irq + count - 1. +It is ideal if drivers can cope with a variable number of MSI interrupts; +there are many reasons why the platform may not be able to provide the +exact number that a driver asks for. -If this function returns a negative number, it indicates an error and -the driver should not attempt to request any more MSI interrupts for -this device. If this function returns a positive number, it is -less than 'count' and indicates the number of interrupts that could have -been allocated. In neither case is the irq value updated or the device -switched into MSI mode. - -The device driver must decide what action to take if -pci_enable_msi_block() returns a value less than the number requested. -For instance, the driver could still make use of fewer interrupts; -in this case the driver should call pci_enable_msi_block() -again. Note that it is not guaranteed to succeed, even when the -'count' has been reduced to the value returned from a previous call to -pci_enable_msi_block(). This is because there are multiple constraints -on the number of vectors that can be allocated; pci_enable_msi_block() -returns as soon as it finds any constraint that doesn't allow the -call to succeed. - -4.2.3 pci_disable_msi +There could be devices that can not operate with just any number of MSI +interrupts within a range. See chapter 4.3.1.3 to get the idea how to +handle such devices for MSI-X - the same logic applies to MSI. + +4.2.1.1 Maximum possible number of MSI interrupts + +The typical usage of MSI interrupts is to allocate as many vectors as +possible, likely up to the limit returned by pci_msi_vec_count() function: + +static int foo_driver_enable_msi(struct pci_dev *pdev, int nvec) +{ + return pci_enable_msi_range(pdev, 1, nvec); +} + +Note the value of 'minvec' parameter is 1. As 'minvec' is inclusive, +the value of 0 would be meaningless and could result in error. + +Some devices have a minimal limit on number of MSI interrupts. +In this case the function could look like this: + +static int foo_driver_enable_msi(struct pci_dev *pdev, int nvec) +{ + return pci_enable_msi_range(pdev, FOO_DRIVER_MINIMUM_NVEC, nvec); +} + +4.2.1.2 Exact number of MSI interrupts + +If a driver is unable or unwilling to deal with a variable number of MSI +interrupts it could request a particular number of interrupts by passing +that number to pci_enable_msi_range() function as both 'minvec' and 'maxvec' +parameters: + +static int foo_driver_enable_msi(struct pci_dev *pdev, int nvec) +{ + return pci_enable_msi_range(pdev, nvec, nvec); +} + +4.2.1.3 Single MSI mode + +The most notorious example of the request type described above is +enabling the single MSI mode for a device. It could be done by passing +two 1s as 'minvec' and 'maxvec': + +static int foo_driver_enable_single_msi(struct pci_dev *pdev) +{ + return pci_enable_msi_range(pdev, 1, 1); +} + +4.2.2 pci_disable_msi void pci_disable_msi(struct pci_dev *dev) -This function should be used to undo the effect of pci_enable_msi() or -pci_enable_msi_block(). Calling it restores dev->irq to the pin-based -interrupt number and frees the previously allocated message signaled -interrupt(s). The interrupt may subsequently be assigned to another -device, so drivers should not cache the value of dev->irq. +This function should be used to undo the effect of pci_enable_msi_range(). +Calling it restores dev->irq to the pin-based interrupt number and frees +the previously allocated MSIs. The interrupts may subsequently be assigned +to another device, so drivers should not cache the value of dev->irq. Before calling this function, a device driver must always call free_irq() on any interrupt for which it previously called request_irq(). Failure to do so results in a BUG_ON(), leaving the device with MSI enabled and thus leaking its vector. -4.2.4 pci_msi_vec_count +4.2.3 pci_msi_vec_count int pci_msi_vec_count(struct pci_dev *dev) @@ -176,26 +206,31 @@ in each element of the array to indicate for which entries the kernel should assign interrupts; it is invalid to fill in two entries with the same number. -4.3.1 pci_enable_msix +4.3.1 pci_enable_msix_range -int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) +int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, + int minvec, int maxvec) -Calling this function asks the PCI subsystem to allocate 'nvec' MSIs. +Calling this function asks the PCI subsystem to allocate any number of +MSI-X interrupts within specified range from 'minvec' to 'maxvec'. The 'entries' argument is a pointer to an array of msix_entry structs -which should be at least 'nvec' entries in size. On success, the -device is switched into MSI-X mode and the function returns 0. -The 'vector' member in each entry is populated with the interrupt number; +which should be at least 'maxvec' entries in size. + +On success, the device is switched into MSI-X mode and the function +returns the number of MSI-X interrupts that have been successfully +allocated. In this case the 'vector' member in entries numbered from +0 to the returned value - 1 is populated with the interrupt number; the driver should then call request_irq() for each 'vector' that it decides to use. The device driver is responsible for keeping track of the interrupts assigned to the MSI-X vectors so it can free them again later. +Device driver can use the returned number of successfully allocated MSI-X +interrupts to further allocate and initialize device resources. If this function returns a negative number, it indicates an error and the driver should not attempt to allocate any more MSI-X interrupts for -this device. If it returns a positive number, it indicates the maximum -number of interrupt vectors that could have been allocated. See example -below. +this device. -This function, in contrast with pci_enable_msi(), does not adjust +This function, in contrast with pci_enable_msi_range(), does not adjust dev->irq. The device will not generate interrupts for this interrupt number once MSI-X is enabled. @@ -206,28 +241,103 @@ It is ideal if drivers can cope with a variable number of MSI-X interrupts; there are many reasons why the platform may not be able to provide the exact number that a driver asks for. -A request loop to achieve that might look like: +There could be devices that can not operate with just any number of MSI-X +interrupts within a range. E.g., an network adapter might need let's say +four vectors per each queue it provides. Therefore, a number of MSI-X +interrupts allocated should be a multiple of four. In this case interface +pci_enable_msix_range() can not be used alone to request MSI-X interrupts +(since it can allocate any number within the range, without any notion of +the multiple of four) and the device driver should master a custom logic +to request the required number of MSI-X interrupts. + +4.3.1.1 Maximum possible number of MSI-X interrupts + +The typical usage of MSI-X interrupts is to allocate as many vectors as +possible, likely up to the limit returned by pci_msix_vec_count() function: + +static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec) +{ + return pci_enable_msi_range(adapter->pdev, adapter->msix_entries, + 1, nvec); +} + +Note the value of 'minvec' parameter is 1. As 'minvec' is inclusive, +the value of 0 would be meaningless and could result in error. + +Some devices have a minimal limit on number of MSI-X interrupts. +In this case the function could look like this: static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec) { - while (nvec >= FOO_DRIVER_MINIMUM_NVEC) { - rc = pci_enable_msix(adapter->pdev, - adapter->msix_entries, nvec); - if (rc > 0) - nvec = rc; - else - return rc; + return pci_enable_msi_range(adapter->pdev, adapter->msix_entries, + FOO_DRIVER_MINIMUM_NVEC, nvec); +} + +4.3.1.2 Exact number of MSI-X interrupts + +If a driver is unable or unwilling to deal with a variable number of MSI-X +interrupts it could request a particular number of interrupts by passing +that number to pci_enable_msix_range() function as both 'minvec' and 'maxvec' +parameters: + +static int foo_driver_enable_msix(struct foo_adapter *adapter, int nvec) +{ + return pci_enable_msi_range(adapter->pdev, adapter->msix_entries, + nvec, nvec); +} + +4.3.1.3 Specific requirements to the number of MSI-X interrupts + +As noted above, there could be devices that can not operate with just any +number of MSI-X interrupts within a range. E.g., let's assume a device that +is only capable sending the number of MSI-X interrupts which is a power of +two. A routine that enables MSI-X mode for such device might look like this: + +/* + * Assume 'minvec' and 'maxvec' are non-zero + */ +static int foo_driver_enable_msix(struct foo_adapter *adapter, + int minvec, int maxvec) +{ + int rc; + + minvec = roundup_pow_of_two(minvec); + maxvec = rounddown_pow_of_two(maxvec); + + if (minvec > maxvec) + return -ERANGE; + +retry: + rc = pci_enable_msix_range(adapter->pdev, adapter->msix_entries, + maxvec, maxvec); + /* + * -ENOSPC is the only error code allowed to be analized + */ + if (rc == -ENOSPC) { + if (maxvec == 1) + return -ENOSPC; + + maxvec /= 2; + + if (minvec > maxvec) + return -ENOSPC; + + goto retry; } - return -ENOSPC; + return rc; } +Note how pci_enable_msix_range() return value is analized for a fallback - +any error code other than -ENOSPC indicates a fatal error and should not +be retried. + 4.3.2 pci_disable_msix void pci_disable_msix(struct pci_dev *dev) -This function should be used to undo the effect of pci_enable_msix(). It frees -the previously allocated message signaled interrupts. The interrupts may +This function should be used to undo the effect of pci_enable_msix_range(). +It frees the previously allocated MSI-X interrupts. The interrupts may subsequently be assigned to another device, so drivers should not cache the value of the 'vector' elements over a call to pci_disable_msix(). @@ -261,13 +371,14 @@ number of MSI-X interrupt vectors that could be allocated. If a device implements both MSI and MSI-X capabilities, it can run in either MSI mode or MSI-X mode, but not both simultaneously. This is a requirement of the PCI spec, and it is enforced by the -PCI layer. Calling pci_enable_msi() when MSI-X is already enabled or -pci_enable_msix() when MSI is already enabled results in an error. -If a device driver wishes to switch between MSI and MSI-X at runtime, -it must first quiesce the device, then switch it back to pin-interrupt -mode, before calling pci_enable_msi() or pci_enable_msix() and resuming -operation. This is not expected to be a common operation but may be -useful for debugging or testing during development. +PCI layer. Calling pci_enable_msi_range() when MSI-X is already +enabled or pci_enable_msix_range() when MSI is already enabled +results in an error. If a device driver wishes to switch between MSI +and MSI-X at runtime, it must first quiesce the device, then switch +it back to pin-interrupt mode, before calling pci_enable_msi_range() +or pci_enable_msix_range() and resuming operation. This is not expected +to be a common operation but may be useful for debugging or testing +during development. 4.5 Considerations when using MSIs @@ -382,5 +493,5 @@ or disabled (0). If 0 is found in any of the msi_bus files belonging to bridges between the PCI root and the device, MSIs are disabled. It is also worth checking the device driver to see whether it supports MSIs. -For example, it may contain calls to pci_enable_msi(), pci_enable_msix() or -pci_enable_msi_block(). +For example, it may contain calls to pci_enable_msi_range() or +pci_enable_msix_range(). diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index bd18ecf74c55..e6bd8e57d92d 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1102,3 +1102,77 @@ void pci_msi_init_pci_dev(struct pci_dev *dev) if (dev->msix_cap) msix_set_enable(dev, 0); } + +/** + * pci_enable_msi_range - configure device's MSI capability structure + * @dev: device to configure + * @minvec: minimal number of interrupts to configure + * @maxvec: maximum number of interrupts to configure + * + * This function tries to allocate a maximum possible number of interrupts in a + * range between @minvec and @maxvec. It returns a negative errno if an error + * occurs. If it succeeds, it returns the actual number of interrupts allocated + * and updates the @dev's irq member to the lowest new interrupt number; + * the other interrupt numbers allocated to this device are consecutive. + **/ +int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) +{ + int nvec = maxvec; + int rc; + + if (maxvec < minvec) + return -ERANGE; + + do { + rc = pci_enable_msi_block(dev, nvec); + if (rc < 0) { + return rc; + } else if (rc > 0) { + if (rc < minvec) + return -ENOSPC; + nvec = rc; + } + } while (rc); + + return nvec; +} +EXPORT_SYMBOL(pci_enable_msi_range); + +/** + * pci_enable_msix_range - configure device's MSI-X capability structure + * @dev: pointer to the pci_dev data structure of MSI-X device function + * @entries: pointer to an array of MSI-X entries + * @minvec: minimum number of MSI-X irqs requested + * @maxvec: maximum number of MSI-X irqs requested + * + * Setup the MSI-X capability structure of device function with a maximum + * possible number of interrupts in the range between @minvec and @maxvec + * upon its software driver call to request for MSI-X mode enabled on its + * hardware device function. It returns a negative errno if an error occurs. + * If it succeeds, it returns the actual number of interrupts allocated and + * indicates the successful configuration of MSI-X capability structure + * with new allocated MSI-X interrupts. + **/ +int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, + int minvec, int maxvec) +{ + int nvec = maxvec; + int rc; + + if (maxvec < minvec) + return -ERANGE; + + do { + rc = pci_enable_msix(dev, entries, nvec); + if (rc < 0) { + return rc; + } else if (rc > 0) { + if (rc < minvec) + return -ENOSPC; + nvec = rc; + } + } while (rc); + + return nvec; +} +EXPORT_SYMBOL(pci_enable_msix_range); diff --git a/include/linux/pci.h b/include/linux/pci.h index cf6125ba649d..6e32a2820126 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1193,6 +1193,17 @@ static inline int pci_msi_enabled(void) { return 0; } + +static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec, + int maxvec) +{ + return -ENOSYS; +} +static inline int pci_enable_msix_range(struct pci_dev *dev, + struct msix_entry *entries, int minvec, int maxvec) +{ + return -ENOSYS; +} #else int pci_msi_vec_count(struct pci_dev *dev); int pci_enable_msi_block(struct pci_dev *dev, int nvec); @@ -1205,6 +1216,9 @@ void pci_disable_msix(struct pci_dev *dev); void msi_remove_pci_irq_vectors(struct pci_dev *dev); void pci_restore_msi_state(struct pci_dev *dev); int pci_msi_enabled(void); +int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec); +int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, + int minvec, int maxvec); #endif #ifdef CONFIG_PCIEPORTBUS -- cgit v1.2.3-70-g09d2 From e501b3d87f003dfad8fcbd0f55ae17ea52495a56 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 3 Jan 2014 18:26:58 -0700 Subject: agp: Support 64-bit APBASE Per the AGP 3.0 spec, APBASE is a standard PCI BAR and may be either 32 bits or 64 bits wide. Many drivers read APBASE directly, but they only handled 32-bit BARs. The PCI core reads APBASE at enumeration-time. Use pci_bus_address() instead of reading it again in the driver. This works correctly for both 32-bit and 64-bit BARs. Signed-off-by: Bjorn Helgaas Reviewed-by: Daniel Vetter --- drivers/char/agp/agp.h | 1 + drivers/char/agp/ali-agp.c | 4 ++-- drivers/char/agp/amd-k7-agp.c | 4 +--- drivers/char/agp/amd64-agp.c | 5 +---- drivers/char/agp/ati-agp.c | 9 ++++---- drivers/char/agp/efficeon-agp.c | 5 ++--- drivers/char/agp/generic.c | 4 ++-- drivers/char/agp/intel-agp.c | 48 +++++++++++++++++------------------------ drivers/char/agp/nvidia-agp.c | 5 ++--- drivers/char/agp/sis-agp.c | 5 ++--- drivers/char/agp/via-agp.c | 13 ++++++----- 11 files changed, 43 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 923f99df4f1c..b709749c8639 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -239,6 +239,7 @@ long compat_agp_ioctl(struct file *file, unsigned int cmd, unsigned long arg); /* Chipset independent registers (from AGP Spec) */ #define AGP_APBASE 0x10 +#define AGP_APERTURE_BAR 0 #define AGPSTAT 0x4 #define AGPCMD 0x8 diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index 443cd6751ca2..19db03667650 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c @@ -85,8 +85,8 @@ static int ali_configure(void) pci_write_config_dword(agp_bridge->dev, ALI_TLBCTRL, ((temp & 0xffffff00) | 0x00000010)); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); #if 0 if (agp_bridge->type == ALI_M1541) { diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index 779f0ab845a9..5f028cb1d6e8 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -126,7 +126,6 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) unsigned long __iomem *cur_gatt; unsigned long addr; int retval; - u32 temp; int i; value = A_SIZE_LVL2(agp_bridge->current_size); @@ -149,8 +148,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) * used to program the agp master not the cpu */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + addr = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR); agp_bridge->gart_bus_addr = addr; /* Calculate the agp offset */ diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index d79d692d05b8..95326ac610f6 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -269,7 +269,6 @@ static int agp_aperture_valid(u64 aper, u32 size) */ static int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, u16 cap) { - u32 aper_low, aper_hi; u64 aper, nb_aper; int order = 0; u32 nb_order, nb_base; @@ -295,9 +294,7 @@ static int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp, u16 cap) apsize |= 0xf00; order = 7 - hweight16(apsize); - pci_read_config_dword(agp, 0x10, &aper_low); - pci_read_config_dword(agp, 0x14, &aper_hi); - aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32); + aper = pci_bus_address(agp, AGP_APERTURE_BAR); /* * On some sick chips APSIZE is 0. This means it wants 4G diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 03c1dc1ab552..53cb310d433e 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -211,10 +211,10 @@ static int ati_configure(void) else pci_write_config_dword(agp_bridge->dev, ATI_RS300_IG_AGPMODE, 0x20000); - /* address to map too */ + /* address to map to */ /* - pci_read_config_dword(agp_bridge.dev, AGP_APBASE, &temp); - agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge.gart_bus_addr = pci_bus_address(agp_bridge.dev, + AGP_APERTURE_BAR); printk(KERN_INFO PFX "IGP320 gart_bus_addr: %x\n", agp_bridge.gart_bus_addr); */ writel(0x60000, ati_generic_private.registers+ATI_GART_FEATURE_ID); @@ -385,8 +385,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) * This is a bus address even on the alpha, b/c its * used to program the agp master not the cpu */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + addr = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR); agp_bridge->gart_bus_addr = addr; /* Calculate the agp offset */ diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 6974d5032053..533cb6d229b8 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -128,7 +128,6 @@ static void efficeon_cleanup(void) static int efficeon_configure(void) { - u32 temp; u16 temp2; struct aper_size_info_lvl2 *current_size; @@ -141,8 +140,8 @@ static int efficeon_configure(void) current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* agpctrl */ pci_write_config_dword(agp_bridge->dev, INTEL_AGPCTRL, 0x2280); diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index a0df182f6f7d..f39437addb58 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1396,8 +1396,8 @@ int agp3_generic_configure(void) current_size = A_SIZE_16(agp_bridge->current_size); - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* set aperture size */ pci_write_config_word(agp_bridge->dev, agp_bridge->capndx+AGPAPSIZE, current_size->size_value); diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index a426ee1f57a6..a7c276585a9f 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -118,7 +118,6 @@ static void intel_8xx_cleanup(void) static int intel_configure(void) { - u32 temp; u16 temp2; struct aper_size_info_16 *current_size; @@ -128,8 +127,8 @@ static int intel_configure(void) pci_write_config_word(agp_bridge->dev, INTEL_APSIZE, current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* attbase - aperture base */ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr); @@ -148,7 +147,7 @@ static int intel_configure(void) static int intel_815_configure(void) { - u32 temp, addr; + u32 addr; u8 temp2; struct aper_size_info_8 *current_size; @@ -167,8 +166,8 @@ static int intel_815_configure(void) current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); pci_read_config_dword(agp_bridge->dev, INTEL_ATTBASE, &addr); addr &= INTEL_815_ATTBASE_MASK; @@ -208,7 +207,6 @@ static void intel_820_cleanup(void) static int intel_820_configure(void) { - u32 temp; u8 temp2; struct aper_size_info_8 *current_size; @@ -218,8 +216,8 @@ static int intel_820_configure(void) pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* attbase - aperture base */ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr); @@ -239,7 +237,6 @@ static int intel_820_configure(void) static int intel_840_configure(void) { - u32 temp; u16 temp2; struct aper_size_info_8 *current_size; @@ -249,8 +246,8 @@ static int intel_840_configure(void) pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* attbase - aperture base */ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr); @@ -268,7 +265,6 @@ static int intel_840_configure(void) static int intel_845_configure(void) { - u32 temp; u8 temp2; struct aper_size_info_8 *current_size; @@ -282,9 +278,9 @@ static int intel_845_configure(void) agp_bridge->apbase_config); } else { /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - agp_bridge->apbase_config = temp; + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); + agp_bridge->apbase_config = agp_bridge->gart_bus_addr; } /* attbase - aperture base */ @@ -303,7 +299,6 @@ static int intel_845_configure(void) static int intel_850_configure(void) { - u32 temp; u16 temp2; struct aper_size_info_8 *current_size; @@ -313,8 +308,8 @@ static int intel_850_configure(void) pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* attbase - aperture base */ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr); @@ -332,7 +327,6 @@ static int intel_850_configure(void) static int intel_860_configure(void) { - u32 temp; u16 temp2; struct aper_size_info_8 *current_size; @@ -342,8 +336,8 @@ static int intel_860_configure(void) pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* attbase - aperture base */ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr); @@ -361,7 +355,6 @@ static int intel_860_configure(void) static int intel_830mp_configure(void) { - u32 temp; u16 temp2; struct aper_size_info_8 *current_size; @@ -371,8 +364,8 @@ static int intel_830mp_configure(void) pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* attbase - aperture base */ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr); @@ -390,7 +383,6 @@ static int intel_830mp_configure(void) static int intel_7505_configure(void) { - u32 temp; u16 temp2; struct aper_size_info_8 *current_size; @@ -400,8 +392,8 @@ static int intel_7505_configure(void) pci_write_config_byte(agp_bridge->dev, INTEL_APSIZE, current_size->size_value); /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* attbase - aperture base */ pci_write_config_dword(agp_bridge->dev, INTEL_ATTBASE, agp_bridge->gatt_bus_addr); diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index be42a2312dc9..ab65d55272c4 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -115,9 +115,8 @@ static int nvidia_configure(void) pci_write_config_byte(agp_bridge->dev, NVIDIA_0_APSIZE, current_size->size_value); - /* address to map to */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &apbase); - apbase &= PCI_BASE_ADDRESS_MEM_MASK; + /* address to map to */ + apbase = pci_bus_address(agp_bridge->dev, AGP_APERTURE_BAR); agp_bridge->gart_bus_addr = apbase; aplimit = apbase + (current_size->size * 1024 * 1024) - 1; pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APBASE, apbase); diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c index 79c838c434bc..2c74038da459 100644 --- a/drivers/char/agp/sis-agp.c +++ b/drivers/char/agp/sis-agp.c @@ -50,13 +50,12 @@ static void sis_tlbflush(struct agp_memory *mem) static int sis_configure(void) { - u32 temp; struct aper_size_info_8 *current_size; current_size = A_SIZE_8(agp_bridge->current_size); pci_write_config_byte(agp_bridge->dev, SIS_TLBCNTRL, 0x05); - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); pci_write_config_dword(agp_bridge->dev, SIS_ATTBASE, agp_bridge->gatt_bus_addr); pci_write_config_byte(agp_bridge->dev, SIS_APSIZE, diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index 74d3aa3773bf..228f20cddc05 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -43,16 +43,15 @@ static int via_fetch_size(void) static int via_configure(void) { - u32 temp; struct aper_size_info_8 *current_size; current_size = A_SIZE_8(agp_bridge->current_size); /* aperture size */ pci_write_config_byte(agp_bridge->dev, VIA_APSIZE, current_size->size_value); - /* address to map too */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + /* address to map to */ + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* GART control register */ pci_write_config_dword(agp_bridge->dev, VIA_GARTCTRL, 0x0000000f); @@ -132,9 +131,9 @@ static int via_configure_agp3(void) current_size = A_SIZE_16(agp_bridge->current_size); - /* address to map too */ - pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + /* address to map to */ + agp_bridge->gart_bus_addr = pci_bus_address(agp_bridge->dev, + AGP_APERTURE_BAR); /* attbase - aperture GATT base */ pci_write_config_dword(agp_bridge->dev, VIA_AGP3_ATTBASE, -- cgit v1.2.3-70-g09d2 From d68c5a271727e09ce4a26ea8b85cbb852e06650f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 6 Jan 2014 15:21:16 -0700 Subject: agp: Use pci_resource_start() to get CPU physical address for BAR amd_irongate_configure(), ati_configure(), and nvidia_configure() call ioremap() on an address read directly from a BAR. But a BAR contains a bus address, and ioremap() expects a CPU physical address. Use pci_resource_start() to obtain the physical address. Signed-off-by: Bjorn Helgaas Reviewed-by: Daniel Vetter --- drivers/char/agp/amd-k7-agp.c | 8 ++++---- drivers/char/agp/ati-agp.c | 8 ++++---- drivers/char/agp/nvidia-agp.c | 4 +++- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index 5f028cb1d6e8..3661a51e93e2 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -11,7 +11,7 @@ #include #include "agp.h" -#define AMD_MMBASE 0x14 +#define AMD_MMBASE_BAR 1 #define AMD_APSIZE 0xac #define AMD_MODECNTL 0xb0 #define AMD_MODECNTL2 0xb2 @@ -205,6 +205,7 @@ static int amd_irongate_fetch_size(void) static int amd_irongate_configure(void) { struct aper_size_info_lvl2 *current_size; + phys_addr_t reg; u32 temp; u16 enable_reg; @@ -212,9 +213,8 @@ static int amd_irongate_configure(void) if (!amd_irongate_private.registers) { /* Get the memory mapped registers */ - pci_read_config_dword(agp_bridge->dev, AMD_MMBASE, &temp); - temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); - amd_irongate_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096); + reg = pci_resource_start(agp_bridge->dev, AMD_MMBASE_BAR); + amd_irongate_private.registers = (volatile u8 __iomem *) ioremap(reg, 4096); if (!amd_irongate_private.registers) return -ENOMEM; } diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 53cb310d433e..ba83c11186ce 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -12,7 +12,7 @@ #include #include "agp.h" -#define ATI_GART_MMBASE_ADDR 0x14 +#define ATI_GART_MMBASE_BAR 1 #define ATI_RS100_APSIZE 0xac #define ATI_RS100_IG_AGPMODE 0xb0 #define ATI_RS300_APSIZE 0xf8 @@ -196,12 +196,12 @@ static void ati_cleanup(void) static int ati_configure(void) { + phys_addr_t reg; u32 temp; /* Get the memory mapped registers */ - pci_read_config_dword(agp_bridge->dev, ATI_GART_MMBASE_ADDR, &temp); - temp = (temp & 0xfffff000); - ati_generic_private.registers = (volatile u8 __iomem *) ioremap(temp, 4096); + reg = pci_resource_start(agp_bridge->dev, ATI_GART_MMBASE_BAR); + ati_generic_private.registers = (volatile u8 __iomem *) ioremap(reg, 4096); if (!ati_generic_private.registers) return -ENOMEM; diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index ab65d55272c4..a1861b75eb31 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -106,6 +106,7 @@ static int nvidia_configure(void) { int i, rc, num_dirs; u32 apbase, aplimit; + phys_addr_t apbase_phys; struct aper_size_info_8 *current_size; u32 temp; @@ -152,8 +153,9 @@ static int nvidia_configure(void) pci_write_config_dword(agp_bridge->dev, NVIDIA_0_APSIZE, temp | 0x100); /* map aperture */ + apbase_phys = pci_resource_start(agp_bridge->dev, AGP_APERTURE_BAR); nvidia_private.aperture = - (volatile u32 __iomem *) ioremap(apbase, 33 * PAGE_SIZE); + (volatile u32 __iomem *) ioremap(apbase_phys, 33 * PAGE_SIZE); if (!nvidia_private.aperture) return -ENOMEM; -- cgit v1.2.3-70-g09d2 From 21c346075c0694581303ff04ff2be021587e4b40 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 21 Dec 2013 10:52:52 -0700 Subject: drm/i915: Rename gtt_bus_addr to gtt_phys_addr We're dealing with CPU physical addresses here, which may be different from bus addresses, so rename gtt_bus_addr to gtt_phys_addr to avoid confusion. No functional change. Signed-off-by: Bjorn Helgaas Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 3620a1b0a73c..15604b3dda3e 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1260,14 +1260,14 @@ static int ggtt_probe_common(struct drm_device *dev, size_t gtt_size) { struct drm_i915_private *dev_priv = dev->dev_private; - phys_addr_t gtt_bus_addr; + phys_addr_t gtt_phys_addr; int ret; /* For Modern GENs the PTEs and register space are split in the BAR */ - gtt_bus_addr = pci_resource_start(dev->pdev, 0) + + gtt_phys_addr = pci_resource_start(dev->pdev, 0) + (pci_resource_len(dev->pdev, 0) / 2); - dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, gtt_size); + dev_priv->gtt.gsm = ioremap_wc(gtt_phys_addr, gtt_size); if (!dev_priv->gtt.gsm) { DRM_ERROR("Failed to map the gtt page table\n"); return -ENOMEM; -- cgit v1.2.3-70-g09d2 From 5acc4ce44cd0a9cf5dbcfe50085708e9156e0177 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 6 Jan 2014 14:39:40 -0700 Subject: agp/intel: Rename gtt_bus_addr to gtt_phys_addr The only use of gtt_bus_addr is as an argument to ioremap(), so it is a CPU physical address, not a bus address. Rename it to gtt_phys_addr to reflect this. No functional change. Signed-off-by: Bjorn Helgaas Reviewed-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index b8e2014cb9cb..54202ffcf467 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -64,7 +64,7 @@ static struct _intel_private { struct pci_dev *pcidev; /* device one */ struct pci_dev *bridge_dev; u8 __iomem *registers; - phys_addr_t gtt_bus_addr; + phys_addr_t gtt_phys_addr; u32 PGETBL_save; u32 __iomem *gtt; /* I915G */ bool clear_fake_agp; /* on first access via agp, fill with scratch */ @@ -191,7 +191,7 @@ static int i810_setup(void) writel(virt_to_phys(gtt_table) | I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE; + intel_private.gtt_phys_addr = reg_addr + I810_PTE_BASE; if ((readl(intel_private.registers+I810_DRAM_CTL) & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { @@ -636,10 +636,10 @@ static int intel_gtt_init(void) intel_private.gtt = NULL; if (intel_gtt_can_wc()) - intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr, + intel_private.gtt = ioremap_wc(intel_private.gtt_phys_addr, gtt_map_size); if (intel_private.gtt == NULL) - intel_private.gtt = ioremap(intel_private.gtt_bus_addr, + intel_private.gtt = ioremap(intel_private.gtt_phys_addr, gtt_map_size); if (intel_private.gtt == NULL) { intel_private.driver->cleanup(); @@ -796,7 +796,7 @@ static int i830_setup(void) if (!intel_private.registers) return -ENOMEM; - intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE; + intel_private.gtt_phys_addr = reg_addr + I810_PTE_BASE; return 0; } @@ -1123,13 +1123,13 @@ static int i9xx_setup(void) case 3: pci_read_config_dword(intel_private.pcidev, I915_PTEADDR, >t_addr); - intel_private.gtt_bus_addr = gtt_addr; + intel_private.gtt_phys_addr = gtt_addr; break; case 5: - intel_private.gtt_bus_addr = reg_addr + MB(2); + intel_private.gtt_phys_addr = reg_addr + MB(2); break; default: - intel_private.gtt_bus_addr = reg_addr + KB(512); + intel_private.gtt_phys_addr = reg_addr + KB(512); break; } -- cgit v1.2.3-70-g09d2 From 545b0a746b79f54a45cd3b595dce67abbf35233f Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 3 Jan 2014 18:28:06 -0700 Subject: agp/intel: Support 64-bit GMADR Per the Intel 915G/915GV/... Chipset spec (document number 301467-005), GMADR is a standard PCI BAR. The PCI core reads GMADR at enumeration-time. Use pci_bus_address() instead of reading it again in the driver. This works correctly for both 32-bit and 64-bit BARs. The spec above only mentions 32-bit GMADR, but Yinghai's patch (link below) indicates some devices have a 64-bit GMADR. [bhelgaas: reworked starting from http://lkml.kernel.org/r/1385851238-21085-13-git-send-email-yinghai@kernel.org] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas Reviewed-by: Daniel Vetter --- drivers/char/agp/intel-agp.h | 4 ++-- drivers/char/agp/intel-gtt.c | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index 1042c1b90376..0bf5590fd0f9 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -55,7 +55,7 @@ #define INTEL_I860_ERRSTS 0xc8 /* Intel i810 registers */ -#define I810_GMADDR 0x10 +#define I810_GMADR_BAR 0 #define I810_MMADDR 0x14 #define I810_PTE_BASE 0x10000 #define I810_PTE_MAIN_UNCACHED 0x00000000 @@ -113,7 +113,7 @@ #define INTEL_I850_ERRSTS 0xc8 /* intel 915G registers */ -#define I915_GMADDR 0x18 +#define I915_GMADR_BAR 2 #define I915_MMADDR 0x10 #define I915_PTEADDR 0x1C #define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 54202ffcf467..560f66bffebb 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -608,9 +608,8 @@ static bool intel_gtt_can_wc(void) static int intel_gtt_init(void) { - u32 gma_addr; u32 gtt_map_size; - int ret; + int ret, bar; ret = intel_private.driver->setup(); if (ret != 0) @@ -660,14 +659,11 @@ static int intel_gtt_init(void) } if (INTEL_GTT_GEN <= 2) - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, - &gma_addr); + bar = I810_GMADR_BAR; else - pci_read_config_dword(intel_private.pcidev, I915_GMADDR, - &gma_addr); - - intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); + bar = I915_GMADR_BAR; + intel_private.gma_bus_addr = pci_bus_address(intel_private.pcidev, bar); return 0; } -- cgit v1.2.3-70-g09d2 From 5ef6d8f49533bb28a90ae9eec177ffd1ade54267 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 3 Jan 2014 18:28:31 -0700 Subject: agp/intel: Use pci_bus_address() to get MMADR bus address Per the Intel 915G/915GV/... Chipset spec (document number 301467-005), MMADR is a standard PCI BAR. The PCI core reads MMADR at enumeration-time. Use pci_bus_address() instead of reading it again in the driver. This works correctly for both 32-bit and 64-bit BARs. The spec above only mentions 32-bit MMADR, but we should still use the standard interface. Also, stop clearing the low 19 bits of the bus address because it's invalid to use addresses outside the region defined by the BAR. The spec claims MMADR is 512KB; if that's the case, those bits will be zero anyway. Signed-off-by: Bjorn Helgaas Reviewed-by: Daniel Vetter --- drivers/char/agp/intel-agp.h | 4 ++-- drivers/char/agp/intel-gtt.c | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index 0bf5590fd0f9..18bbaafb8509 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -56,7 +56,7 @@ /* Intel i810 registers */ #define I810_GMADR_BAR 0 -#define I810_MMADDR 0x14 +#define I810_MMADR_BAR 1 #define I810_PTE_BASE 0x10000 #define I810_PTE_MAIN_UNCACHED 0x00000000 #define I810_PTE_LOCAL 0x00000002 @@ -114,7 +114,7 @@ /* intel 915G registers */ #define I915_GMADR_BAR 2 -#define I915_MMADDR 0x10 +#define I915_MMADR_BAR 0 #define I915_PTEADDR 0x1C #define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) #define I915_GMCH_GMS_STOLEN_64M (0x7 << 4) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 560f66bffebb..58916f32c0f3 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -181,8 +181,7 @@ static int i810_setup(void) return -ENOMEM; intel_private.i81x_gtt_table = gtt_table; - pci_read_config_dword(intel_private.pcidev, I810_MMADDR, ®_addr); - reg_addr &= 0xfff80000; + reg_addr = pci_bus_address(intel_private.pcidev, I810_MMADR_BAR); intel_private.registers = ioremap(reg_addr, KB(64)); if (!intel_private.registers) @@ -785,8 +784,7 @@ static int i830_setup(void) { u32 reg_addr; - pci_read_config_dword(intel_private.pcidev, I810_MMADDR, ®_addr); - reg_addr &= 0xfff80000; + reg_addr = pci_bus_address(intel_private.pcidev, I810_MMADR_BAR); intel_private.registers = ioremap(reg_addr, KB(64)); if (!intel_private.registers) @@ -1107,9 +1105,7 @@ static int i9xx_setup(void) u32 reg_addr, gtt_addr; int size = KB(512); - pci_read_config_dword(intel_private.pcidev, I915_MMADDR, ®_addr); - - reg_addr &= 0xfff80000; + reg_addr = pci_bus_address(intel_private.pcidev, I915_MMADR_BAR); intel_private.registers = ioremap(reg_addr, size); if (!intel_private.registers) -- cgit v1.2.3-70-g09d2 From b5e350f919acb8ef6961bc1b62e395f53cea123a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 3 Jan 2014 18:29:00 -0700 Subject: agp/intel: Use pci_bus_address() to get GTTADR bus address Per the Intel 915G/915GV/... Chipset spec (document number 301467-005), GTTADR is a standard PCI BAR. The PCI core reads GTTADR at enumeration-time. Use pci_bus_address() instead of reading it again in the driver. This works correctly for both 32-bit and 64-bit BARs. The spec above only mentions 32-bit GTTADR, but we should still use the standard interface. Signed-off-by: Bjorn Helgaas Reviewed-by: Daniel Vetter --- drivers/char/agp/intel-agp.h | 2 +- drivers/char/agp/intel-gtt.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index 18bbaafb8509..fda073dcd967 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -115,7 +115,7 @@ /* intel 915G registers */ #define I915_GMADR_BAR 2 #define I915_MMADR_BAR 0 -#define I915_PTEADDR 0x1C +#define I915_PTE_BAR 3 #define I915_GMCH_GMS_STOLEN_48M (0x6 << 4) #define I915_GMCH_GMS_STOLEN_64M (0x7 << 4) #define G33_GMCH_GMS_STOLEN_128M (0x8 << 4) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 58916f32c0f3..dd8b66a617dc 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1102,7 +1102,7 @@ static void i965_write_entry(dma_addr_t addr, static int i9xx_setup(void) { - u32 reg_addr, gtt_addr; + u32 reg_addr; int size = KB(512); reg_addr = pci_bus_address(intel_private.pcidev, I915_MMADR_BAR); @@ -1113,9 +1113,8 @@ static int i9xx_setup(void) switch (INTEL_GTT_GEN) { case 3: - pci_read_config_dword(intel_private.pcidev, - I915_PTEADDR, >t_addr); - intel_private.gtt_phys_addr = gtt_addr; + intel_private.gtt_phys_addr = + pci_bus_address(intel_private.pcidev, I915_PTE_BAR); break; case 5: intel_private.gtt_phys_addr = reg_addr + MB(2); -- cgit v1.2.3-70-g09d2 From d3572532993c7e8635ad8e5b50f8f613bf855ee2 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 6 Jan 2014 14:43:13 -0700 Subject: agp/intel: Use CPU physical address, not bus address, for ioremap() In i810_setup(), i830_setup(), and i9xx_setup(), we use the result of pci_bus_address() as an argument to ioremap() and to compute gtt_phys_addr. These should use pci_resource_start() instead because we want the CPU physical address, not the bus address. If there were an AGP device behind a host bridge that translated addresses, e.g., a PNP0A08 device with _TRA != 0, this would fix a bug. I'm not aware of any of those, but they are possible. Signed-off-by: Bjorn Helgaas Reviewed-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index dd8b66a617dc..ad5da1ffcbe9 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -172,7 +172,7 @@ static void i8xx_destroy_pages(struct page *page) #define I810_GTT_ORDER 4 static int i810_setup(void) { - u32 reg_addr; + phys_addr_t reg_addr; char *gtt_table; /* i81x does not preallocate the gtt. It's always 64kb in size. */ @@ -181,7 +181,7 @@ static int i810_setup(void) return -ENOMEM; intel_private.i81x_gtt_table = gtt_table; - reg_addr = pci_bus_address(intel_private.pcidev, I810_MMADR_BAR); + reg_addr = pci_resource_start(intel_private.pcidev, I810_MMADR_BAR); intel_private.registers = ioremap(reg_addr, KB(64)); if (!intel_private.registers) @@ -782,9 +782,9 @@ EXPORT_SYMBOL(intel_enable_gtt); static int i830_setup(void) { - u32 reg_addr; + phys_addr_t reg_addr; - reg_addr = pci_bus_address(intel_private.pcidev, I810_MMADR_BAR); + reg_addr = pci_resource_start(intel_private.pcidev, I810_MMADR_BAR); intel_private.registers = ioremap(reg_addr, KB(64)); if (!intel_private.registers) @@ -1102,10 +1102,10 @@ static void i965_write_entry(dma_addr_t addr, static int i9xx_setup(void) { - u32 reg_addr; + phys_addr_t reg_addr; int size = KB(512); - reg_addr = pci_bus_address(intel_private.pcidev, I915_MMADR_BAR); + reg_addr = pci_resource_start(intel_private.pcidev, I915_MMADR_BAR); intel_private.registers = ioremap(reg_addr, size); if (!intel_private.registers) @@ -1114,7 +1114,7 @@ static int i9xx_setup(void) switch (INTEL_GTT_GEN) { case 3: intel_private.gtt_phys_addr = - pci_bus_address(intel_private.pcidev, I915_PTE_BAR); + pci_resource_start(intel_private.pcidev, I915_PTE_BAR); break; case 5: intel_private.gtt_phys_addr = reg_addr + MB(2); -- cgit v1.2.3-70-g09d2 From 167b1f049008b367a9003a6a8df090af4282a6b0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 6 Jan 2014 16:15:31 -0700 Subject: agp/ati: Use PCI_COMMAND instead of hard-coded 4 We're accessing the PCI_COMMAND register here, so use the appropriate #define. The bit we're writing (1 << 14) isn't defined by the PCI or PCIe spec, so we don't have a name for it. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/char/agp/ati-agp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index ba83c11186ce..18a7a6baa304 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -221,8 +221,8 @@ static int ati_configure(void) readl(ati_generic_private.registers+ATI_GART_FEATURE_ID); /* PCI Posting.*/ /* SIGNALED_SYSTEM_ERROR @ NB_STATUS */ - pci_read_config_dword(agp_bridge->dev, 4, &temp); - pci_write_config_dword(agp_bridge->dev, 4, temp | (1<<14)); + pci_read_config_dword(agp_bridge->dev, PCI_COMMAND, &temp); + pci_write_config_dword(agp_bridge->dev, PCI_COMMAND, temp | (1<<14)); /* Write out the address of the gatt table */ writel(agp_bridge->gatt_bus_addr, ati_generic_private.registers+ATI_GART_BASE); -- cgit v1.2.3-70-g09d2 From 36e097a8a29761e0e2c951c0390f1057df248a87 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 18 Dec 2013 16:31:39 -0700 Subject: PCI: Split out bridge window override of minimum allocation address pci_bus_alloc_resource() avoids allocating space below the "min" supplied by the caller (usually PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM). This is to protect badly documented motherboard resources. But if we're allocating space inside an already-configured PCI-PCI bridge window, we ignore "min". See 688d191821de ("pci: make bus resource start address override minimum IO address"). This patch moves the check to make it more visible and simplify future patches. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index fc1b74013743..6f2f47a7b6c6 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -147,11 +147,18 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, !(res->flags & IORESOURCE_PREFETCH)) continue; + /* + * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to + * protect badly documented motherboard resources, but if + * this is an already-configured bridge window, its start + * overrides "min". + */ + if (r->start) + min = r->start; + /* Ok, try it out.. */ - ret = allocate_resource(r, res, size, - r->start ? : min, - max, align, - alignf, alignf_data); + ret = allocate_resource(r, res, size, min, max, + align, alignf, alignf_data); if (ret == 0) break; } -- cgit v1.2.3-70-g09d2 From f75b99d5a77d63f20e07bd276d5a427808ac8ef6 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 20 Dec 2013 09:57:37 -0700 Subject: PCI: Enforce bus address limits in resource allocation When allocating space for 32-bit BARs, we previously limited RESOURCE addresses so they would fit in 32 bits. However, the BUS address need not be the same as the resource address, and it's the bus address that must fit in the 32-bit BAR. This patch adds: - pci_clip_resource_to_region(), which clips a resource so it contains only the range that maps to the specified bus address region, e.g., to clip a resource to 32-bit bus addresses, and - pci_bus_alloc_from_region(), which allocates space for a resource from the specified bus address region, and changes pci_bus_alloc_resource() to allocate space for 64-bit BARs from the entire bus address region, and space for 32-bit BARs from only the bus address region below 4GB. If we had this window: pci_root HWP0002:0a: host bridge window [mem 0xf0180000000-0xf01fedfffff] (bus address [0x80000000-0xfedfffff]) we previously could not put a 32-bit BAR there, because the CPU addresses don't fit in 32 bits. This patch fixes this, so we can use this space for 32-bit BARs. It's also possible (though unlikely) to have resources with 32-bit CPU addresses but bus addresses above 4GB. In this case the previous code would allocate space that a 32-bit BAR could not map. Remove PCIBIOS_MAX_MEM_32, which is no longer used. [bhelgaas: reworked starting from http://lkml.kernel.org/r/1386658484-15774-3-git-send-email-yinghai@kernel.org] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/include/asm/pci.h | 1 - drivers/pci/bus.c | 111 +++++++++++++++++++++++++++++++++------------ include/linux/pci.h | 4 -- 3 files changed, 83 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 947b5c417e83..122c299e90c8 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -125,7 +125,6 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, /* generic pci stuff */ #include -#define PCIBIOS_MAX_MEM_32 0xffffffff #ifdef CONFIG_NUMA /* Returns the node based on pci bus */ diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 6f2f47a7b6c6..c30baae929f4 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -98,41 +98,52 @@ void pci_bus_remove_resources(struct pci_bus *bus) } } -/** - * pci_bus_alloc_resource - allocate a resource from a parent bus - * @bus: PCI bus - * @res: resource to allocate - * @size: size of resource to allocate - * @align: alignment of resource to allocate - * @min: minimum /proc/iomem address to allocate - * @type_mask: IORESOURCE_* type flags - * @alignf: resource alignment function - * @alignf_data: data argument for resource alignment function - * - * Given the PCI bus a device resides on, the size, minimum address, - * alignment and type, try to find an acceptable resource allocation - * for a specific device resource. +static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL}; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT +static struct pci_bus_region pci_64_bit = {0, + (dma_addr_t) 0xffffffffffffffffULL}; +#endif + +/* + * @res contains CPU addresses. Clip it so the corresponding bus addresses + * on @bus are entirely within @region. This is used to control the bus + * addresses of resources we allocate, e.g., we may need a resource that + * can be mapped by a 32-bit BAR. */ -int -pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, +static void pci_clip_resource_to_region(struct pci_bus *bus, + struct resource *res, + struct pci_bus_region *region) +{ + struct pci_bus_region r; + + pcibios_resource_to_bus(bus, &r, res); + if (r.start < region->start) + r.start = region->start; + if (r.end > region->end) + r.end = region->end; + + if (r.end < r.start) + res->end = res->start - 1; + else + pcibios_bus_to_resource(bus, res, &r); +} + +static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, resource_size_t min, unsigned int type_mask, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, resource_size_t), - void *alignf_data) + void *alignf_data, + struct pci_bus_region *region) { - int i, ret = -ENOMEM; - struct resource *r; - resource_size_t max = -1; + int i, ret; + struct resource *r, avail; + resource_size_t max; type_mask |= IORESOURCE_IO | IORESOURCE_MEM; - /* don't allocate too high if the pref mem doesn't support 64bit*/ - if (!(res->flags & IORESOURCE_MEM_64)) - max = PCIBIOS_MAX_MEM_32; - pci_bus_for_each_resource(bus, r, i) { if (!r) continue; @@ -147,22 +158,66 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, !(res->flags & IORESOURCE_PREFETCH)) continue; + avail = *r; + pci_clip_resource_to_region(bus, &avail, region); + if (!resource_size(&avail)) + continue; + /* * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to * protect badly documented motherboard resources, but if * this is an already-configured bridge window, its start * overrides "min". */ - if (r->start) - min = r->start; + if (avail.start) + min = avail.start; + + max = avail.end; /* Ok, try it out.. */ ret = allocate_resource(r, res, size, min, max, align, alignf, alignf_data); if (ret == 0) - break; + return 0; } - return ret; + return -ENOMEM; +} + +/** + * pci_bus_alloc_resource - allocate a resource from a parent bus + * @bus: PCI bus + * @res: resource to allocate + * @size: size of resource to allocate + * @align: alignment of resource to allocate + * @min: minimum /proc/iomem address to allocate + * @type_mask: IORESOURCE_* type flags + * @alignf: resource alignment function + * @alignf_data: data argument for resource alignment function + * + * Given the PCI bus a device resides on, the size, minimum address, + * alignment and type, try to find an acceptable resource allocation + * for a specific device resource. + */ +int +pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, + resource_size_t size, resource_size_t align, + resource_size_t min, unsigned int type_mask, + resource_size_t (*alignf)(void *, + const struct resource *, + resource_size_t, + resource_size_t), + void *alignf_data) +{ +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + if (res->flags & IORESOURCE_MEM_64) + return pci_bus_alloc_from_region(bus, res, size, align, min, + type_mask, alignf, alignf_data, + &pci_64_bit); +#endif + + return pci_bus_alloc_from_region(bus, res, size, align, min, + type_mask, alignf, alignf_data, + &pci_32_bit); } void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } diff --git a/include/linux/pci.h b/include/linux/pci.h index 966b286b5d53..095eb44fcbb6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1493,10 +1493,6 @@ static inline struct pci_dev *pci_dev_get(struct pci_dev *dev) #include -#ifndef PCIBIOS_MAX_MEM_32 -#define PCIBIOS_MAX_MEM_32 (-1) -#endif - /* these helpers provide future and backwards compatibility * for accessing popular PCI BAR info */ #define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start) -- cgit v1.2.3-70-g09d2 From d56dbf5bab8ce44c5407bb099f71987f58d18bb4 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 20 Dec 2013 10:55:44 -0700 Subject: PCI: Allocate 64-bit BARs above 4G when possible Try to allocate space for 64-bit BARs above 4G first, to preserve the space below 4G for 32-bit BARs. If there's no space above 4G available, fall back to allocating anywhere. [bhelgaas: reworked starting from http://lkml.kernel.org/r/1387485843-17403-2-git-send-email-yinghai@kernel.org] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index c30baae929f4..86fb8ec5e448 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -102,6 +102,8 @@ static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL}; #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT static struct pci_bus_region pci_64_bit = {0, (dma_addr_t) 0xffffffffffffffffULL}; +static struct pci_bus_region pci_high = {(dma_addr_t) 0x100000000ULL, + (dma_addr_t) 0xffffffffffffffffULL}; #endif /* @@ -198,8 +200,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, * alignment and type, try to find an acceptable resource allocation * for a specific device resource. */ -int -pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, +int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, resource_size_t min, unsigned int type_mask, resource_size_t (*alignf)(void *, @@ -209,10 +210,19 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, void *alignf_data) { #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (res->flags & IORESOURCE_MEM_64) + int rc; + + if (res->flags & IORESOURCE_MEM_64) { + rc = pci_bus_alloc_from_region(bus, res, size, align, min, + type_mask, alignf, alignf_data, + &pci_high); + if (rc == 0) + return 0; + return pci_bus_alloc_from_region(bus, res, size, align, min, type_mask, alignf, alignf_data, &pci_64_bit); + } #endif return pci_bus_alloc_from_region(bus, res, size, align, min, -- cgit v1.2.3-70-g09d2 From aa9319773619c9d0caf2c4abc3ac12dc879dc8ed Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 9 Jan 2014 08:36:08 -0700 Subject: PCI: Never treat a VF as a multifunction device Per the SR-IOV spec rev 1.1: 3.4.1.9 Header Type (Offset 0Eh) "... For VFs, this register must be RO Zero." Unfortunately some devices get this wrong, ex. Emulex OneConnect 10Gb NIC. When they do it makes us handle ACS testing and therefore IOMMU groups as if they were actual multifunction devices and require ACS capabilities to make sure there's no peer-to-peer between functions. VFs are never traditional multifunction devices, so simply clear this bit before we get any further into setup. Link: https://bugzilla.kernel.org/show_bug.cgi?id=68431 Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 68311ec849ee..9dce7c5e2a77 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -84,6 +84,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) virtfn->dev.parent = dev->dev.parent; virtfn->physfn = pci_dev_get(dev); virtfn->is_virtfn = 1; + virtfn->multifunction = 0; for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { res = dev->resource + PCI_IOV_RESOURCES + i; -- cgit v1.2.3-70-g09d2 From b340cacc1b47d8fc15051aaf9b2d44678e245ba9 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 14:00:47 -0700 Subject: PCI: Removed unused parts of Page Request Interface support My philosophy is unused code is dead code. And dead code is subject to bit rot and is a likely source of bugs. Use it or lose it. This reverts parts of c320b976d783 ("PCI: Add implementation for PRI capability"), removing these interfaces: pci_pri_enabled() pci_pri_stopped() pci_pri_status() [bhelgaas: split to separate patch] Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas CC: Joerg Roedel --- drivers/pci/ats.c | 82 ------------------------------------------------- include/linux/pci-ats.h | 17 ---------- 2 files changed, 99 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index e52d7ffa38b9..a8099d4d0c9d 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -234,27 +234,6 @@ void pci_disable_pri(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_pri); -/** - * pci_pri_enabled - Checks if PRI capability is enabled - * @pdev: PCI device structure - * - * Returns true if PRI is enabled on the device, false otherwise - */ -bool pci_pri_enabled(struct pci_dev *pdev) -{ - u16 control; - int pos; - - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); - if (!pos) - return false; - - pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); - - return (control & PCI_PRI_CTRL_ENABLE) ? true : false; -} -EXPORT_SYMBOL_GPL(pci_pri_enabled); - /** * pci_reset_pri - Resets device's PRI state * @pdev: PCI device structure @@ -282,67 +261,6 @@ int pci_reset_pri(struct pci_dev *pdev) return 0; } EXPORT_SYMBOL_GPL(pci_reset_pri); - -/** - * pci_pri_stopped - Checks whether the PRI capability is stopped - * @pdev: PCI device structure - * - * Returns true if the PRI capability on the device is disabled and the - * device has no outstanding PRI requests, false otherwise. The device - * indicates this via the STOPPED bit in the status register of the - * capability. - * The device internal state can be cleared by resetting the PRI state - * with pci_reset_pri(). This can force the capability into the STOPPED - * state. - */ -bool pci_pri_stopped(struct pci_dev *pdev) -{ - u16 control, status; - int pos; - - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); - if (!pos) - return true; - - pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); - pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status); - - if (control & PCI_PRI_CTRL_ENABLE) - return false; - - return (status & PCI_PRI_STATUS_STOPPED) ? true : false; -} -EXPORT_SYMBOL_GPL(pci_pri_stopped); - -/** - * pci_pri_status - Request PRI status of a device - * @pdev: PCI device structure - * - * Returns negative value on failure, status on success. The status can - * be checked against status-bits. Supported bits are currently: - * PCI_PRI_STATUS_RF: Response failure - * PCI_PRI_STATUS_UPRGI: Unexpected Page Request Group Index - * PCI_PRI_STATUS_STOPPED: PRI has stopped - */ -int pci_pri_status(struct pci_dev *pdev) -{ - u16 status, control; - int pos; - - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); - if (!pos) - return -EINVAL; - - pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); - pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status); - - /* Stopped bit is undefined when enable == 1, so clear it */ - if (control & PCI_PRI_CTRL_ENABLE) - status &= ~PCI_PRI_STATUS_STOPPED; - - return status; -} -EXPORT_SYMBOL_GPL(pci_pri_status); #endif /* CONFIG_PCI_PRI */ #ifdef CONFIG_PCI_PASID diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index 68bcefd7fca0..72031785fe1d 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h @@ -56,10 +56,7 @@ static inline int pci_ats_enabled(struct pci_dev *dev) int pci_enable_pri(struct pci_dev *pdev, u32 reqs); void pci_disable_pri(struct pci_dev *pdev); -bool pci_pri_enabled(struct pci_dev *pdev); int pci_reset_pri(struct pci_dev *pdev); -bool pci_pri_stopped(struct pci_dev *pdev); -int pci_pri_status(struct pci_dev *pdev); #else /* CONFIG_PCI_PRI */ @@ -72,25 +69,11 @@ static inline void pci_disable_pri(struct pci_dev *pdev) { } -static inline bool pci_pri_enabled(struct pci_dev *pdev) -{ - return false; -} - static inline int pci_reset_pri(struct pci_dev *pdev) { return -ENODEV; } -static inline bool pci_pri_stopped(struct pci_dev *pdev) -{ - return true; -} - -static inline int pci_pri_status(struct pci_dev *pdev) -{ - return -ENODEV; -} #endif /* CONFIG_PCI_PRI */ #ifdef CONFIG_PCI_PASID -- cgit v1.2.3-70-g09d2 From 3ea8197e1371abd68aef289cad97feb764281642 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 14:00:57 -0700 Subject: PCI: Remove unused Latency Tolerance Reporting support My philosophy is unused code is dead code. And dead code is subject to bit rot and is a likely source of bugs. Use it or lose it. This reverts 51c2e0a7e5bc ("PCI: add latency tolerance reporting enable/disable support"), removing these interfaces: pci_enable_ltr() pci_disable_ltr() pci_set_ltr() [bhelgaas: split to separate patch, also remove prototypes from pci.h] Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas CC: Jesse Barnes --- drivers/pci/pci.c | 123 ---------------------------------------------------- include/linux/pci.h | 4 -- 2 files changed, 127 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 33120d156668..020f672deabb 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2223,129 +2223,6 @@ void pci_disable_obff(struct pci_dev *dev) } EXPORT_SYMBOL(pci_disable_obff); -/** - * pci_ltr_supported - check whether a device supports LTR - * @dev: PCI device - * - * RETURNS: - * True if @dev supports latency tolerance reporting, false otherwise. - */ -static bool pci_ltr_supported(struct pci_dev *dev) -{ - u32 cap; - - pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap); - - return cap & PCI_EXP_DEVCAP2_LTR; -} - -/** - * pci_enable_ltr - enable latency tolerance reporting - * @dev: PCI device - * - * Enable LTR on @dev if possible, which means enabling it first on - * upstream ports. - * - * RETURNS: - * Zero on success, errno on failure. - */ -int pci_enable_ltr(struct pci_dev *dev) -{ - int ret; - - /* Only primary function can enable/disable LTR */ - if (PCI_FUNC(dev->devfn) != 0) - return -EINVAL; - - if (!pci_ltr_supported(dev)) - return -ENOTSUPP; - - /* Enable upstream ports first */ - if (dev->bus->self) { - ret = pci_enable_ltr(dev->bus->self); - if (ret) - return ret; - } - - return pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_LTR_EN); -} -EXPORT_SYMBOL(pci_enable_ltr); - -/** - * pci_disable_ltr - disable latency tolerance reporting - * @dev: PCI device - */ -void pci_disable_ltr(struct pci_dev *dev) -{ - /* Only primary function can enable/disable LTR */ - if (PCI_FUNC(dev->devfn) != 0) - return; - - if (!pci_ltr_supported(dev)) - return; - - pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_LTR_EN); -} -EXPORT_SYMBOL(pci_disable_ltr); - -static int __pci_ltr_scale(int *val) -{ - int scale = 0; - - while (*val > 1023) { - *val = (*val + 31) / 32; - scale++; - } - return scale; -} - -/** - * pci_set_ltr - set LTR latency values - * @dev: PCI device - * @snoop_lat_ns: snoop latency in nanoseconds - * @nosnoop_lat_ns: nosnoop latency in nanoseconds - * - * Figure out the scale and set the LTR values accordingly. - */ -int pci_set_ltr(struct pci_dev *dev, int snoop_lat_ns, int nosnoop_lat_ns) -{ - int pos, ret, snoop_scale, nosnoop_scale; - u16 val; - - if (!pci_ltr_supported(dev)) - return -ENOTSUPP; - - snoop_scale = __pci_ltr_scale(&snoop_lat_ns); - nosnoop_scale = __pci_ltr_scale(&nosnoop_lat_ns); - - if (snoop_lat_ns > PCI_LTR_VALUE_MASK || - nosnoop_lat_ns > PCI_LTR_VALUE_MASK) - return -EINVAL; - - if ((snoop_scale > (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT)) || - (nosnoop_scale > (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT))) - return -EINVAL; - - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); - if (!pos) - return -ENOTSUPP; - - val = (snoop_scale << PCI_LTR_SCALE_SHIFT) | snoop_lat_ns; - ret = pci_write_config_word(dev, pos + PCI_LTR_MAX_SNOOP_LAT, val); - if (ret != 4) - return -EIO; - - val = (nosnoop_scale << PCI_LTR_SCALE_SHIFT) | nosnoop_lat_ns; - ret = pci_write_config_word(dev, pos + PCI_LTR_MAX_NOSNOOP_LAT, val); - if (ret != 4) - return -EIO; - - return 0; -} -EXPORT_SYMBOL(pci_set_ltr); - static int pci_acs_enable; /** diff --git a/include/linux/pci.h b/include/linux/pci.h index 1084a15175e0..ee26454746ba 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1009,10 +1009,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); -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); - /* For use by arch with custom probe code */ void set_pcie_port_type(struct pci_dev *pdev); void set_pcie_hotplug_bridge(struct pci_dev *pdev); -- cgit v1.2.3-70-g09d2 From ecc8635608342a05ded722f0e30e6ccd682f84a6 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 14:01:05 -0700 Subject: PCI: Remove unused Optimized Buffer Flush/Fill support My philosophy is unused code is dead code. And dead code is subject to bit rot and is a likely source of bugs. Use it or lose it. This reverts 48a92a8179b3 ("PCI: add OBFF enable/disable support"), removing these interfaces: pci_enable_obff() pci_disable_obff() [bhelgaas: split to separate patch, also remove prototypes from pci.h] Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas CC: Jesse Barnes --- drivers/pci/pci.c | 73 ----------------------------------------------------- include/linux/pci.h | 16 ------------ 2 files changed, 89 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 020f672deabb..84a24d16778f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2150,79 +2150,6 @@ void pci_disable_ido(struct pci_dev *dev, unsigned long type) } EXPORT_SYMBOL(pci_disable_ido); -/** - * pci_enable_obff - enable optimized buffer flush/fill - * @dev: PCI device - * @type: type of signaling to use - * - * Try to enable @type OBFF signaling on @dev. It will try using WAKE# - * signaling if possible, falling back to message signaling only if - * WAKE# isn't supported. @type should indicate whether the PCIe link - * be brought out of L0s or L1 to send the message. It should be either - * %PCI_EXP_OBFF_SIGNAL_ALWAYS or %PCI_OBFF_SIGNAL_L0. - * - * If your device can benefit from receiving all messages, even at the - * power cost of bringing the link back up from a low power state, use - * %PCI_EXP_OBFF_SIGNAL_ALWAYS. Otherwise, use %PCI_OBFF_SIGNAL_L0 (the - * preferred type). - * - * RETURNS: - * Zero on success, appropriate error number on failure. - */ -int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) -{ - u32 cap; - u16 ctrl; - int ret; - - pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap); - if (!(cap & PCI_EXP_DEVCAP2_OBFF_MASK)) - return -ENOTSUPP; /* no OBFF support at all */ - - /* Make sure the topology supports OBFF as well */ - if (dev->bus->self) { - ret = pci_enable_obff(dev->bus->self, type); - if (ret) - return ret; - } - - pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &ctrl); - if (cap & PCI_EXP_DEVCAP2_OBFF_WAKE) - ctrl |= PCI_EXP_DEVCTL2_OBFF_WAKE_EN; - else { - switch (type) { - case PCI_EXP_OBFF_SIGNAL_L0: - if (!(ctrl & PCI_EXP_DEVCTL2_OBFF_WAKE_EN)) - ctrl |= PCI_EXP_DEVCTL2_OBFF_MSGA_EN; - break; - case PCI_EXP_OBFF_SIGNAL_ALWAYS: - ctrl &= ~PCI_EXP_DEVCTL2_OBFF_WAKE_EN; - ctrl |= PCI_EXP_DEVCTL2_OBFF_MSGB_EN; - break; - default: - WARN(1, "bad OBFF signal type\n"); - return -ENOTSUPP; - } - } - pcie_capability_write_word(dev, PCI_EXP_DEVCTL2, ctrl); - - return 0; -} -EXPORT_SYMBOL(pci_enable_obff); - -/** - * pci_disable_obff - disable optimized buffer flush/fill - * @dev: PCI device - * - * Disable OBFF on @dev. - */ -void pci_disable_obff(struct pci_dev *dev) -{ - pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_OBFF_WAKE_EN); -} -EXPORT_SYMBOL(pci_disable_obff); - static int pci_acs_enable; /** diff --git a/include/linux/pci.h b/include/linux/pci.h index ee26454746ba..b8cbe52c7a48 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1002,13 +1002,6 @@ static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, void pci_enable_ido(struct pci_dev *dev, unsigned long type); void pci_disable_ido(struct pci_dev *dev, unsigned long type); -enum pci_obff_signal_type { - PCI_EXP_OBFF_SIGNAL_L0 = 0, - PCI_EXP_OBFF_SIGNAL_ALWAYS = 1, -}; -int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); -void pci_disable_obff(struct pci_dev *dev); - /* For use by arch with custom probe code */ void set_pcie_port_type(struct pci_dev *pdev); void set_pcie_hotplug_bridge(struct pci_dev *pdev); @@ -1427,15 +1420,6 @@ static inline void pci_disable_ido(struct pci_dev *dev, unsigned long type) { } -static inline int pci_enable_obff(struct pci_dev *dev, unsigned long type) -{ - return 0; -} - -static inline void pci_disable_obff(struct pci_dev *dev) -{ -} - static inline int pci_request_regions(struct pci_dev *dev, const char *res_name) { return -EIO; -- cgit v1.2.3-70-g09d2 From 7c2dd2d7cfabdb37875c5e481a0c3cc5962bb0b6 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 14:01:11 -0700 Subject: PCI: Remove unused ID-Based Ordering support My philosophy is unused code is dead code. And dead code is subject to bit rot and is a likely source of bugs. Use it or lose it. This reverts b48d4425b602 ("PCI: add ID-based ordering enable/disable support"), removing these interfaces: pci_enable_ido() pci_disable_ido() [bhelgaas: split to separate patch, also remove prototypes from pci.h] Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas CC: Jesse Barnes --- drivers/pci/pci.c | 40 ---------------------------------------- include/linux/pci.h | 13 ------------- 2 files changed, 53 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 84a24d16778f..6db2f1a441e9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2110,46 +2110,6 @@ void pci_configure_ari(struct pci_dev *dev) } } -/** - * pci_enable_ido - enable ID-based Ordering on a device - * @dev: the PCI device - * @type: which types of IDO to enable - * - * Enable ID-based ordering on @dev. @type can contain the bits - * %PCI_EXP_IDO_REQUEST and/or %PCI_EXP_IDO_COMPLETION to indicate - * which types of transactions are allowed to be re-ordered. - */ -void pci_enable_ido(struct pci_dev *dev, unsigned long type) -{ - u16 ctrl = 0; - - if (type & PCI_EXP_IDO_REQUEST) - ctrl |= PCI_EXP_DEVCTL2_IDO_REQ_EN; - if (type & PCI_EXP_IDO_COMPLETION) - ctrl |= PCI_EXP_DEVCTL2_IDO_CMP_EN; - if (ctrl) - pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, ctrl); -} -EXPORT_SYMBOL(pci_enable_ido); - -/** - * pci_disable_ido - disable ID-based ordering on a device - * @dev: the PCI device - * @type: which types of IDO to disable - */ -void pci_disable_ido(struct pci_dev *dev, unsigned long type) -{ - u16 ctrl = 0; - - if (type & PCI_EXP_IDO_REQUEST) - ctrl |= PCI_EXP_DEVCTL2_IDO_REQ_EN; - if (type & PCI_EXP_IDO_COMPLETION) - ctrl |= PCI_EXP_DEVCTL2_IDO_CMP_EN; - if (ctrl) - pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, ctrl); -} -EXPORT_SYMBOL(pci_disable_ido); - static int pci_acs_enable; /** diff --git a/include/linux/pci.h b/include/linux/pci.h index b8cbe52c7a48..f2284f1332db 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -997,11 +997,6 @@ static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, return __pci_enable_wake(dev, state, false, enable); } -#define PCI_EXP_IDO_REQUEST (1<<0) -#define PCI_EXP_IDO_COMPLETION (1<<1) -void pci_enable_ido(struct pci_dev *dev, unsigned long type); -void pci_disable_ido(struct pci_dev *dev, unsigned long type); - /* For use by arch with custom probe code */ void set_pcie_port_type(struct pci_dev *pdev); void set_pcie_hotplug_bridge(struct pci_dev *pdev); @@ -1412,14 +1407,6 @@ static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, return 0; } -static inline void pci_enable_ido(struct pci_dev *dev, unsigned long type) -{ -} - -static inline void pci_disable_ido(struct pci_dev *dev, unsigned long type) -{ -} - static inline int pci_request_regions(struct pci_dev *dev, const char *res_name) { return -EIO; -- cgit v1.2.3-70-g09d2 From 3984ca1c6e493d06fa7e8dc96ce2b23b86175ec4 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 15:10:13 -0700 Subject: PCI: Remove unused pci_vpd_truncate() My philosophy is unused code is dead code. And dead code is subject to bit rot and is a likely source of bugs. Use it or lose it. This reverts db5679437a2b ("PCI: add interface to set visible size of VPD"), removing this interface: pci_vpd_truncate() [bhelgaas: split to separate patch, also remove prototype from pci.h] Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 24 ------------------------ include/linux/pci.h | 1 - 2 files changed, 25 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 0857ca981fae..7f8b78c08879 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -380,30 +380,6 @@ int pci_vpd_pci22_init(struct pci_dev *dev) return 0; } -/** - * pci_vpd_truncate - Set available Vital Product Data size - * @dev: pci device struct - * @size: available memory in bytes - * - * Adjust size of available VPD area. - */ -int pci_vpd_truncate(struct pci_dev *dev, size_t size) -{ - if (!dev->vpd) - return -EINVAL; - - /* limited by the access method */ - if (size > dev->vpd->len) - return -EINVAL; - - dev->vpd->len = size; - if (dev->vpd->attr) - dev->vpd->attr->size = size; - - return 0; -} -EXPORT_SYMBOL(pci_vpd_truncate); - /** * pci_cfg_access_lock - Lock PCI config reads/writes * @dev: pci device struct diff --git a/include/linux/pci.h b/include/linux/pci.h index f2284f1332db..53cdc6dc411b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1009,7 +1009,6 @@ unsigned int pci_rescan_bus(struct pci_bus *bus); /* Vital product data routines */ ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); -int pci_vpd_truncate(struct pci_dev *dev, size_t size); /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx); -- cgit v1.2.3-70-g09d2 From 8f92fb06ffb090f2b22717481352f1e58511c834 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 15:23:16 -0700 Subject: PCI: Remove unused pcie_aspm_enabled() My philosophy is unused code is dead code. And dead code is subject to bit rot and is a likely source of bugs. Use it or lose it. This reverts part of 3e1b16002af2 ("ACPI/PCI: PCIe ASPM _OSC support capabilities called when root bridge added"), removing this interface: pcie_aspm_enabled() [bhelgaas: split to separate patch] Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas CC: Andrew Patterson --- drivers/pci/pcie/aspm.c | 12 ------------ include/linux/pci.h | 2 -- 2 files changed, 14 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index f1272dc54de1..e1e7026b838d 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -984,18 +984,6 @@ void pcie_no_aspm(void) } } -/** - * pcie_aspm_enabled - is PCIe ASPM enabled? - * - * Returns true if ASPM has not been disabled by the command-line option - * pcie_aspm=off. - **/ -int pcie_aspm_enabled(void) -{ - return !aspm_disabled; -} -EXPORT_SYMBOL(pcie_aspm_enabled); - bool pcie_aspm_support_enabled(void) { return aspm_support_enabled; diff --git a/include/linux/pci.h b/include/linux/pci.h index 53cdc6dc411b..ac4703f719f7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1200,10 +1200,8 @@ extern bool pcie_ports_auto; #endif #ifndef CONFIG_PCIEASPM -static inline int pcie_aspm_enabled(void) { return 0; } static inline bool pcie_aspm_support_enabled(void) { return false; } #else -int pcie_aspm_enabled(void); bool pcie_aspm_support_enabled(void); #endif -- cgit v1.2.3-70-g09d2 From 4ab44676064baeb6c7d807c0f627e07d29ce48e0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 15:41:35 -0700 Subject: PCI: Remove unused pci_renumber_slot() My philosophy is unused code is dead code. And dead code is subject to bit rot and is a likely source of bugs. Use it or lose it. This reverts part of f46753c5e354 ("PCI: introduce pci_slot") and d25b7c8d6ba2 ("PCI: rename pci_update_slot_number to pci_renumber_slot"), removing this interface: pci_renumber_slot() [bhelgaas: split to separate patch, add historical link from Alex] Link: http://lkml.kernel.org/r/20081009043140.8678.44164.stgit@bob.kio Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas Acked-by: Alex Chiang --- drivers/pci/slot.c | 26 -------------------------- include/linux/pci.h | 1 - 2 files changed, 27 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 448ca562d1f8..7dd62fa9d0bd 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -319,32 +319,6 @@ err: } EXPORT_SYMBOL_GPL(pci_create_slot); -/** - * pci_renumber_slot - update %struct pci_slot -> number - * @slot: &struct pci_slot to update - * @slot_nr: new number for slot - * - * The primary purpose of this interface is to allow callers who earlier - * created a placeholder slot in pci_create_slot() by passing a -1 as - * slot_nr, to update their %struct pci_slot with the correct @slot_nr. - */ -void pci_renumber_slot(struct pci_slot *slot, int slot_nr) -{ - struct pci_slot *tmp; - - down_write(&pci_bus_sem); - - list_for_each_entry(tmp, &slot->bus->slots, list) { - WARN_ON(tmp->number == slot_nr); - goto out; - } - - slot->number = slot_nr; -out: - up_write(&pci_bus_sem); -} -EXPORT_SYMBOL_GPL(pci_renumber_slot); - /** * pci_destroy_slot - decrement refcount for physical PCI slot * @slot: struct pci_slot to decrement diff --git a/include/linux/pci.h b/include/linux/pci.h index ac4703f719f7..3a3e513d8a50 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -763,7 +763,6 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, const char *name, struct hotplug_slot *hotplug); void pci_destroy_slot(struct pci_slot *slot); -void pci_renumber_slot(struct pci_slot *slot, int slot_nr); int pci_scan_slot(struct pci_bus *bus, int devfn); struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn); void pci_device_add(struct pci_dev *dev, struct pci_bus *bus); -- cgit v1.2.3-70-g09d2 From e2760c54a4f5131867bf1b35d59169267d35d3d0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 15:46:34 -0700 Subject: PCI: Remove unused alloc_pci_dev() My philosophy is unused code is dead code. And dead code is subject to bit rot and is a likely source of bugs. Use it or lose it. This removes this unused and deprecated interface: alloc_pci_dev() [bhelgaas: split to separate patch] Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 6 ------ include/linux/pci.h | 1 - 2 files changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 38e403dddf6e..a4f53b677185 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1242,12 +1242,6 @@ struct pci_dev *pci_alloc_dev(struct pci_bus *bus) } EXPORT_SYMBOL(pci_alloc_dev); -struct pci_dev *alloc_pci_dev(void) -{ - return pci_alloc_dev(NULL); -} -EXPORT_SYMBOL(alloc_pci_dev); - bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, int crs_timeout) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 3a3e513d8a50..bd31f51d2c8f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -375,7 +375,6 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev) } struct pci_dev *pci_alloc_dev(struct pci_bus *bus); -struct pci_dev * __deprecated alloc_pci_dev(void); #define to_pci_dev(n) container_of(n, struct pci_dev, dev) #define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) -- cgit v1.2.3-70-g09d2 From 0b950f0f3c67e42f18c655a3ab3e36ea192635bb Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Jan 2014 17:14:48 -0700 Subject: PCI: Make local functions static Using 'make namespacecheck' identify code which should be declared static. Checked for users in other driver/archs as well. Compile tested only. This stops exporting the following interfaces to modules: pci_target_state() pci_load_saved_state() [bhelgaas: retained pci_find_next_ext_capability() and pci_cfg_space_size()] Signed-off-by: Stephen Hemminger Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 1 - drivers/pci/hotplug/pciehp_core.c | 2 +- drivers/pci/pci.c | 54 +++++++++---------- drivers/pci/pci.h | 2 - drivers/pci/probe.c | 111 +++++++++++++++++++------------------- include/linux/pci.h | 5 -- 6 files changed, 84 insertions(+), 91 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 21e865ded1dc..8de88b052715 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -43,7 +43,6 @@ extern bool pciehp_poll_mode; extern int pciehp_poll_time; extern bool pciehp_debug; -extern bool pciehp_force; #define dbg(format, arg...) \ do { \ diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index bbd48bbe4e9b..67cbd3e90ffd 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -41,7 +41,7 @@ bool pciehp_debug; bool pciehp_poll_mode; int pciehp_poll_time; -bool pciehp_force; +static bool pciehp_force; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman , Dely Sy " diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6db2f1a441e9..9ea31080870f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -656,6 +656,28 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) return error; } +/** + * pci_wakeup - Wake up a PCI device + * @pci_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. + */ +static void pci_wakeup_bus(struct pci_bus *bus) +{ + if (bus) + pci_walk_bus(bus, pci_wakeup, NULL); +} + /** * __pci_start_power_transition - Start power transition of a PCI device * @dev: PCI device to handle. @@ -835,8 +857,8 @@ EXPORT_SYMBOL(pci_choose_state); #define PCI_EXP_SAVE_REGS 7 -static struct pci_cap_saved_state *pci_find_saved_cap( - struct pci_dev *pci_dev, char cap) +static struct pci_cap_saved_state *pci_find_saved_cap(struct pci_dev *pci_dev, + char cap) { struct pci_cap_saved_state *tmp; @@ -1071,7 +1093,8 @@ EXPORT_SYMBOL_GPL(pci_store_saved_state); * @dev: PCI device that we're dealing with * @state: Saved state returned from pci_store_saved_state() */ -int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state) +static int pci_load_saved_state(struct pci_dev *dev, + struct pci_saved_state *state) { struct pci_cap_saved_data *cap; @@ -1099,7 +1122,6 @@ int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state) dev->state_saved = true; return 0; } -EXPORT_SYMBOL_GPL(pci_load_saved_state); /** * pci_load_and_free_saved_state - Reload the save state pointed to by state, @@ -1531,27 +1553,6 @@ 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 - * @pci_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# @@ -1765,7 +1766,7 @@ int pci_wake_from_d3(struct pci_dev *dev, bool enable) * If the platform can't manage @dev, return the deepest state from which it * can generate wake events, based on any available PME info. */ -pci_power_t pci_target_state(struct pci_dev *dev) +static pci_power_t pci_target_state(struct pci_dev *dev) { pci_power_t target_state = PCI_D3hot; @@ -4206,7 +4207,6 @@ EXPORT_SYMBOL(pci_restore_state); EXPORT_SYMBOL(pci_pme_capable); EXPORT_SYMBOL(pci_pme_active); EXPORT_SYMBOL(pci_wake_from_d3); -EXPORT_SYMBOL(pci_target_state); EXPORT_SYMBOL(pci_prepare_to_sleep); EXPORT_SYMBOL(pci_back_from_sleep); EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9c91ecc1301b..4df38df224f4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -6,7 +6,6 @@ #define PCI_CFG_SPACE_SIZE 256 #define PCI_CFG_SPACE_EXP_SIZE 4096 -extern const unsigned char pcix_bus_speed[]; extern const unsigned char pcie_link_speed[]; /* Functions internal to the PCI core code */ @@ -68,7 +67,6 @@ void pci_power_up(struct pci_dev *dev); void pci_disable_enabled_device(struct pci_dev *dev); int pci_finish_runtime_suspend(struct pci_dev *dev); int __pci_pme_wakeup(struct pci_dev *dev, void *ign); -void pci_wakeup_bus(struct pci_bus *bus); void pci_config_pm_runtime_get(struct pci_dev *dev); void pci_config_pm_runtime_put(struct pci_dev *dev); void pci_pm_init(struct pci_dev *dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index a4f53b677185..7fef23ba6bc6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -16,7 +16,7 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 -struct resource busn_resource = { +static struct resource busn_resource = { .name = "PCI busn", .start = 0, .end = 255, @@ -518,7 +518,7 @@ static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b) return bridge; } -const unsigned char pcix_bus_speed[] = { +static const unsigned char pcix_bus_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCI_SPEED_66MHz_PCIX, /* 1 */ PCI_SPEED_100MHz_PCIX, /* 2 */ @@ -999,6 +999,60 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev) pdev->is_hotplug_bridge = 1; } + +/** + * pci_cfg_space_size - get the configuration space size of the PCI device. + * @dev: PCI device + * + * Regular PCI devices have 256 bytes, but PCI-X 2 and PCI Express devices + * have 4096 bytes. Even if the device is capable, that doesn't mean we can + * access it. Maybe we don't have a way to generate extended config space + * accesses, or the device is behind a reverse Express bridge. So we try + * reading the dword at 0x100 which must either be 0 or a valid extended + * capability header. + */ +static int pci_cfg_space_size_ext(struct pci_dev *dev) +{ + u32 status; + int pos = PCI_CFG_SPACE_SIZE; + + if (pci_read_config_dword(dev, pos, &status) != PCIBIOS_SUCCESSFUL) + goto fail; + if (status == 0xffffffff) + goto fail; + + return PCI_CFG_SPACE_EXP_SIZE; + + fail: + return PCI_CFG_SPACE_SIZE; +} + +int pci_cfg_space_size(struct pci_dev *dev) +{ + int pos; + u32 status; + u16 class; + + class = dev->class >> 8; + if (class == PCI_CLASS_BRIDGE_HOST) + return pci_cfg_space_size_ext(dev); + + if (!pci_is_pcie(dev)) { + pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); + if (!pos) + goto fail; + + pci_read_config_dword(dev, pos + PCI_X_STATUS, &status); + if (!(status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ))) + goto fail; + } + + return pci_cfg_space_size_ext(dev); + + fail: + return PCI_CFG_SPACE_SIZE; +} + #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED) /** @@ -1173,59 +1227,6 @@ static void pci_release_dev(struct device *dev) kfree(pci_dev); } -/** - * pci_cfg_space_size - get the configuration space size of the PCI device. - * @dev: PCI device - * - * Regular PCI devices have 256 bytes, but PCI-X 2 and PCI Express devices - * have 4096 bytes. Even if the device is capable, that doesn't mean we can - * access it. Maybe we don't have a way to generate extended config space - * accesses, or the device is behind a reverse Express bridge. So we try - * reading the dword at 0x100 which must either be 0 or a valid extended - * capability header. - */ -int pci_cfg_space_size_ext(struct pci_dev *dev) -{ - u32 status; - int pos = PCI_CFG_SPACE_SIZE; - - if (pci_read_config_dword(dev, pos, &status) != PCIBIOS_SUCCESSFUL) - goto fail; - if (status == 0xffffffff) - goto fail; - - return PCI_CFG_SPACE_EXP_SIZE; - - fail: - return PCI_CFG_SPACE_SIZE; -} - -int pci_cfg_space_size(struct pci_dev *dev) -{ - int pos; - u32 status; - u16 class; - - class = dev->class >> 8; - if (class == PCI_CLASS_BRIDGE_HOST) - return pci_cfg_space_size_ext(dev); - - if (!pci_is_pcie(dev)) { - pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); - if (!pos) - goto fail; - - pci_read_config_dword(dev, pos + PCI_X_STATUS, &status); - if (!(status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ))) - goto fail; - } - - return pci_cfg_space_size_ext(dev); - - fail: - return PCI_CFG_SPACE_SIZE; -} - struct pci_dev *pci_alloc_dev(struct pci_bus *bus) { struct pci_dev *dev; diff --git a/include/linux/pci.h b/include/linux/pci.h index bd31f51d2c8f..fd440ccd431b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -384,8 +384,6 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } -extern struct resource busn_resource; - struct pci_host_bridge_window { struct list_head list; struct resource *res; /* host bridge aperture (CPU address) */ @@ -971,7 +969,6 @@ void __iomem __must_check *pci_platform_rom(struct pci_dev *pdev, size_t *size); int pci_save_state(struct pci_dev *dev); void pci_restore_state(struct pci_dev *dev); struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev); -int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state); int pci_load_and_free_saved_state(struct pci_dev *dev, struct pci_saved_state **state); int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state); @@ -982,7 +979,6 @@ void pci_pme_active(struct pci_dev *dev, bool enable); int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool runtime, bool enable); int pci_wake_from_d3(struct pci_dev *dev, bool enable); -pci_power_t pci_target_state(struct pci_dev *dev); int pci_prepare_to_sleep(struct pci_dev *dev); int pci_back_from_sleep(struct pci_dev *dev); bool pci_dev_run_wake(struct pci_dev *dev); @@ -1095,7 +1091,6 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata); -int pci_cfg_space_size_ext(struct pci_dev *dev); int pci_cfg_space_size(struct pci_dev *dev); unsigned char pci_bus_max_busnr(struct pci_bus *bus); void pci_setup_bridge(struct pci_bus *bus); -- cgit v1.2.3-70-g09d2 From 339c0fc58618b717565f109d68da50c5bc1a0510 Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Thu, 5 Dec 2013 08:08:24 -0700 Subject: PCI/AER: Support ACPI HEST AER error sources for PCI domains other than 0 In the discussion for this set of patches [link below], Bjorn Helgaas pointed out that the ACPI HEST AER error sources do not have the PCIe segment number associated with the bus. I worked with the ACPI spec and got this change to definition of the "Bus" field into the recently released ACPI Spec 5.0a section 18.3.2.3-5: Identifies the PCI Bus and Segment of the device. The Bus is encoded in bits 0-7. For systems that expose multiple PCI segment groups, the segment number is encoded in bits 8-23 and bits 24-31 must be zero. For systems that do not expose multiple PCI segment groups, bits 8-31 must be zero. If the GLOBAL flag is specified, this field is ignored. This patch makes use of the new definition in the only place in the kernel that uses the acpi_hest_aer_common's bus field. This depends on 36f3615152c1 ("ACPICA: Add helper macros to extract bus/segment numbers from HEST table.") Link: http://lkml.kernel.org/r/1370542251-27387-1-git-send-email-betty.dall@hp.com Signed-off-by: Betty Dall Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_acpi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 4d6991794fa2..01906576ab91 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -23,10 +23,10 @@ static inline int hest_match_pci(struct acpi_hest_aer_common *p, struct pci_dev *pci) { - return (0 == pci_domain_nr(pci->bus) && - p->bus == pci->bus->number && - p->device == PCI_SLOT(pci->devfn) && - p->function == PCI_FUNC(pci->devfn)); + return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) && + ACPI_HEST_BUS(p->bus) == pci->bus->number && + p->device == PCI_SLOT(pci->devfn) && + p->function == PCI_FUNC(pci->devfn); } static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, -- cgit v1.2.3-70-g09d2 From 4c85980429f2769bd11c91c7cd90d297aef31fbd Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 13 Jan 2014 17:01:11 -0700 Subject: PCI: Reorder so actual code comes before stubs Consistently use the: #ifdef CONFIG_PCI_FOO int pci_foo(...); #else static inline int pci_foo(...) { return -1; } #endif pattern, instead of sometimes using "#ifndef CONFIG_PCI_FOO". No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-label.c | 57 ++++++++++++++++++++++--------------------------- include/linux/pci.h | 46 +++++++++++++++++++-------------------- 2 files changed, 48 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index d51f45aa669e..6f5d343d251c 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -34,21 +34,7 @@ #define DEVICE_LABEL_DSM 0x07 -#ifndef CONFIG_DMI - -static inline int -pci_create_smbiosname_file(struct pci_dev *pdev) -{ - return -1; -} - -static inline void -pci_remove_smbiosname_file(struct pci_dev *pdev) -{ -} - -#else - +#ifdef CONFIG_DMI enum smbios_attr_enum { SMBIOS_ATTR_NONE = 0, SMBIOS_ATTR_LABEL_SHOW, @@ -156,31 +142,20 @@ pci_remove_smbiosname_file(struct pci_dev *pdev) { sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); } - -#endif - -#ifndef CONFIG_ACPI - -static inline int -pci_create_acpi_index_label_files(struct pci_dev *pdev) -{ - return -1; -} - +#else static inline int -pci_remove_acpi_index_label_files(struct pci_dev *pdev) +pci_create_smbiosname_file(struct pci_dev *pdev) { return -1; } -static inline bool -device_has_dsm(struct device *dev) +static inline void +pci_remove_smbiosname_file(struct pci_dev *pdev) { - return false; } +#endif -#else - +#ifdef CONFIG_ACPI static const char device_label_dsm_uuid[] = { 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D @@ -364,6 +339,24 @@ pci_remove_acpi_index_label_files(struct pci_dev *pdev) sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group); return 0; } +#else +static inline int +pci_create_acpi_index_label_files(struct pci_dev *pdev) +{ + return -1; +} + +static inline int +pci_remove_acpi_index_label_files(struct pci_dev *pdev) +{ + return -1; +} + +static inline bool +device_has_dsm(struct device *dev) +{ + return false; +} #endif void pci_create_firmware_label_files(struct pci_dev *pdev) diff --git a/include/linux/pci.h b/include/linux/pci.h index eba7764b1586..39a3609c8cda 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1149,7 +1149,22 @@ struct msix_entry { }; -#ifndef CONFIG_PCI_MSI +#ifdef CONFIG_PCI_MSI +int pci_msi_vec_count(struct pci_dev *dev); +int pci_enable_msi_block(struct pci_dev *dev, int nvec); +void pci_msi_shutdown(struct pci_dev *dev); +void pci_disable_msi(struct pci_dev *dev); +int pci_msix_vec_count(struct pci_dev *dev); +int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec); +void pci_msix_shutdown(struct pci_dev *dev); +void pci_disable_msix(struct pci_dev *dev); +void msi_remove_pci_irq_vectors(struct pci_dev *dev); +void pci_restore_msi_state(struct pci_dev *dev); +int pci_msi_enabled(void); +int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec); +int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, + int minvec, int maxvec); +#else static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; @@ -1200,21 +1215,6 @@ static inline int pci_enable_msix_range(struct pci_dev *dev, { return -ENOSYS; } -#else -int pci_msi_vec_count(struct pci_dev *dev); -int pci_enable_msi_block(struct pci_dev *dev, int nvec); -void pci_msi_shutdown(struct pci_dev *dev); -void pci_disable_msi(struct pci_dev *dev); -int pci_msix_vec_count(struct pci_dev *dev); -int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec); -void pci_msix_shutdown(struct pci_dev *dev); -void pci_disable_msix(struct pci_dev *dev); -void msi_remove_pci_irq_vectors(struct pci_dev *dev); -void pci_restore_msi_state(struct pci_dev *dev); -int pci_msi_enabled(void); -int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec); -int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, - int minvec, int maxvec); #endif #ifdef CONFIG_PCIEPORTBUS @@ -1225,10 +1225,10 @@ extern bool pcie_ports_auto; #define pcie_ports_auto false #endif -#ifndef CONFIG_PCIEASPM -static inline bool pcie_aspm_support_enabled(void) { return false; } -#else +#ifdef CONFIG_PCIEASPM bool pcie_aspm_support_enabled(void); +#else +static inline bool pcie_aspm_support_enabled(void) { return false; } #endif #ifdef CONFIG_PCIEAER @@ -1239,15 +1239,15 @@ static inline void pci_no_aer(void) { } static inline bool pci_aer_available(void) { return false; } #endif -#ifndef CONFIG_PCIE_ECRC +#ifdef CONFIG_PCIE_ECRC +void pcie_set_ecrc_checking(struct pci_dev *dev); +void pcie_ecrc_get_policy(char *str); +#else static inline void pcie_set_ecrc_checking(struct pci_dev *dev) { return; } static inline void pcie_ecrc_get_policy(char *str) {}; -#else -void pcie_set_ecrc_checking(struct pci_dev *dev); -void pcie_ecrc_get_policy(char *str); #endif #define pci_enable_msi(pdev) pci_enable_msi_block(pdev, 1) -- cgit v1.2.3-70-g09d2 From 9d16947b75831acd317ab9a53e0e94d160731d33 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jan 2014 15:22:18 +0100 Subject: PCI: Add global pci_lock_rescan_remove() There are multiple PCI device addition and removal code paths that may be run concurrently with the generic PCI bus rescan and device removal that can be triggered via sysfs. If that happens, it may lead to multiple different, potentially dangerous race conditions. The most straightforward way to address those problems is to run the code in question under the same lock that is used by the generic rescan/remove code in pci-sysfs.c. To prepare for those changes, move the definition of the global PCI remove/rescan lock to probe.c and provide global wrappers, pci_lock_rescan_remove() and pci_unlock_rescan_remove(), allowing drivers to manipulate that lock. Also provide pci_stop_and_remove_bus_device_locked() for the callers of pci_stop_and_remove_bus_device() who only need to hold the rescan/remove lock around it. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 19 +++++++------------ drivers/pci/probe.c | 18 ++++++++++++++++++ drivers/pci/remove.c | 8 ++++++++ include/linux/pci.h | 3 +++ 4 files changed, 36 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c91e6c18debc..276ef9c18802 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -297,7 +297,6 @@ msi_bus_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(msi_bus); -static DEFINE_MUTEX(pci_remove_rescan_mutex); static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, size_t count) { @@ -308,10 +307,10 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, return -EINVAL; if (val) { - mutex_lock(&pci_remove_rescan_mutex); + pci_lock_rescan_remove(); while ((b = pci_find_next_bus(b)) != NULL) pci_rescan_bus(b); - mutex_unlock(&pci_remove_rescan_mutex); + pci_unlock_rescan_remove(); } return count; } @@ -342,9 +341,9 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr, return -EINVAL; if (val) { - mutex_lock(&pci_remove_rescan_mutex); + pci_lock_rescan_remove(); pci_rescan_bus(pdev->bus); - mutex_unlock(&pci_remove_rescan_mutex); + pci_unlock_rescan_remove(); } return count; } @@ -354,11 +353,7 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan, static void remove_callback(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - - mutex_lock(&pci_remove_rescan_mutex); - pci_stop_and_remove_bus_device(pdev); - mutex_unlock(&pci_remove_rescan_mutex); + pci_stop_and_remove_bus_device_locked(to_pci_dev(dev)); } static ssize_t @@ -395,12 +390,12 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, return -EINVAL; if (val) { - mutex_lock(&pci_remove_rescan_mutex); + pci_lock_rescan_remove(); if (!pci_is_root_bus(bus) && list_empty(&bus->devices)) pci_rescan_bus_bridge_resize(bus->self); else pci_rescan_bus(bus); - mutex_unlock(&pci_remove_rescan_mutex); + pci_unlock_rescan_remove(); } return count; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index c86c8638d3c4..04796c056d12 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2024,6 +2024,24 @@ EXPORT_SYMBOL(pci_scan_slot); EXPORT_SYMBOL(pci_scan_bridge); EXPORT_SYMBOL_GPL(pci_scan_child_bus); +/* + * pci_rescan_bus(), pci_rescan_bus_bridge_resize() and PCI device removal + * routines should always be executed under this mutex. + */ +static DEFINE_MUTEX(pci_rescan_remove_lock); + +void pci_lock_rescan_remove(void) +{ + mutex_lock(&pci_rescan_remove_lock); +} +EXPORT_SYMBOL_GPL(pci_lock_rescan_remove); + +void pci_unlock_rescan_remove(void) +{ + mutex_unlock(&pci_rescan_remove_lock); +} +EXPORT_SYMBOL_GPL(pci_unlock_rescan_remove); + static int __init pci_sort_bf_cmp(const struct device *d_a, const struct device *d_b) { const struct pci_dev *a = to_pci_dev(d_a); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index f452148e6d55..10fa13f9e309 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -95,6 +95,14 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev) } EXPORT_SYMBOL(pci_stop_and_remove_bus_device); +void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev) +{ + pci_lock_rescan_remove(); + pci_stop_and_remove_bus_device(dev); + pci_unlock_rescan_remove(); +} +EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked); + void pci_stop_root_bus(struct pci_bus *bus) { struct pci_dev *child, *tmp; diff --git a/include/linux/pci.h b/include/linux/pci.h index 9e3ec8b951b7..2087e6b35545 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -775,6 +775,7 @@ struct pci_dev *pci_dev_get(struct pci_dev *dev); void pci_dev_put(struct pci_dev *dev); void pci_remove_bus(struct pci_bus *b); void pci_stop_and_remove_bus_device(struct pci_dev *dev); +void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev); void pci_stop_root_bus(struct pci_bus *bus); void pci_remove_root_bus(struct pci_bus *bus); void pci_setup_cardbus(struct pci_bus *bus); @@ -1011,6 +1012,8 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev); int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); unsigned int pci_rescan_bus(struct pci_bus *bus); +void pci_lock_rescan_remove(void); +void pci_unlock_rescan_remove(void); /* Vital product data routines */ ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); -- cgit v1.2.3-70-g09d2 From 7a3bb55ebd1cb04f08dd68cf60af24b434f8cc2a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jan 2014 15:23:14 +0100 Subject: ACPI / PCI: Use global PCI rescan-remove locking in PCI root hotplug Multiple race conditions are possible between the addition and removal of PCI devices during ACPI PCI host bridge hotplug and the generic PCI bus rescan and device removal that can be triggered via sysfs. To avoid those race conditions make the ACPI PCI host bridge addition and removal code use global PCI rescan-remove locking. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 0703bff5e60e..07ee02aa3c51 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -596,7 +596,9 @@ static int acpi_pci_root_add(struct acpi_device *device, pci_assign_unassigned_root_bus_resources(root->bus); } + pci_lock_rescan_remove(); pci_bus_add_devices(root->bus); + pci_unlock_rescan_remove(); return 1; end: @@ -608,6 +610,8 @@ static void acpi_pci_root_remove(struct acpi_device *device) { struct acpi_pci_root *root = acpi_driver_data(device); + pci_lock_rescan_remove(); + pci_stop_root_bus(root->bus); device_set_run_wake(root->bus->bridge, false); @@ -615,6 +619,8 @@ static void acpi_pci_root_remove(struct acpi_device *device) pci_remove_root_bus(root->bus); + pci_unlock_rescan_remove(); + kfree(root); } -- cgit v1.2.3-70-g09d2 From 9217a984671e8a7e38f1822eba754898066e2ae1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jan 2014 15:24:41 +0100 Subject: ACPI / hotplug / PCI: Use global PCI rescan-remove locking Multiple race conditions are possible between the ACPI-based PCI hotplug (ACPIPHP) and the generic PCI bus rescan and device removal that can be triggered via sysfs. To avoid those race conditions make the ACPIPHP code use global PCI rescan-remove locking. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpiphp.h | 5 ++++- drivers/pci/hotplug/acpiphp_core.c | 2 +- drivers/pci/hotplug/acpiphp_glue.c | 43 +++++++++++++++++++++++++++++++++----- 3 files changed, 43 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 1592dbe4f904..b6162be4df40 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -77,6 +77,8 @@ struct acpiphp_bridge { /* PCI-to-PCI bridge device */ struct pci_dev *pci_dev; + + bool is_going_away; }; @@ -150,6 +152,7 @@ struct acpiphp_attention_info /* slot flags */ #define SLOT_ENABLED (0x00000001) +#define SLOT_IS_GOING_AWAY (0x00000002) /* function flags */ @@ -169,7 +172,7 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot); typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); int acpiphp_enable_slot(struct acpiphp_slot *slot); -int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); +int acpiphp_disable_slot(struct acpiphp_slot *slot); u8 acpiphp_get_power_status(struct acpiphp_slot *slot); u8 acpiphp_get_attention_status(struct acpiphp_slot *slot); u8 acpiphp_get_latch_status(struct acpiphp_slot *slot); diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index dca66bc44578..728c31f4c2c5 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -156,7 +156,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot)); /* disable the specified slot */ - return acpiphp_disable_and_eject_slot(slot->acpi_slot); + return acpiphp_disable_slot(slot->acpi_slot); } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 1cf605f67673..641ba6761bd7 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -430,6 +430,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) pr_err("failed to remove notify handler\n"); } } + slot->flags |= SLOT_IS_GOING_AWAY; if (slot->slot) acpiphp_unregister_hotplug_slot(slot); } @@ -437,6 +438,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) mutex_lock(&bridge_mutex); list_del(&bridge->list); mutex_unlock(&bridge_mutex); + + bridge->is_going_away = true; } /** @@ -736,6 +739,10 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) { struct acpiphp_slot *slot; + /* Bail out if the bridge is going away. */ + if (bridge->is_going_away) + return; + list_for_each_entry(slot, &bridge->slots, node) { struct pci_bus *bus = slot->bus; struct pci_dev *dev, *tmp; @@ -805,6 +812,8 @@ void acpiphp_check_host_bridge(acpi_handle handle) } } +static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); + static void hotplug_event(acpi_handle handle, u32 type, void *data) { struct acpiphp_context *context = data; @@ -834,6 +843,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) } else { struct acpiphp_slot *slot = func->slot; + if (slot->flags & SLOT_IS_GOING_AWAY) + break; + mutex_lock(&slot->crit_sect); enable_slot(slot); mutex_unlock(&slot->crit_sect); @@ -849,6 +861,9 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) struct acpiphp_slot *slot = func->slot; int ret; + if (slot->flags & SLOT_IS_GOING_AWAY) + break; + /* * Check if anything has changed in the slot and rescan * from the parent if that's the case. @@ -878,9 +893,11 @@ static void hotplug_event_work(void *data, u32 type) acpi_handle handle = context->handle; acpi_scan_lock_acquire(); + pci_lock_rescan_remove(); hotplug_event(handle, type, context); + pci_unlock_rescan_remove(); acpi_scan_lock_release(); acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL); put_bridge(context->func.parent); @@ -1048,12 +1065,19 @@ void acpiphp_remove_slots(struct pci_bus *bus) */ int acpiphp_enable_slot(struct acpiphp_slot *slot) { + pci_lock_rescan_remove(); + + if (slot->flags & SLOT_IS_GOING_AWAY) + return -ENODEV; + mutex_lock(&slot->crit_sect); /* configure all functions */ if (!(slot->flags & SLOT_ENABLED)) enable_slot(slot); mutex_unlock(&slot->crit_sect); + + pci_unlock_rescan_remove(); return 0; } @@ -1061,10 +1085,12 @@ int acpiphp_enable_slot(struct acpiphp_slot *slot) * acpiphp_disable_and_eject_slot - power off and eject slot * @slot: ACPI PHP slot */ -int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) +static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) { struct acpiphp_func *func; - int retval = 0; + + if (slot->flags & SLOT_IS_GOING_AWAY) + return -ENODEV; mutex_lock(&slot->crit_sect); @@ -1082,9 +1108,18 @@ int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) } mutex_unlock(&slot->crit_sect); - return retval; + return 0; } +int acpiphp_disable_slot(struct acpiphp_slot *slot) +{ + int ret; + + pci_lock_rescan_remove(); + ret = acpiphp_disable_and_eject_slot(slot); + pci_unlock_rescan_remove(); + return ret; +} /* * slot enabled: 1 @@ -1095,7 +1130,6 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot) return (slot->flags & SLOT_ENABLED); } - /* * latch open: 1 * latch closed: 0 @@ -1105,7 +1139,6 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI); } - /* * adapter presence : 1 * absence : 0 -- cgit v1.2.3-70-g09d2 From 5ef68e8867ca9d979644d05c6045b2c79d8989de Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jan 2014 15:25:34 +0100 Subject: pcmcia: Use global PCI rescan-remove locking Multiple race conditions are possible between the cardbus PCI device addition and removal and the generic PCI bus rescan and device removal that can be triggered via sysfs. To avoid those race conditions make the cardbus code use global PCI rescan-remove locking. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pcmcia/cardbus.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index b2a98cdbd0d2..8bde61952d20 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -70,6 +70,8 @@ int __ref cb_alloc(struct pcmcia_socket *s) struct pci_dev *dev; unsigned int max, pass; + pci_lock_rescan_remove(); + s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0)); pci_fixup_cardbus(bus); @@ -93,6 +95,7 @@ int __ref cb_alloc(struct pcmcia_socket *s) pci_bus_add_devices(bus); + pci_unlock_rescan_remove(); return 0; } @@ -115,6 +118,10 @@ void cb_free(struct pcmcia_socket *s) if (!bus) return; + pci_lock_rescan_remove(); + list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) pci_stop_and_remove_bus_device(dev); + + pci_unlock_rescan_remove(); } -- cgit v1.2.3-70-g09d2 From c4ec84c7db0e4b01ed40cc2388f16ae5c6513cc0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 14 Jan 2014 12:03:14 -0700 Subject: PCI: hotplug: Use global PCI rescan-remove locking Multiple race conditions are possible between PCI hotplug and the generic PCI bus rescan and device removal that can be triggered via sysfs. To avoid those race conditions make PCI hotplug use global PCI rescan-remove locking. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpci_hotplug_pci.c | 14 ++++++++++++-- drivers/pci/hotplug/cpqphp_pci.c | 8 +++++++- drivers/pci/hotplug/ibmphp_core.c | 13 +++++++++++-- drivers/pci/hotplug/pciehp_pci.c | 17 +++++++++++++---- drivers/pci/hotplug/rpadlpar_core.c | 19 ++++++++++++++----- drivers/pci/hotplug/rpaphp_core.c | 4 ++++ drivers/pci/hotplug/s390_pci_hpc.c | 4 +++- drivers/pci/hotplug/sgi_hotplug.c | 5 +++++ drivers/pci/hotplug/shpchp_pci.c | 18 ++++++++++++++---- 9 files changed, 83 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index d3add9819f63..8c1464851768 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -254,9 +254,12 @@ int __ref cpci_configure_slot(struct slot *slot) { struct pci_dev *dev; struct pci_bus *parent; + int ret = 0; dbg("%s - enter", __func__); + pci_lock_rescan_remove(); + if (slot->dev == NULL) { dbg("pci_dev null, finding %02x:%02x:%x", slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); @@ -277,7 +280,8 @@ int __ref cpci_configure_slot(struct slot *slot) slot->dev = pci_get_slot(slot->bus, slot->devfn); if (slot->dev == NULL) { err("Could not find PCI device for slot %02x", slot->number); - return -ENODEV; + ret = -ENODEV; + goto out; } } parent = slot->dev->bus; @@ -294,8 +298,10 @@ int __ref cpci_configure_slot(struct slot *slot) pci_bus_add_devices(parent); + out: + pci_unlock_rescan_remove(); dbg("%s - exit", __func__); - return 0; + return ret; } int cpci_unconfigure_slot(struct slot* slot) @@ -308,6 +314,8 @@ int cpci_unconfigure_slot(struct slot* slot) return -ENODEV; } + pci_lock_rescan_remove(); + list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) { if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn)) continue; @@ -318,6 +326,8 @@ int cpci_unconfigure_slot(struct slot* slot) pci_dev_put(slot->dev); slot->dev = NULL; + pci_unlock_rescan_remove(); + dbg("%s - exit", __func__); return 0; } diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index 6e4a12c91adb..a3e3c2002b58 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -86,6 +86,8 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) struct pci_bus *child; int num; + pci_lock_rescan_remove(); + if (func->pci_dev == NULL) func->pci_dev = pci_get_bus_and_slot(func->bus,PCI_DEVFN(func->device, func->function)); @@ -100,7 +102,7 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) func->pci_dev = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, func->function)); if (func->pci_dev == NULL) { dbg("ERROR: pci_dev still null\n"); - return 0; + goto out; } } @@ -113,6 +115,8 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) pci_dev_put(func->pci_dev); + out: + pci_unlock_rescan_remove(); return 0; } @@ -123,6 +127,7 @@ int cpqhp_unconfigure_device(struct pci_func* func) dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function); + pci_lock_rescan_remove(); for (j=0; j<8 ; j++) { struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j)); if (temp) { @@ -130,6 +135,7 @@ int cpqhp_unconfigure_device(struct pci_func* func) pci_stop_and_remove_bus_device(temp); } } + pci_unlock_rescan_remove(); return 0; } diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index efdc13adbe41..cf3ac1e4b099 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -718,6 +718,8 @@ static void ibm_unconfigure_device(struct pci_func *func) func->device, func->function); debug("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0); + pci_lock_rescan_remove(); + for (j = 0; j < 0x08; j++) { temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j); if (temp) { @@ -725,7 +727,10 @@ static void ibm_unconfigure_device(struct pci_func *func) pci_dev_put(temp); } } + pci_dev_put(func->dev); + + pci_unlock_rescan_remove(); } /* @@ -780,6 +785,8 @@ static int ibm_configure_device(struct pci_func *func) int flag = 0; /* this is to make sure we don't double scan the bus, for bridged devices primarily */ + pci_lock_rescan_remove(); + if (!(bus_structure_fixup(func->busno))) flag = 1; if (func->dev == NULL) @@ -789,7 +796,7 @@ static int ibm_configure_device(struct pci_func *func) if (func->dev == NULL) { struct pci_bus *bus = pci_find_bus(0, func->busno); if (!bus) - return 0; + goto out; num = pci_scan_slot(bus, PCI_DEVFN(func->device, func->function)); @@ -800,7 +807,7 @@ static int ibm_configure_device(struct pci_func *func) PCI_DEVFN(func->device, func->function)); if (func->dev == NULL) { err("ERROR... : pci_dev still NULL\n"); - return 0; + goto out; } } if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { @@ -810,6 +817,8 @@ static int ibm_configure_device(struct pci_func *func) pci_bus_add_devices(child); } + out: + pci_unlock_rescan_remove(); return 0; } diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 198355112ee7..b07d7cc2d697 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -39,22 +39,26 @@ int pciehp_configure_device(struct slot *p_slot) struct pci_dev *dev; struct pci_dev *bridge = p_slot->ctrl->pcie->port; struct pci_bus *parent = bridge->subordinate; - int num; + int num, ret = 0; struct controller *ctrl = p_slot->ctrl; + pci_lock_rescan_remove(); + dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); if (dev) { ctrl_err(ctrl, "Device %s already exists " "at %04x:%02x:00, cannot hot-add\n", pci_name(dev), pci_domain_nr(parent), parent->number); pci_dev_put(dev); - return -EINVAL; + ret = -EINVAL; + goto out; } num = pci_scan_slot(parent, PCI_DEVFN(0, 0)); if (num == 0) { ctrl_err(ctrl, "No new device found\n"); - return -ENODEV; + ret = -ENODEV; + goto out; } list_for_each_entry(dev, &parent->devices, bus_list) @@ -73,7 +77,9 @@ int pciehp_configure_device(struct slot *p_slot) pci_bus_add_devices(parent); - return 0; + out: + pci_unlock_rescan_remove(); + return ret; } int pciehp_unconfigure_device(struct slot *p_slot) @@ -90,6 +96,8 @@ int pciehp_unconfigure_device(struct slot *p_slot) __func__, pci_domain_nr(parent), parent->number); pciehp_get_adapter_status(p_slot, &presence); + pci_lock_rescan_remove(); + /* * Stopping an SR-IOV PF device removes all the associated VFs, * which will update the bus->devices list and confuse the @@ -124,5 +132,6 @@ int pciehp_unconfigure_device(struct slot *p_slot) pci_dev_put(dev); } + pci_unlock_rescan_remove(); return rc; } diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c index e9c044d15add..4fcdeedda31b 100644 --- a/drivers/pci/hotplug/rpadlpar_core.c +++ b/drivers/pci/hotplug/rpadlpar_core.c @@ -354,10 +354,15 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) { struct pci_bus *bus; struct slot *slot; + int ret = 0; + + pci_lock_rescan_remove(); bus = pcibios_find_pci_bus(dn); - if (!bus) - return -EINVAL; + if (!bus) { + ret = -EINVAL; + goto out; + } pr_debug("PCI: Removing PCI slot below EADS bridge %s\n", bus->self ? pci_name(bus->self) : ""); @@ -371,7 +376,8 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) printk(KERN_ERR "%s: unable to remove hotplug slot %s\n", __func__, drc_name); - return -EIO; + ret = -EIO; + goto out; } } @@ -382,7 +388,8 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) if (pcibios_unmap_io_space(bus)) { printk(KERN_ERR "%s: failed to unmap bus range\n", __func__); - return -ERANGE; + ret = -ERANGE; + goto out; } /* Remove the EADS bridge device itself */ @@ -390,7 +397,9 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn) pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self)); pci_stop_and_remove_bus_device(bus->self); - return 0; + out: + pci_unlock_rescan_remove(); + return ret; } /** diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index b7fc5c9255a5..4796c15fba94 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -398,7 +398,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) return retval; if (state == PRESENT) { + pci_lock_rescan_remove(); pcibios_add_pci_devices(slot->bus); + pci_unlock_rescan_remove(); slot->state = CONFIGURED; } else if (state == EMPTY) { slot->state = EMPTY; @@ -418,7 +420,9 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) if (slot->state == NOT_CONFIGURED) return -EINVAL; + pci_lock_rescan_remove(); pcibios_remove_pci_devices(slot->bus); + pci_unlock_rescan_remove(); vm_unmap_aliases(); slot->state = NOT_CONFIGURED; diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 3c7eb5dd91c6..8d2ce22151eb 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -80,7 +80,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) goto out_deconfigure; pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN); + pci_lock_rescan_remove(); pci_bus_add_devices(slot->zdev->bus); + pci_unlock_rescan_remove(); return rc; @@ -98,7 +100,7 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) return -EIO; if (slot->zdev->pdev) - pci_stop_and_remove_bus_device(slot->zdev->pdev); + pci_stop_and_remove_bus_device_locked(slot->zdev->pdev); rc = zpci_disable_device(slot->zdev); if (rc) diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 5b05a68cca6c..613043f7576f 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -459,12 +459,15 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) acpi_scan_lock_release(); } + pci_lock_rescan_remove(); + /* Call the driver for the new device */ pci_bus_add_devices(slot->pci_bus); /* Call the drivers for the new devices subordinate to PPB */ if (new_ppb) pci_bus_add_devices(new_bus); + pci_unlock_rescan_remove(); mutex_unlock(&sn_hotplug_mutex); if (rc == 0) @@ -540,6 +543,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) acpi_scan_lock_release(); } + pci_lock_rescan_remove(); /* Free the SN resources assigned to the Linux device.*/ list_for_each_entry_safe(dev, temp, &slot->pci_bus->devices, bus_list) { if (PCI_SLOT(dev->devfn) != slot->device_num + 1) @@ -550,6 +554,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } + pci_unlock_rescan_remove(); /* Remove the SSDT for the slot from the ACPI namespace */ if (SN_ACPI_BASE_SUPPORT() && ssdt_id) { diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index b0e83132542e..2bf69fe1926c 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -40,7 +40,9 @@ int __ref shpchp_configure_device(struct slot *p_slot) struct controller *ctrl = p_slot->ctrl; struct pci_dev *bridge = ctrl->pci_dev; struct pci_bus *parent = bridge->subordinate; - int num; + int num, ret = 0; + + pci_lock_rescan_remove(); dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0)); if (dev) { @@ -48,13 +50,15 @@ int __ref shpchp_configure_device(struct slot *p_slot) "at %04x:%02x:%02x, cannot hot-add\n", pci_name(dev), pci_domain_nr(parent), p_slot->bus, p_slot->device); pci_dev_put(dev); - return -EINVAL; + ret = -EINVAL; + goto out; } num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0)); if (num == 0) { ctrl_err(ctrl, "No new device found\n"); - return -ENODEV; + ret = -ENODEV; + goto out; } list_for_each_entry(dev, &parent->devices, bus_list) { @@ -75,7 +79,9 @@ int __ref shpchp_configure_device(struct slot *p_slot) pci_bus_add_devices(parent); - return 0; + out: + pci_unlock_rescan_remove(); + return ret; } int shpchp_unconfigure_device(struct slot *p_slot) @@ -89,6 +95,8 @@ int shpchp_unconfigure_device(struct slot *p_slot) ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:%02x\n", __func__, pci_domain_nr(parent), p_slot->bus, p_slot->device); + pci_lock_rescan_remove(); + list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) { if (PCI_SLOT(dev->devfn) != p_slot->device) continue; @@ -108,6 +116,8 @@ int shpchp_unconfigure_device(struct slot *p_slot) pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } + + pci_unlock_rescan_remove(); return rc; } -- cgit v1.2.3-70-g09d2 From 8b9ec1da6a310b438516468c669a7e1be046b18c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jan 2014 15:27:08 +0100 Subject: platform / x86: Use global PCI rescan-remove locking Multiple race conditions are possible between the rfkill hotplug in the asus-wmi and eeepc-laptop drivers and the generic PCI bus rescan and device removal that can be triggered via sysfs. To avoid those race conditions make asus-wmi and eeepc-laptop use global PCI rescan-remove locking around the rfkill hotplug. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/platform/x86/asus-wmi.c | 2 ++ drivers/platform/x86/eeepc-laptop.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 19c313b056c3..6fe268f6af91 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -606,6 +606,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) mutex_unlock(&asus->wmi_lock); mutex_lock(&asus->hotplug_lock); + pci_lock_rescan_remove(); if (asus->wlan.rfkill) rfkill_set_sw_state(asus->wlan.rfkill, blocked); @@ -656,6 +657,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) } out_unlock: + pci_unlock_rescan_remove(); mutex_unlock(&asus->hotplug_lock); } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index aefcc32e5634..538521b00948 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -592,6 +592,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); mutex_lock(&eeepc->hotplug_lock); + pci_lock_rescan_remove(); if (eeepc->hotplug_slot) { port = acpi_get_pci_dev(handle); @@ -649,6 +650,7 @@ out_put_dev: } out_unlock: + pci_unlock_rescan_remove(); mutex_unlock(&eeepc->hotplug_lock); } -- cgit v1.2.3-70-g09d2 From 64cdb4181d87900c6560f8c72d02c7ccf75c9070 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jan 2014 15:27:56 +0100 Subject: MPT / PCI: Use pci_stop_and_remove_bus_device_locked() Race conditions are theoretically possible between the MPT PCI device removal and the generic PCI bus rescan and device removal that can be triggered via sysfs. To avoid those race conditions make the MPT PCI code use pci_stop_and_remove_bus_device_locked(). Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/message/fusion/mptbase.c | 2 +- drivers/scsi/mpt2sas/mpt2sas_base.c | 2 +- drivers/scsi/mpt3sas/mpt3sas_base.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 767ff4d839f4..570b18a113ff 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -346,7 +346,7 @@ static int mpt_remove_dead_ioc_func(void *arg) if ((pdev == NULL)) return -1; - pci_stop_and_remove_bus_device(pdev); + pci_stop_and_remove_bus_device_locked(pdev); return 0; } diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 3901edc35812..bde63f7452bd 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -128,7 +128,7 @@ static int mpt2sas_remove_dead_ioc_func(void *arg) pdev = ioc->pdev; if ((pdev == NULL)) return -1; - pci_stop_and_remove_bus_device(pdev); + pci_stop_and_remove_bus_device_locked(pdev); return 0; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index fa785062e97b..0cf4f7000f94 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -131,7 +131,7 @@ static int mpt3sas_remove_dead_ioc_func(void *arg) pdev = ioc->pdev; if ((pdev == NULL)) return -1; - pci_stop_and_remove_bus_device(pdev); + pci_stop_and_remove_bus_device_locked(pdev); return 0; } -- cgit v1.2.3-70-g09d2 From 61cf16d8bd38c3dc52033ea75d5b1f8368514a17 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 16 Dec 2013 15:14:31 -0700 Subject: PCI: Add pci_try_reset_function(), pci_try_reset_slot(), pci_try_reset_bus() When doing a function/slot/bus reset PCI grabs the device_lock for each device to block things like suspend and driver probes, but call paths exist where this lock may already be held. This creates an opportunity for deadlock. For instance, vfio allows userspace to issue resets so long as it owns the device(s). If a driver unbind .remove callback races with userspace issuing a reset, we have a deadlock as userspace gets stuck waiting on device_lock while another thread has device_lock and waits for .remove to complete. To resolve this, we can make a version of the reset interfaces which use trylock. With this, we can safely attempt a reset and return error to userspace if there is contention. [bhelgaas: the deadlock happens when A (userspace) has a file descriptor for the device, and B waits in this path: driver_detach device_lock # take device_lock __device_release_driver pci_device_remove # pci_bus_type.remove vfio_pci_remove # pci_driver .remove vfio_del_group_dev wait_event(vfio.release_q, !vfio_dev_present) # wait (holding device_lock) Now B is stuck until A gives up the file descriptor. If A tries to acquire device_lock for any reason, we deadlock because A is waiting for B to release the lock, and B is waiting for A to release the file descriptor.] Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 3 + 2 files changed, 158 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1cbd590cf1d1..8386367814ee 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3250,6 +3250,18 @@ static void pci_dev_lock(struct pci_dev *dev) device_lock(&dev->dev); } +/* Return 1 on successful lock, 0 on contention */ +static int pci_dev_trylock(struct pci_dev *dev) +{ + if (pci_cfg_access_trylock(dev)) { + if (device_trylock(&dev->dev)) + return 1; + pci_cfg_access_unlock(dev); + } + + return 0; +} + static void pci_dev_unlock(struct pci_dev *dev) { device_unlock(&dev->dev); @@ -3393,6 +3405,34 @@ int pci_reset_function(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_reset_function); +/** + * pci_try_reset_function - quiesce and reset a PCI device function + * @dev: PCI device to reset + * + * Same as above, except return -EAGAIN if unable to lock device. + */ +int pci_try_reset_function(struct pci_dev *dev) +{ + int rc; + + rc = pci_dev_reset(dev, 1); + if (rc) + return rc; + + pci_dev_save_and_disable(dev); + + if (pci_dev_trylock(dev)) { + rc = __pci_dev_reset(dev, 0); + pci_dev_unlock(dev); + } else + rc = -EAGAIN; + + pci_dev_restore(dev); + + return rc; +} +EXPORT_SYMBOL_GPL(pci_try_reset_function); + /* Lock devices from the top of the tree down */ static void pci_bus_lock(struct pci_bus *bus) { @@ -3417,6 +3457,32 @@ static void pci_bus_unlock(struct pci_bus *bus) } } +/* Return 1 on successful lock, 0 on contention */ +static int pci_bus_trylock(struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + if (!pci_dev_trylock(dev)) + goto unlock; + if (dev->subordinate) { + if (!pci_bus_trylock(dev->subordinate)) { + pci_dev_unlock(dev); + goto unlock; + } + } + } + return 1; + +unlock: + list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) { + if (dev->subordinate) + pci_bus_unlock(dev->subordinate); + pci_dev_unlock(dev); + } + return 0; +} + /* Lock devices from the top of the tree down */ static void pci_slot_lock(struct pci_slot *slot) { @@ -3445,6 +3511,37 @@ static void pci_slot_unlock(struct pci_slot *slot) } } +/* Return 1 on successful lock, 0 on contention */ +static int pci_slot_trylock(struct pci_slot *slot) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &slot->bus->devices, bus_list) { + if (!dev->slot || dev->slot != slot) + continue; + if (!pci_dev_trylock(dev)) + goto unlock; + if (dev->subordinate) { + if (!pci_bus_trylock(dev->subordinate)) { + pci_dev_unlock(dev); + goto unlock; + } + } + } + return 1; + +unlock: + list_for_each_entry_continue_reverse(dev, + &slot->bus->devices, bus_list) { + if (!dev->slot || dev->slot != slot) + continue; + if (dev->subordinate) + pci_bus_unlock(dev->subordinate); + pci_dev_unlock(dev); + } + return 0; +} + /* Save and disable devices from the top of the tree down */ static void pci_bus_save_and_disable(struct pci_bus *bus) { @@ -3568,6 +3665,35 @@ int pci_reset_slot(struct pci_slot *slot) } EXPORT_SYMBOL_GPL(pci_reset_slot); +/** + * pci_try_reset_slot - Try to reset a PCI slot + * @slot: PCI slot to reset + * + * Same as above except return -EAGAIN if the slot cannot be locked + */ +int pci_try_reset_slot(struct pci_slot *slot) +{ + int rc; + + rc = pci_slot_reset(slot, 1); + if (rc) + return rc; + + pci_slot_save_and_disable(slot); + + if (pci_slot_trylock(slot)) { + might_sleep(); + rc = pci_reset_hotplug_slot(slot->hotplug, 0); + pci_slot_unlock(slot); + } else + rc = -EAGAIN; + + pci_slot_restore(slot); + + return rc; +} +EXPORT_SYMBOL_GPL(pci_try_reset_slot); + static int pci_bus_reset(struct pci_bus *bus, int probe) { if (!bus->self) @@ -3626,6 +3752,35 @@ int pci_reset_bus(struct pci_bus *bus) } EXPORT_SYMBOL_GPL(pci_reset_bus); +/** + * pci_try_reset_bus - Try to reset a PCI bus + * @bus: top level PCI bus to reset + * + * Same as above except return -EAGAIN if the bus cannot be locked + */ +int pci_try_reset_bus(struct pci_bus *bus) +{ + int rc; + + rc = pci_bus_reset(bus, 1); + if (rc) + return rc; + + pci_bus_save_and_disable(bus); + + if (pci_bus_trylock(bus)) { + might_sleep(); + pci_reset_bridge_secondary_bus(bus->self); + pci_bus_unlock(bus); + } else + rc = -EAGAIN; + + pci_bus_restore(bus); + + return rc; +} +EXPORT_SYMBOL_GPL(pci_try_reset_bus); + /** * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count * @dev: PCI device to query diff --git a/include/linux/pci.h b/include/linux/pci.h index 9e3ec8b951b7..d21be0343865 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -948,10 +948,13 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, int __pci_reset_function(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); +int pci_try_reset_function(struct pci_dev *dev); int pci_probe_reset_slot(struct pci_slot *slot); int pci_reset_slot(struct pci_slot *slot); +int pci_try_reset_slot(struct pci_slot *slot); int pci_probe_reset_bus(struct pci_bus *bus); int pci_reset_bus(struct pci_bus *bus); +int pci_try_reset_bus(struct pci_bus *bus); void pci_reset_bridge_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); -- cgit v1.2.3-70-g09d2 From ebd50b939c33c692a4914e66bf06a99b21ffa483 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 14 Jan 2014 17:10:39 -0700 Subject: PCI: Fix pci_check_and_unmask_intx() comment typos Fix typos in pci_check_and_unmask_intx() function comment. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 33120d156668..4f89a99e2de3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3138,7 +3138,7 @@ bool pci_check_and_mask_intx(struct pci_dev *dev) EXPORT_SYMBOL_GPL(pci_check_and_mask_intx); /** - * pci_check_and_mask_intx - unmask INTx of no interrupt is pending + * pci_check_and_unmask_intx - unmask INTx if no interrupt is pending * @dev: the PCI device to operate on * * Check if the device dev has its INTx line asserted, unmask it if not -- cgit v1.2.3-70-g09d2 From a83919e0940f6eb8f77ab1d602a063f8a6703117 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Jan 2014 15:29:19 +0100 Subject: xen/pcifront: Use global PCI rescan-remove locking Multiple race conditions are possible between the Xen pcifront device addition and removal and the generic PCI device addition and removal that can be triggered via sysfs. To avoid those race conditions make the Xen pcifront code use global PCI rescan-remove locking. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pci/xen-pcifront.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index f7197a790341..d1cd60f51f87 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -471,12 +471,15 @@ static int pcifront_scan_root(struct pcifront_device *pdev, } pcifront_init_sd(sd, domain, bus, pdev); + pci_lock_rescan_remove(); + b = pci_scan_bus_parented(&pdev->xdev->dev, bus, &pcifront_bus_ops, sd); if (!b) { dev_err(&pdev->xdev->dev, "Error creating PCI Frontend Bus!\n"); err = -ENOMEM; + pci_unlock_rescan_remove(); goto err_out; } @@ -494,6 +497,7 @@ static int pcifront_scan_root(struct pcifront_device *pdev, /* Create SysFS and notify udev of the devices. Aka: "going live" */ pci_bus_add_devices(b); + pci_unlock_rescan_remove(); return err; err_out: @@ -556,6 +560,7 @@ static void pcifront_free_roots(struct pcifront_device *pdev) dev_dbg(&pdev->xdev->dev, "cleaning up root buses\n"); + pci_lock_rescan_remove(); list_for_each_entry_safe(bus_entry, t, &pdev->root_buses, list) { list_del(&bus_entry->list); @@ -568,6 +573,7 @@ static void pcifront_free_roots(struct pcifront_device *pdev) kfree(bus_entry); } + pci_unlock_rescan_remove(); } static pci_ers_result_t pcifront_common_process(int cmd, @@ -1043,8 +1049,10 @@ static int pcifront_detach_devices(struct pcifront_device *pdev) domain, bus, slot, func); continue; } + pci_lock_rescan_remove(); pci_stop_and_remove_bus_device(pci_dev); pci_dev_put(pci_dev); + pci_unlock_rescan_remove(); dev_dbg(&pdev->xdev->dev, "PCI device %04x:%02x:%02x.%d removed.\n", -- cgit v1.2.3-70-g09d2 From 8a4c5c329de716996eea03d93753ccbb5406072b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 14 Jan 2014 12:04:51 -0700 Subject: PCI: Check parent kobject in pci_destroy_dev() If pci_stop_and_remove_bus_device() is run concurrently for a device and its parent bridge via remove_callback(), both code paths attempt to acquire pci_rescan_remove_lock. If the child device removal acquires it first, there will be no problems. However, if the parent bridge removal acquires it first, it will eventually execute pci_destroy_dev() for the child device, but that device object will not be freed yet due to the reference held by the concurrent child removal. Consequently, both pci_stop_bus_device() and pci_remove_bus_device() will be executed for that device unnecessarily and pci_destroy_dev() will see a corrupted list head in that object. Moreover, an excess put_device() will be executed for that device in that case which may lead to a use-after-free in the final kobject_put() done by sysfs_schedule_callback_work(). To avoid that problem, make pci_destroy_dev() check if the device's parent kobject is NULL, which only happens after device_del() has already run for it. Make pci_destroy_dev() return immediately whithout doing anything in that case. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pci/remove.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 10fa13f9e309..4ff36bfa785e 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -20,6 +20,9 @@ static void pci_stop_dev(struct pci_dev *dev) static void pci_destroy_dev(struct pci_dev *dev) { + if (!dev->dev.kobj.parent) + return; + device_del(&dev->dev); put_device(&dev->dev); -- cgit v1.2.3-70-g09d2 From 890ed578df82f5b7b5a874f9f2fa4f117305df5f Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 14 Jan 2014 20:45:09 -0700 Subject: vfio-pci: Use pci "try" reset interface PCI resets will attempt to take the device_lock for any device to be reset. This is a problem if that lock is already held, for instance in the device remove path. It's not sufficient to simply kill the user process or skip the reset if called after .remove as a race could result in the same deadlock. Instead, we handle all resets as "best effort" using the PCI "try" reset interfaces. This prevents the user from being able to induce a deadlock by triggering a reset. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/vfio/pci/vfio_pci.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 6ab71b9fcf8d..2319d206f630 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -139,25 +139,14 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); /* - * Careful, device_lock may already be held. This is the case if - * a driver unbind is blocked. Try to get the locks ourselves to - * prevent a deadlock. + * Try to reset the device. The success of this is dependent on + * being able to lock the device, which is not always possible. */ if (vdev->reset_works) { - bool reset_done = false; - - if (pci_cfg_access_trylock(pdev)) { - if (device_trylock(&pdev->dev)) { - __pci_reset_function_locked(pdev); - reset_done = true; - device_unlock(&pdev->dev); - } - pci_cfg_access_unlock(pdev); - } - - if (!reset_done) - pr_warn("%s: Unable to acquire locks for reset of %s\n", - __func__, dev_name(&pdev->dev)); + int ret = pci_try_reset_function(pdev); + if (ret) + pr_warn("%s: Failed to reset device %s (%d)\n", + __func__, dev_name(&pdev->dev), ret); } pci_restore_state(pdev); @@ -514,7 +503,7 @@ static long vfio_pci_ioctl(void *device_data, } else if (cmd == VFIO_DEVICE_RESET) { return vdev->reset_works ? - pci_reset_function(vdev->pdev) : -EINVAL; + pci_try_reset_function(vdev->pdev) : -EINVAL; } else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) { struct vfio_pci_hot_reset_info hdr; @@ -684,8 +673,8 @@ reset_info_exit: &info, slot); if (!ret) /* User has access, do the reset */ - ret = slot ? pci_reset_slot(vdev->pdev->slot) : - pci_reset_bus(vdev->pdev->bus); + ret = slot ? pci_try_reset_slot(vdev->pdev->slot) : + pci_try_reset_bus(vdev->pdev->bus); hot_reset_release: for (i--; i >= 0; i--) -- cgit v1.2.3-70-g09d2 From bea1b0b38439ea40b1a933ff8bc0b3c506958dd6 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jan 2014 14:57:01 -0700 Subject: Revert "EISA: Log device resources in dmesg" This reverts commit a2080d0c561c546d73cb8b296d4b7ca414e6860b. Signed-off-by: Bjorn Helgaas --- drivers/eisa/eisa-bus.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 8842cde69177..1b86fe0c2e80 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -288,7 +288,6 @@ static int __init eisa_request_resources(struct eisa_root_device *root, edev->res[i].flags = IORESOURCE_IO | IORESOURCE_BUSY; } - dev_printk(KERN_DEBUG, &edev->dev, "%pR\n", &edev->res[i]); if (request_resource(root->res, &edev->res[i])) goto failed; } -- cgit v1.2.3-70-g09d2 From 765ee51f9a3f652959b4c7297d198a28e37952b4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 Jan 2014 14:57:29 -0700 Subject: Revert "EISA: Initialize device before its resources" This reverts commit 26abfeed4341872364386c6a52b9acef8c81a81a. In the eisa_probe() force_probe path, if we were unable to request slot resources (e.g., [io 0x800-0x8ff]), we skipped the slot with "Cannot allocate resource for EISA slot %d" before reading the EISA signature in eisa_init_device(). Commit 26abfeed4341 moved eisa_init_device() earlier, so we tried to read the EISA signature before requesting the slot resources, and this caused hangs during boot. Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1251816 Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org # v3.10+ a2080d0c561c: Revert "EISA: Log device resources in dmesg" --- drivers/eisa/eisa-bus.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 1b86fe0c2e80..612afeaec3cb 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -277,11 +277,13 @@ static int __init eisa_request_resources(struct eisa_root_device *root, } if (slot) { + edev->res[i].name = NULL; edev->res[i].start = SLOT_ADDRESS(root, slot) + (i * 0x400); edev->res[i].end = edev->res[i].start + 0xff; edev->res[i].flags = IORESOURCE_IO; } else { + edev->res[i].name = NULL; edev->res[i].start = SLOT_ADDRESS(root, slot) + EISA_VENDOR_ID_OFFSET; edev->res[i].end = edev->res[i].start + 3; @@ -327,19 +329,20 @@ static int __init eisa_probe(struct eisa_root_device *root) return -ENOMEM; } - if (eisa_init_device(root, edev, 0)) { + if (eisa_request_resources(root, edev, 0)) { + dev_warn(root->dev, + "EISA: Cannot allocate resource for mainboard\n"); kfree(edev); if (!root->force_probe) - return -ENODEV; + return -EBUSY; goto force_probe; } - if (eisa_request_resources(root, edev, 0)) { - dev_warn(root->dev, - "EISA: Cannot allocate resource for mainboard\n"); + if (eisa_init_device(root, edev, 0)) { + eisa_release_resources(edev); kfree(edev); if (!root->force_probe) - return -EBUSY; + return -ENODEV; goto force_probe; } @@ -362,11 +365,6 @@ static int __init eisa_probe(struct eisa_root_device *root) continue; } - if (eisa_init_device(root, edev, i)) { - kfree(edev); - continue; - } - if (eisa_request_resources(root, edev, i)) { dev_warn(root->dev, "Cannot allocate resource for EISA slot %d\n", @@ -375,6 +373,12 @@ static int __init eisa_probe(struct eisa_root_device *root) continue; } + if (eisa_init_device(root, edev, i)) { + eisa_release_resources(edev); + kfree(edev); + continue; + } + if (edev->state == (EISA_CONFIG_ENABLED | EISA_CONFIG_FORCED)) enabled_str = " (forced enabled)"; else if (edev->state == EISA_CONFIG_FORCED) -- cgit v1.2.3-70-g09d2