diff options
Diffstat (limited to 'drivers')
42 files changed, 1514 insertions, 328 deletions
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index deb203026496..320276f42653 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -92,6 +92,7 @@ struct pci_endpoint_test { void __iomem *bar[6]; struct completion irq_raised; int last_irq; + int num_irqs; /* mutex to protect the ioctls */ struct mutex mutex; struct miscdevice miscdev; @@ -226,6 +227,9 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) u32 src_crc32; u32 dst_crc32; + if (size > SIZE_MAX - alignment) + goto err; + orig_src_addr = dma_alloc_coherent(dev, size + alignment, &orig_src_phys_addr, GFP_KERNEL); if (!orig_src_addr) { @@ -311,6 +315,9 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size) size_t alignment = test->alignment; u32 crc32; + if (size > SIZE_MAX - alignment) + goto err; + orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, GFP_KERNEL); if (!orig_addr) { @@ -369,6 +376,9 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) size_t alignment = test->alignment; u32 crc32; + if (size > SIZE_MAX - alignment) + goto err; + orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, GFP_KERNEL); if (!orig_addr) { @@ -504,6 +514,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); if (irq < 0) dev_err(dev, "failed to get MSI interrupts\n"); + test->num_irqs = irq; } err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler, @@ -533,6 +544,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, test->base = test->bar[test_reg_bar]; if (!test->base) { + err = -ENOMEM; dev_err(dev, "Cannot perform PCI test without BAR%d\n", test_reg_bar); goto err_iounmap; @@ -542,6 +554,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL); if (id < 0) { + err = id; dev_err(dev, "unable to get id\n"); goto err_iounmap; } @@ -549,17 +562,24 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id); misc_device = &test->miscdev; misc_device->minor = MISC_DYNAMIC_MINOR; - misc_device->name = name; + misc_device->name = kstrdup(name, GFP_KERNEL); + if (!misc_device->name) { + err = -ENOMEM; + goto err_ida_remove; + } misc_device->fops = &pci_endpoint_test_fops, err = misc_register(misc_device); if (err) { dev_err(dev, "failed to register device\n"); - goto err_ida_remove; + goto err_kfree_name; } return 0; +err_kfree_name: + kfree(misc_device->name); + err_ida_remove: ida_simple_remove(&pci_endpoint_test_ida, id); @@ -569,6 +589,9 @@ err_iounmap: pci_iounmap(pdev, test->bar[bar]); } + for (i = 0; i < irq; i++) + devm_free_irq(dev, pdev->irq + i, test); + err_disable_msi: pci_disable_msi(pdev); pci_release_regions(pdev); @@ -582,19 +605,25 @@ err_disable_pdev: static void pci_endpoint_test_remove(struct pci_dev *pdev) { int id; + int i; enum pci_barno bar; struct pci_endpoint_test *test = pci_get_drvdata(pdev); struct miscdevice *misc_device = &test->miscdev; if (sscanf(misc_device->name, DRV_MODULE_NAME ".%d", &id) != 1) return; + if (id < 0) + return; misc_deregister(&test->miscdev); + kfree(misc_device->name); ida_simple_remove(&pci_endpoint_test_ida, id); for (bar = BAR_0; bar <= BAR_5; bar++) { if (test->bar[bar]) pci_iounmap(pdev, test->bar[bar]); } + for (i = 0; i < test->num_irqs; i++) + devm_free_irq(&pdev->dev, pdev->irq + i, test); pci_disable_msi(pdev); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index c32a77fc8b03..90944667ccea 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -29,6 +29,15 @@ config PCI_MSI_IRQ_DOMAIN depends on PCI_MSI select GENERIC_MSI_IRQ_DOMAIN +config PCI_QUIRKS + default y + bool "Enable PCI quirk workarounds" if EXPERT + depends on PCI + help + This enables workarounds for various PCI chipset bugs/quirks. + Disable this only if your target machine is unaffected by PCI + quirks. + config PCI_DEBUG bool "PCI Debugging" depends on PCI && DEBUG_KERNEL @@ -42,13 +51,13 @@ config PCI_DEBUG config PCI_REALLOC_ENABLE_AUTO bool "Enable PCI resource re-allocation detection" depends on PCI + depends on PCI_IOV help Say Y here if you want the PCI core to detect if PCI resource re-allocation needs to be enabled. You can always use pci=realloc=on - or pci=realloc=off to override it. Note this feature is a no-op - unless PCI_IOV support is also enabled; in that case it will - automatically re-allocate PCI resources if SR-IOV BARs have not - been allocated by the BIOS. + or pci=realloc=off to override it. It will automatically + re-allocate PCI resources if SR-IOV BARs have not been allocated by + the BIOS. When in doubt, say N. diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 66a21acad952..fa56267fa2c0 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -16,9 +16,6 @@ obj-$(CONFIG_PCIEPORTBUS) += pcie/ # Build the PCI Hotplug drivers if we were asked to obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ -ifdef CONFIG_HOTPLUG_PCI -obj-y += hotplug-pci.o -endif # Build the PCI MSI interrupt support obj-$(CONFIG_PCI_MSI) += msi.o diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig index 22ec82fcdea2..113e09440f85 100644 --- a/drivers/pci/dwc/Kconfig +++ b/drivers/pci/dwc/Kconfig @@ -169,4 +169,14 @@ config PCIE_KIRIN Say Y here if you want PCIe controller support on HiSilicon Kirin series SoCs. +config PCIE_HISI_STB + bool "HiSilicon STB SoCs PCIe controllers" + depends on ARCH_HISI + depends on PCI + depends on PCI_MSI_IRQ_DOMAIN + select PCIEPORTBUS + select PCIE_DW_HOST + help + Say Y here if you want PCIe controller support on HiSilicon STB SoCs + endmenu diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile index c61be9738cce..54f56f6e9236 100644 --- a/drivers/pci/dwc/Makefile +++ b/drivers/pci/dwc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o +obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o # The following drivers are for devices that use the generic ACPI # pci_root.c driver but don't support standard ECAM config access. diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c index 34427a6a15af..d0848006945a 100644 --- a/drivers/pci/dwc/pci-dra7xx.c +++ b/drivers/pci/dwc/pci-dra7xx.c @@ -794,6 +794,22 @@ static int dra7xx_pcie_resume_noirq(struct device *dev) } #endif +void dra7xx_pcie_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); + int ret; + + dra7xx_pcie_stop_link(dra7xx->pci); + + ret = pm_runtime_put_sync(dev); + if (ret < 0) + dev_dbg(dev, "pm_runtime_put_sync failed\n"); + + pm_runtime_disable(dev); + dra7xx_pcie_disable_phy(dra7xx); +} + static const struct dev_pm_ops dra7xx_pcie_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend, dra7xx_pcie_resume) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend_noirq, @@ -807,5 +823,6 @@ static struct platform_driver dra7xx_pcie_driver = { .suppress_bind_attrs = true, .pm = &dra7xx_pcie_pm_ops, }, + .shutdown = dra7xx_pcie_shutdown, }; builtin_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe); diff --git a/drivers/pci/dwc/pcie-histb.c b/drivers/pci/dwc/pcie-histb.c new file mode 100644 index 000000000000..33b01b734d7d --- /dev/null +++ b/drivers/pci/dwc/pcie-histb.c @@ -0,0 +1,470 @@ +/* + * PCIe host controller driver for HiSilicon STB SoCs + * + * Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com + * + * Authors: Ruqiang Ju <juruqiang@hisilicon.com> + * Jianguo Sun <sunjianguo1@huawei.com> + * + * 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 <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/pci.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/resource.h> +#include <linux/reset.h> + +#include "pcie-designware.h" + +#define to_histb_pcie(x) dev_get_drvdata((x)->dev) + +#define PCIE_SYS_CTRL0 0x0000 +#define PCIE_SYS_CTRL1 0x0004 +#define PCIE_SYS_CTRL7 0x001C +#define PCIE_SYS_CTRL13 0x0034 +#define PCIE_SYS_CTRL15 0x003C +#define PCIE_SYS_CTRL16 0x0040 +#define PCIE_SYS_CTRL17 0x0044 + +#define PCIE_SYS_STAT0 0x0100 +#define PCIE_SYS_STAT4 0x0110 + +#define PCIE_RDLH_LINK_UP BIT(5) +#define PCIE_XMLH_LINK_UP BIT(15) +#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21) +#define PCIE_APP_LTSSM_ENABLE BIT(11) + +#define PCIE_DEVICE_TYPE_MASK GENMASK(31, 28) +#define PCIE_WM_EP 0 +#define PCIE_WM_LEGACY BIT(1) +#define PCIE_WM_RC BIT(30) + +#define PCIE_LTSSM_STATE_MASK GENMASK(5, 0) +#define PCIE_LTSSM_STATE_ACTIVE 0x11 + +struct histb_pcie { + struct dw_pcie *pci; + struct clk *aux_clk; + struct clk *pipe_clk; + struct clk *sys_clk; + struct clk *bus_clk; + struct phy *phy; + struct reset_control *soft_reset; + struct reset_control *sys_reset; + struct reset_control *bus_reset; + void __iomem *ctrl; + int reset_gpio; +}; + +static u32 histb_pcie_readl(struct histb_pcie *histb_pcie, u32 reg) +{ + return readl(histb_pcie->ctrl + reg); +} + +static void histb_pcie_writel(struct histb_pcie *histb_pcie, u32 reg, u32 val) +{ + writel(val, histb_pcie->ctrl + reg); +} + +static void histb_pcie_dbi_w_mode(struct pcie_port *pp, bool enable) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct histb_pcie *hipcie = to_histb_pcie(pci); + u32 val; + + val = histb_pcie_readl(hipcie, PCIE_SYS_CTRL0); + if (enable) + val |= PCIE_ELBI_SLV_DBI_ENABLE; + else + val &= ~PCIE_ELBI_SLV_DBI_ENABLE; + histb_pcie_writel(hipcie, PCIE_SYS_CTRL0, val); +} + +static void histb_pcie_dbi_r_mode(struct pcie_port *pp, bool enable) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct histb_pcie *hipcie = to_histb_pcie(pci); + u32 val; + + val = histb_pcie_readl(hipcie, PCIE_SYS_CTRL1); + if (enable) + val |= PCIE_ELBI_SLV_DBI_ENABLE; + else + val &= ~PCIE_ELBI_SLV_DBI_ENABLE; + histb_pcie_writel(hipcie, PCIE_SYS_CTRL1, val); +} + +static u32 histb_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size) +{ + u32 val; + + histb_pcie_dbi_r_mode(&pci->pp, true); + dw_pcie_read(base + reg, size, &val); + histb_pcie_dbi_r_mode(&pci->pp, false); + + return val; +} + +static void histb_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size, u32 val) +{ + histb_pcie_dbi_w_mode(&pci->pp, true); + dw_pcie_write(base + reg, size, val); + histb_pcie_dbi_w_mode(&pci->pp, false); +} + +static int histb_pcie_rd_own_conf(struct pcie_port *pp, int where, + int size, u32 *val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int ret; + + histb_pcie_dbi_r_mode(pp, true); + ret = dw_pcie_read(pci->dbi_base + where, size, val); + histb_pcie_dbi_r_mode(pp, false); + + return ret; +} + +static int histb_pcie_wr_own_conf(struct pcie_port *pp, int where, + int size, u32 val) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + int ret; + + histb_pcie_dbi_w_mode(pp, true); + ret = dw_pcie_write(pci->dbi_base + where, size, val); + histb_pcie_dbi_w_mode(pp, false); + + return ret; +} + +static int histb_pcie_link_up(struct dw_pcie *pci) +{ + struct histb_pcie *hipcie = to_histb_pcie(pci); + u32 regval; + u32 status; + + regval = histb_pcie_readl(hipcie, PCIE_SYS_STAT0); + status = histb_pcie_readl(hipcie, PCIE_SYS_STAT4); + status &= PCIE_LTSSM_STATE_MASK; + if ((regval & PCIE_XMLH_LINK_UP) && (regval & PCIE_RDLH_LINK_UP) && + (status == PCIE_LTSSM_STATE_ACTIVE)) + return 1; + + return 0; +} + +static int histb_pcie_establish_link(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct histb_pcie *hipcie = to_histb_pcie(pci); + u32 regval; + + if (dw_pcie_link_up(pci)) { + dev_info(pci->dev, "Link already up\n"); + return 0; + } + + /* PCIe RC work mode */ + regval = histb_pcie_readl(hipcie, PCIE_SYS_CTRL0); + regval &= ~PCIE_DEVICE_TYPE_MASK; + regval |= PCIE_WM_RC; + histb_pcie_writel(hipcie, PCIE_SYS_CTRL0, regval); + + /* setup root complex */ + dw_pcie_setup_rc(pp); + + /* assert LTSSM enable */ + regval = histb_pcie_readl(hipcie, PCIE_SYS_CTRL7); + regval |= PCIE_APP_LTSSM_ENABLE; + histb_pcie_writel(hipcie, PCIE_SYS_CTRL7, regval); + + return dw_pcie_wait_for_link(pci); +} + +static int histb_pcie_host_init(struct pcie_port *pp) +{ + histb_pcie_establish_link(pp); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); + + return 0; +} + +static struct dw_pcie_host_ops histb_pcie_host_ops = { + .rd_own_conf = histb_pcie_rd_own_conf, + .wr_own_conf = histb_pcie_wr_own_conf, + .host_init = histb_pcie_host_init, +}; + +static irqreturn_t histb_pcie_msi_irq_handler(int irq, void *arg) +{ + struct pcie_port *pp = arg; + + return dw_handle_msi_irq(pp); +} + +static void histb_pcie_host_disable(struct histb_pcie *hipcie) +{ + reset_control_assert(hipcie->soft_reset); + reset_control_assert(hipcie->sys_reset); + reset_control_assert(hipcie->bus_reset); + + clk_disable_unprepare(hipcie->aux_clk); + clk_disable_unprepare(hipcie->pipe_clk); + clk_disable_unprepare(hipcie->sys_clk); + clk_disable_unprepare(hipcie->bus_clk); + + if (gpio_is_valid(hipcie->reset_gpio)) + gpio_set_value_cansleep(hipcie->reset_gpio, 0); +} + +static int histb_pcie_host_enable(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct histb_pcie *hipcie = to_histb_pcie(pci); + struct device *dev = pci->dev; + int ret; + + /* power on PCIe device if have */ + if (gpio_is_valid(hipcie->reset_gpio)) + gpio_set_value_cansleep(hipcie->reset_gpio, 1); + + ret = clk_prepare_enable(hipcie->bus_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable bus clk\n"); + goto err_bus_clk; + } + + ret = clk_prepare_enable(hipcie->sys_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable sys clk\n"); + goto err_sys_clk; + } + + ret = clk_prepare_enable(hipcie->pipe_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable pipe clk\n"); + goto err_pipe_clk; + } + + ret = clk_prepare_enable(hipcie->aux_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable aux clk\n"); + goto err_aux_clk; + } + + reset_control_assert(hipcie->soft_reset); + reset_control_deassert(hipcie->soft_reset); + + reset_control_assert(hipcie->sys_reset); + reset_control_deassert(hipcie->sys_reset); + + reset_control_assert(hipcie->bus_reset); + reset_control_deassert(hipcie->bus_reset); + + return 0; + +err_aux_clk: + clk_disable_unprepare(hipcie->aux_clk); +err_pipe_clk: + clk_disable_unprepare(hipcie->pipe_clk); +err_sys_clk: + clk_disable_unprepare(hipcie->sys_clk); +err_bus_clk: + clk_disable_unprepare(hipcie->bus_clk); + + return ret; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .read_dbi = histb_pcie_read_dbi, + .write_dbi = histb_pcie_write_dbi, + .link_up = histb_pcie_link_up, +}; + +static int histb_pcie_probe(struct platform_device *pdev) +{ + struct histb_pcie *hipcie; + struct dw_pcie *pci; + struct pcie_port *pp; + struct resource *res; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + enum of_gpio_flags of_flags; + unsigned long flag = GPIOF_DIR_OUT; + int ret; + + hipcie = devm_kzalloc(dev, sizeof(*hipcie), GFP_KERNEL); + if (!hipcie) + return -ENOMEM; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + hipcie->pci = pci; + pp = &pci->pp; + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); + hipcie->ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(hipcie->ctrl)) { + dev_err(dev, "cannot get control reg base\n"); + return PTR_ERR(hipcie->ctrl); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc-dbi"); + pci->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pci->dbi_base)) { + dev_err(dev, "cannot get rc-dbi base\n"); + return PTR_ERR(pci->dbi_base); + } + + hipcie->reset_gpio = of_get_named_gpio_flags(np, + "reset-gpios", 0, &of_flags); + if (of_flags & OF_GPIO_ACTIVE_LOW) + flag |= GPIOF_ACTIVE_LOW; + if (gpio_is_valid(hipcie->reset_gpio)) { + ret = devm_gpio_request_one(dev, hipcie->reset_gpio, + flag, "PCIe device power control"); + if (ret) { + dev_err(dev, "unable to request gpio\n"); + return ret; + } + } + + hipcie->aux_clk = devm_clk_get(dev, "aux"); + if (IS_ERR(hipcie->aux_clk)) { + dev_err(dev, "Failed to get PCIe aux clk\n"); + return PTR_ERR(hipcie->aux_clk); + } + + hipcie->pipe_clk = devm_clk_get(dev, "pipe"); + if (IS_ERR(hipcie->pipe_clk)) { + dev_err(dev, "Failed to get PCIe pipe clk\n"); + return PTR_ERR(hipcie->pipe_clk); + } + + hipcie->sys_clk = devm_clk_get(dev, "sys"); + if (IS_ERR(hipcie->sys_clk)) { + dev_err(dev, "Failed to get PCIEe sys clk\n"); + return PTR_ERR(hipcie->sys_clk); + } + + hipcie->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(hipcie->bus_clk)) { + dev_err(dev, "Failed to get PCIe bus clk\n"); + return PTR_ERR(hipcie->bus_clk); + } + + hipcie->soft_reset = devm_reset_control_get(dev, "soft"); + if (IS_ERR(hipcie->soft_reset)) { + dev_err(dev, "couldn't get soft reset\n"); + return PTR_ERR(hipcie->soft_reset); + } + + hipcie->sys_reset = devm_reset_control_get(dev, "sys"); + if (IS_ERR(hipcie->sys_reset)) { + dev_err(dev, "couldn't get sys reset\n"); + return PTR_ERR(hipcie->sys_reset); + } + + hipcie->bus_reset = devm_reset_control_get(dev, "bus"); + if (IS_ERR(hipcie->bus_reset)) { + dev_err(dev, "couldn't get bus reset\n"); + return PTR_ERR(hipcie->bus_reset); + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pp->msi_irq < 0) { + dev_err(dev, "Failed to get MSI IRQ\n"); + return pp->msi_irq; + } + + ret = devm_request_irq(dev, pp->msi_irq, + histb_pcie_msi_irq_handler, + IRQF_SHARED, "histb-pcie-msi", pp); + if (ret) { + dev_err(dev, "cannot request MSI IRQ\n"); + return ret; + } + } + + hipcie->phy = devm_phy_get(dev, "phy"); + if (IS_ERR(hipcie->phy)) { + dev_info(dev, "no pcie-phy found\n"); + hipcie->phy = NULL; + /* fall through here! + * if no pcie-phy found, phy init + * should be done under boot! + */ + } else { + phy_init(hipcie->phy); + } + + pp->root_bus_nr = -1; + pp->ops = &histb_pcie_host_ops; + + platform_set_drvdata(pdev, hipcie); + + ret = histb_pcie_host_enable(pp); + if (ret) { + dev_err(dev, "failed to enable host\n"); + return ret; + } + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int histb_pcie_remove(struct platform_device *pdev) +{ + struct histb_pcie *hipcie = platform_get_drvdata(pdev); + + histb_pcie_host_disable(hipcie); + + if (hipcie->phy) + phy_exit(hipcie->phy); + + return 0; +} + +static const struct of_device_id histb_pcie_of_match[] = { + { .compatible = "hisilicon,hi3798cv200-pcie", }, + {}, +}; +MODULE_DEVICE_TABLE(of, histb_pcie_of_match); + +static struct platform_driver histb_pcie_platform_driver = { + .probe = histb_pcie_probe, + .remove = histb_pcie_remove, + .driver = { + .name = "histb-pcie", + .of_match_table = histb_pcie_of_match, + }, +}; +module_platform_driver(histb_pcie_platform_driver); + +MODULE_DESCRIPTION("HiSilicon STB PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c index 96028f01bc90..a19919c17d04 100644 --- a/drivers/pci/host/pci-ftpci100.c +++ b/drivers/pci/host/pci-ftpci100.c @@ -481,7 +481,7 @@ static int faraday_pci_probe(struct platform_device *pdev) } p->bus_clk = devm_clk_get(dev, "PCICLK"); if (IS_ERR(p->bus_clk)) - return PTR_ERR(clk); + return PTR_ERR(p->bus_clk); ret = clk_prepare_enable(p->bus_clk); if (ret) { dev_err(dev, "could not prepare PCICLK\n"); diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index 7d709a7e0aa8..2f05511ce718 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -35,6 +35,40 @@ static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { } }; +static bool pci_dw_valid_device(struct pci_bus *bus, unsigned int devfn) +{ + struct pci_config_window *cfg = bus->sysdata; + + /* + * The Synopsys DesignWare PCIe controller in ECAM mode will not filter + * type 0 config TLPs sent to devices 1 and up on its downstream port, + * resulting in devices appearing multiple times on bus 0 unless we + * filter out those accesses here. + */ + if (bus->number == cfg->busr.start && PCI_SLOT(devfn) > 0) + return false; + + return true; +} + +static void __iomem *pci_dw_ecam_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + if (!pci_dw_valid_device(bus, devfn)) + return NULL; + + return pci_ecam_map_bus(bus, devfn, where); +} + +static struct pci_ecam_ops pci_dw_ecam_bus_ops = { + .bus_shift = 20, + .pci_ops = { + .map_bus = pci_dw_ecam_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, + } +}; + static const struct of_device_id gen_pci_of_match[] = { { .compatible = "pci-host-cam-generic", .data = &gen_pci_cfg_cam_bus_ops }, @@ -42,6 +76,15 @@ static const struct of_device_id gen_pci_of_match[] = { { .compatible = "pci-host-ecam-generic", .data = &pci_generic_ecam_ops }, + { .compatible = "marvell,armada8k-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + + { .compatible = "socionext,synquacer-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + + { .compatible = "snps,dw-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + { }, }; diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 0fe3ea164ee5..04dac6a42c9f 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -879,7 +879,7 @@ static void hv_irq_unmask(struct irq_data *data) int cpu; u64 res; - dest = irq_data_get_affinity_mask(data); + dest = irq_data_get_effective_affinity_mask(data); pdev = msi_desc_to_pci_dev(msi_desc); pbus = pdev->bus; hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); @@ -1042,6 +1042,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) struct hv_pci_dev *hpdev; struct pci_bus *pbus; struct pci_dev *pdev; + struct cpumask *dest; struct compose_comp_ctxt comp; struct tran_int_desc *int_desc; struct { @@ -1056,6 +1057,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) int ret; pdev = msi_desc_to_pci_dev(irq_data_get_msi_desc(data)); + dest = irq_data_get_effective_affinity_mask(data); pbus = pdev->bus; hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn)); @@ -1081,14 +1083,14 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) switch (pci_protocol_version) { case PCI_PROTOCOL_VERSION_1_1: size = hv_compose_msi_req_v1(&ctxt.int_pkts.v1, - irq_data_get_affinity_mask(data), + dest, hpdev->desc.win_slot.slot, cfg->vector); break; case PCI_PROTOCOL_VERSION_1_2: size = hv_compose_msi_req_v2(&ctxt.int_pkts.v2, - irq_data_get_affinity_mask(data), + dest, hpdev->desc.win_slot.slot, cfg->vector); break; diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index b468b8cccf8d..5cc4f594d79a 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -105,7 +105,7 @@ static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg) return readl_relaxed(pcie->cra_base + reg); } -static bool altera_pcie_link_is_up(struct altera_pcie *pcie) +static bool altera_pcie_link_up(struct altera_pcie *pcie) { return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); } @@ -142,7 +142,7 @@ static bool altera_pcie_valid_device(struct altera_pcie *pcie, { /* If there is no link, then there is no device */ if (bus->number != pcie->root_bus_nr) { - if (!altera_pcie_link_is_up(pcie)) + if (!altera_pcie_link_up(pcie)) return false; } @@ -412,7 +412,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie) /* Wait for link is up */ start_jiffies = jiffies; for (;;) { - if (altera_pcie_link_is_up(pcie)) + if (altera_pcie_link_up(pcie)) break; if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { @@ -427,7 +427,7 @@ static void altera_pcie_retrain(struct altera_pcie *pcie) { u16 linkcap, linkstat, linkctl; - if (!altera_pcie_link_is_up(pcie)) + if (!altera_pcie_link_up(pcie)) return; /* diff --git a/drivers/pci/host/pcie-iproc-msi.c b/drivers/pci/host/pcie-iproc-msi.c index 2d0f535a2f69..990fc906d73d 100644 --- a/drivers/pci/host/pcie-iproc-msi.c +++ b/drivers/pci/host/pcie-iproc-msi.c @@ -179,7 +179,7 @@ static struct irq_chip iproc_msi_irq_chip = { static struct msi_domain_info iproc_msi_domain_info = { .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX, + MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, .chip = &iproc_msi_irq_chip, }; @@ -237,7 +237,7 @@ static void iproc_msi_irq_compose_msi_msg(struct irq_data *data, addr = msi->msi_addr + iproc_msi_addr_offset(msi, data->hwirq); msg->address_lo = lower_32_bits(addr); msg->address_hi = upper_32_bits(addr); - msg->data = data->hwirq; + msg->data = data->hwirq << 5; } static struct irq_chip iproc_msi_bottom_irq_chip = { @@ -251,7 +251,7 @@ static int iproc_msi_irq_domain_alloc(struct irq_domain *domain, void *args) { struct iproc_msi *msi = domain->host_data; - int hwirq; + int hwirq, i; mutex_lock(&msi->bitmap_lock); @@ -267,10 +267,14 @@ static int iproc_msi_irq_domain_alloc(struct irq_domain *domain, mutex_unlock(&msi->bitmap_lock); - irq_domain_set_info(domain, virq, hwirq, &iproc_msi_bottom_irq_chip, - domain->host_data, handle_simple_irq, NULL, NULL); + for (i = 0; i < nr_irqs; i++) { + irq_domain_set_info(domain, virq + i, hwirq + i, + &iproc_msi_bottom_irq_chip, + domain->host_data, handle_simple_irq, + NULL, NULL); + } - return 0; + return hwirq; } static void iproc_msi_irq_domain_free(struct irq_domain *domain, @@ -302,7 +306,8 @@ static inline u32 decode_msi_hwirq(struct iproc_msi *msi, u32 eq, u32 head) offs = iproc_msi_eq_offset(msi, eq) + head * sizeof(u32); msg = (u32 *)(msi->eq_cpu + offs); - hwirq = *msg & IPROC_MSI_EQ_MASK; + hwirq = readl(msg); + hwirq = (hwirq >> 5) + (hwirq & 0x1f); /* * Since we have multiple hwirq mapped to a single MSI vector, diff --git a/drivers/pci/hotplug-pci.c b/drivers/pci/hotplug-pci.c deleted file mode 100644 index c68366cee6b7..000000000000 --- a/drivers/pci/hotplug-pci.c +++ /dev/null @@ -1,29 +0,0 @@ -/* Core PCI functionality used only by PCI hotplug */ - -#include <linux/pci.h> -#include <linux/export.h> -#include "pci.h" - -int pci_hp_add_bridge(struct pci_dev *dev) -{ - struct pci_bus *parent = dev->bus; - int pass, busnr, start = parent->busn_res.start; - int end = parent->busn_res.end; - - for (busnr = start; busnr <= end; busnr++) { - if (!pci_find_bus(pci_domain_nr(parent), busnr)) - break; - } - if (busnr-- > end) { - printk(KERN_ERR "No bus number available for hot-added bridge %s\n", - pci_name(dev)); - return -1; - } - for (pass = 0; pass < 2; pass++) - busnr = pci_scan_bridge(parent, dev, busnr, pass); - if (!dev->subordinate) - return -1; - - return 0; -} -EXPORT_SYMBOL_GPL(pci_hp_add_bridge); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 5ed2dcaa8e27..5db6f1839dad 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -462,18 +462,15 @@ static void enable_slot(struct acpiphp_slot *slot) acpiphp_rescan_slot(slot); max = acpiphp_max_busnr(bus); for (pass = 0; pass < 2; pass++) { - list_for_each_entry(dev, &bus->devices, bus_list) { + for_each_pci_bridge(dev, bus) { if (PCI_SLOT(dev->devfn) != slot->device) continue; - if (pci_is_bridge(dev)) { - max = pci_scan_bridge(bus, dev, max, pass); - if (pass && dev->subordinate) { - check_hotplug_bridge(slot, dev); - pcibios_resource_survey_bus(dev->subordinate); - __pci_bus_size_bridges(dev->subordinate, - &add_list); - } + max = pci_scan_bridge(bus, dev, max, pass); + if (pass && dev->subordinate) { + check_hotplug_bridge(slot, dev); + pcibios_resource_survey_bus(dev->subordinate); + __pci_bus_size_bridges(dev->subordinate, &add_list); } } } diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 80c80017197d..f616358fa938 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -286,14 +286,11 @@ int cpci_configure_slot(struct slot *slot) } parent = slot->dev->bus; - list_for_each_entry(dev, &parent->devices, bus_list) { - if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn)) - continue; - if (pci_is_bridge(dev)) + for_each_pci_bridge(dev, parent) { + if (PCI_SLOT(dev->devfn) == PCI_SLOT(slot->devfn)) pci_hp_add_bridge(dev); } - pci_assign_unassigned_bridge_resources(parent->self); pci_bus_add_devices(parent); diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h index 48c8a066a6b7..c2bbe6b65d06 100644 --- a/drivers/pci/hotplug/cpqphp.h +++ b/drivers/pci/hotplug/cpqphp.h @@ -410,7 +410,7 @@ void cpqhp_create_debugfs_files(struct controller *ctrl); void cpqhp_remove_debugfs_files(struct controller *ctrl); /* controller functions */ -void cpqhp_pushbutton_thread(unsigned long event_pointer); +void cpqhp_pushbutton_thread(struct timer_list *t); irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data); int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_start); diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 4d06b8461255..70967ac75265 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -661,9 +661,8 @@ static int ctrl_slot_setup(struct controller *ctrl, slot->p_sm_slot = slot_entry; - init_timer(&slot->task_event); + timer_setup(&slot->task_event, cpqhp_pushbutton_thread, 0); slot->task_event.expires = jiffies + 5 * HZ; - slot->task_event.function = cpqhp_pushbutton_thread; /*FIXME: these capabilities aren't used but if they are * they need to be correctly implemented diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index a55653b54eed..a93069e739cb 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -47,7 +47,7 @@ static void interrupt_event_handler(struct controller *ctrl); static struct task_struct *cpqhp_event_thread; -static unsigned long pushbutton_pending; /* = 0 */ +static struct timer_list *pushbutton_pending; /* = NULL */ /* delay is in jiffies to wait for */ static void long_delay(int delay) @@ -1732,9 +1732,10 @@ static u32 remove_board(struct pci_func *func, u32 replace_flag, struct controll return 0; } -static void pushbutton_helper_thread(unsigned long data) +static void pushbutton_helper_thread(struct timer_list *t) { - pushbutton_pending = data; + pushbutton_pending = t; + wake_up_process(cpqhp_event_thread); } @@ -1883,13 +1884,13 @@ static void interrupt_event_handler(struct controller *ctrl) wait_for_ctrl_irq(ctrl); mutex_unlock(&ctrl->crit_sect); - init_timer(&p_slot->task_event); + timer_setup(&p_slot->task_event, + pushbutton_helper_thread, + 0); p_slot->hp_slot = hp_slot; p_slot->ctrl = ctrl; /* p_slot->physical_slot = physical_slot; */ p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ - p_slot->task_event.function = pushbutton_helper_thread; - p_slot->task_event.data = (u32) p_slot; dbg("add_timer p_slot = %p\n", p_slot); add_timer(&p_slot->task_event); @@ -1920,15 +1921,15 @@ static void interrupt_event_handler(struct controller *ctrl) * Scheduled procedure to handle blocking stuff for the pushbuttons. * Handles all pending events and exits. */ -void cpqhp_pushbutton_thread(unsigned long slot) +void cpqhp_pushbutton_thread(struct timer_list *t) { u8 hp_slot; u8 device; struct pci_func *func; - struct slot *p_slot = (struct slot *) slot; + struct slot *p_slot = from_timer(p_slot, t, task_event); struct controller *ctrl = (struct controller *) p_slot->ctrl; - pushbutton_pending = 0; + pushbutton_pending = NULL; hp_slot = p_slot->hp_slot; device = p_slot->device; diff --git a/drivers/pci/hotplug/ibmphp_pci.c b/drivers/pci/hotplug/ibmphp_pci.c index dc1876feb06f..25edd0b18b75 100644 --- a/drivers/pci/hotplug/ibmphp_pci.c +++ b/drivers/pci/hotplug/ibmphp_pci.c @@ -1267,20 +1267,19 @@ static int unconfigure_boot_device(u8 busno, u8 device, u8 function) size = size & 0xFFFFFFFC; size = ~size + 1; end_address = start_address + size - 1; - if (ibmphp_find_resource(bus, start_address, &io, IO) < 0) { - err("cannot find corresponding IO resource to remove\n"); - return -EIO; - } + if (ibmphp_find_resource(bus, start_address, &io, IO)) + goto report_search_failure; + debug("io->start = %x\n", io->start); temp_end = io->end; start_address = io->end + 1; ibmphp_remove_resource(io); /* This is needed b/c of the old I/O restrictions in the BIOS */ while (temp_end < end_address) { - if (ibmphp_find_resource(bus, start_address, &io, IO) < 0) { - err("cannot find corresponding IO resource to remove\n"); - return -EIO; - } + if (ibmphp_find_resource(bus, start_address, + &io, IO)) + goto report_search_failure; + debug("io->start = %x\n", io->start); temp_end = io->end; start_address = io->end + 1; @@ -1327,6 +1326,10 @@ static int unconfigure_boot_device(u8 busno, u8 device, u8 function) } /* end of for */ return 0; + +report_search_failure: + err("cannot find corresponding IO resource to remove\n"); + return -EIO; } static int unconfigure_boot_bridge(u8 busno, u8 device, u8 function) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index ec0b4c11ccd9..83f3d4af3677 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -113,10 +113,11 @@ static int board_added(struct slot *p_slot) retval = pciehp_configure_device(p_slot); if (retval) { - ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", - pci_domain_nr(parent), parent->number); - if (retval != -EEXIST) + if (retval != -EEXIST) { + ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", + pci_domain_nr(parent), parent->number); goto err_exit; + } } pciehp_green_led_on(p_slot); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index e5d5ce9e3010..7bab0606f1a9 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -50,14 +50,13 @@ static irqreturn_t pcie_isr(int irq, void *dev_id); static void start_int_poll_timer(struct controller *ctrl, int sec); /* This is the interrupt polling timeout function. */ -static void int_poll_timeout(unsigned long data) +static void int_poll_timeout(struct timer_list *t) { - struct controller *ctrl = (struct controller *)data; + struct controller *ctrl = from_timer(ctrl, t, poll_timer); /* Poll for interrupt events. regs == NULL => polling */ pcie_isr(0, ctrl); - init_timer(&ctrl->poll_timer); if (!pciehp_poll_time) pciehp_poll_time = 2; /* default polling interval is 2 sec */ @@ -71,8 +70,6 @@ static void start_int_poll_timer(struct controller *ctrl, int sec) if ((sec <= 0) || (sec > 60)) sec = 2; - ctrl->poll_timer.function = &int_poll_timeout; - ctrl->poll_timer.data = (unsigned long)ctrl; ctrl->poll_timer.expires = jiffies + sec * HZ; add_timer(&ctrl->poll_timer); } @@ -83,7 +80,7 @@ static inline int pciehp_request_irq(struct controller *ctrl) /* Install interrupt polling timer. Start with 10 sec delay */ if (pciehp_poll_mode) { - init_timer(&ctrl->poll_timer); + timer_setup(&ctrl->poll_timer, int_poll_timeout, 0); start_int_poll_timer(ctrl, 10); return 0; } @@ -764,8 +761,7 @@ int pciehp_reset_slot(struct slot *slot, int probe) ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask); if (pciehp_poll_mode) - int_poll_timeout(ctrl->poll_timer.data); - + int_poll_timeout(&ctrl->poll_timer); return 0; } @@ -795,7 +791,7 @@ static int pcie_init_slot(struct controller *ctrl) if (!slot) return -ENOMEM; - slot->wq = alloc_workqueue("pciehp-%u", 0, 0, PSN(ctrl)); + slot->wq = alloc_ordered_workqueue("pciehp-%u", 0, PSN(ctrl)); if (!slot->wq) goto abort; @@ -862,11 +858,16 @@ struct controller *pcie_init(struct pcie_device *dev) if (link_cap & PCI_EXP_LNKCAP_DLLLARC) ctrl->link_active_reporting = 1; - /* Clear all remaining event bits in Slot Status register */ + /* + * Clear all remaining event bits in Slot Status register except + * Presence Detect Changed. We want to make sure possible + * hotplug event is triggered when the interrupt is unmasked so + * that we don't lose that event. + */ 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 | PCI_EXP_SLTSTA_DLLSC); + PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC | + PCI_EXP_SLTSTA_DLLSC); ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c\n", (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 19f30a9f461d..2a1ca020cf5a 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -46,7 +46,11 @@ int pciehp_configure_device(struct slot *p_slot) 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", + /* + * The device is already there. Either configured by the + * boot firmware or a previous hotplug event. + */ + ctrl_dbg(ctrl, "Device %s already exists at %04x:%02x:00, skipping hot-add\n", pci_name(dev), pci_domain_nr(parent), parent->number); pci_dev_put(dev); ret = -EEXIST; @@ -60,9 +64,8 @@ int pciehp_configure_device(struct slot *p_slot) goto out; } - list_for_each_entry(dev, &parent->devices, bus_list) - if (pci_is_bridge(dev)) - pci_hp_add_bridge(dev); + for_each_pci_bridge(dev, parent) + pci_hp_add_bridge(dev); pci_assign_unassigned_bridge_resources(bridge); pcie_bus_configure_settings(parent); diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index e5824c7b7b6b..4810e9626d9f 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -229,14 +229,13 @@ static inline int shpc_indirect_read(struct controller *ctrl, int index, /* * This is the interrupt polling timeout function. */ -static void int_poll_timeout(unsigned long data) +static void int_poll_timeout(struct timer_list *t) { - struct controller *ctrl = (struct controller *)data; + struct controller *ctrl = from_timer(ctrl, t, poll_timer); /* Poll for interrupt events. regs == NULL => polling */ shpc_isr(0, ctrl); - init_timer(&ctrl->poll_timer); if (!shpchp_poll_time) shpchp_poll_time = 2; /* default polling interval is 2 sec */ @@ -252,8 +251,6 @@ static void start_int_poll_timer(struct controller *ctrl, int sec) if ((sec <= 0) || (sec > 60)) sec = 2; - ctrl->poll_timer.function = &int_poll_timeout; - ctrl->poll_timer.data = (unsigned long)ctrl; ctrl->poll_timer.expires = jiffies + sec * HZ; add_timer(&ctrl->poll_timer); } @@ -1054,7 +1051,7 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev) if (shpchp_poll_mode) { /* Install interrupt polling timer. Start with 10 sec delay */ - init_timer(&ctrl->poll_timer); + timer_setup(&ctrl->poll_timer, int_poll_timeout, 0); start_int_poll_timer(ctrl, 10); } else { /* Installs the interrupt handler */ diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index f8cd3a27e351..ea63db58b4b1 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -61,10 +61,8 @@ int shpchp_configure_device(struct slot *p_slot) goto out; } - list_for_each_entry(dev, &parent->devices, bus_list) { - if (PCI_SLOT(dev->devfn) != p_slot->device) - continue; - if (pci_is_bridge(dev)) + for_each_pci_bridge(dev, parent) { + if (PCI_SLOT(dev->devfn) == p_slot->device) pci_hp_add_bridge(dev); } diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index ac41c8be9200..6bacb8995e96 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -113,7 +113,7 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) return dev->sriov->barsz[resno - PCI_IOV_RESOURCES]; } -int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) +int pci_iov_add_virtfn(struct pci_dev *dev, int id) { int i; int rc = -ENOMEM; @@ -134,7 +134,7 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) virtfn->devfn = pci_iov_virtfn_devfn(dev, id); virtfn->vendor = dev->vendor; - pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); + virtfn->device = iov->vf_device; rc = pci_setup_device(virtfn); if (rc) goto failed0; @@ -157,12 +157,8 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) BUG_ON(rc); } - if (reset) - __pci_reset_function(virtfn); - pci_device_add(virtfn, virtfn->bus); - pci_bus_add_device(virtfn); sprintf(buf, "virtfn%u", id); rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); if (rc) @@ -173,6 +169,8 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); + pci_bus_add_device(virtfn); + return 0; failed2: @@ -187,7 +185,7 @@ failed: return rc; } -void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset) +void pci_iov_remove_virtfn(struct pci_dev *dev, int id) { char buf[VIRTFN_ID_LEN]; struct pci_dev *virtfn; @@ -198,11 +196,6 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset) if (!virtfn) return; - if (reset) { - device_release_driver(&virtfn->dev); - __pci_reset_function(virtfn); - } - sprintf(buf, "virtfn%u", id); sysfs_remove_link(&dev->dev.kobj, buf); /* @@ -317,7 +310,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) pci_cfg_access_unlock(dev); for (i = 0; i < initial; i++) { - rc = pci_iov_add_virtfn(dev, i, 0); + rc = pci_iov_add_virtfn(dev, i); if (rc) goto failed; } @@ -329,7 +322,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) failed: while (i--) - pci_iov_remove_virtfn(dev, i, 0); + pci_iov_remove_virtfn(dev, i); err_pcibios: iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); @@ -356,7 +349,7 @@ static void sriov_disable(struct pci_dev *dev) return; for (i = 0; i < iov->num_VFs; i++) - pci_iov_remove_virtfn(dev, i, 0); + pci_iov_remove_virtfn(dev, i); iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); pci_cfg_access_lock(dev); @@ -449,6 +442,7 @@ found: iov->nres = nres; iov->ctrl = ctrl; iov->total_VFs = total; + pci_read_config_word(dev, pos + PCI_SRIOV_VF_DID, &iov->vf_device); iov->pgsz = pgsz; iov->self = dev; iov->drivers_autoprobe = true; @@ -504,6 +498,14 @@ static void sriov_restore_state(struct pci_dev *dev) if (ctrl & PCI_SRIOV_CTRL_VFE) return; + /* + * Restore PCI_SRIOV_CTRL_ARI before pci_iov_set_numvfs() because + * it reads offset & stride, which depend on PCI_SRIOV_CTRL_ARI. + */ + ctrl &= ~PCI_SRIOV_CTRL_ARI; + ctrl |= iov->ctrl & PCI_SRIOV_CTRL_ARI; + pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, ctrl); + for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) pci_update_resource(dev, i); @@ -724,7 +726,7 @@ int pci_vfs_assigned(struct pci_dev *dev) * determine the device ID for the VFs, the vendor ID will be the * same as the PF so there is no need to check for that one */ - pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id); + dev_id = dev->sriov->vf_device; /* loop through all the VFs to see if we own any that are assigned */ vfdev = pci_get_device(dev->vendor, dev_id, NULL); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index a8da543b3814..4708eb9df71b 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -624,7 +624,7 @@ void acpi_pci_add_bus(struct pci_bus *bus) union acpi_object *obj; struct pci_host_bridge *bridge; - if (acpi_pci_disabled || !bus->bridge) + if (acpi_pci_disabled || !bus->bridge || !ACPI_HANDLE(bus->bridge)) return; acpi_pci_slot_enumerate(bus); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 8e075ea2743e..720090967444 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -648,6 +648,33 @@ exit: return count; } +static ssize_t sriov_offset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%u\n", pdev->sriov->offset); +} + +static ssize_t sriov_stride_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%u\n", pdev->sriov->stride); +} + +static ssize_t sriov_vf_device_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%x\n", pdev->sriov->vf_device); +} + static ssize_t sriov_drivers_autoprobe_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -676,6 +703,9 @@ static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs); static struct device_attribute sriov_numvfs_attr = __ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP), sriov_numvfs_show, sriov_numvfs_store); +static struct device_attribute sriov_offset_attr = __ATTR_RO(sriov_offset); +static struct device_attribute sriov_stride_attr = __ATTR_RO(sriov_stride); +static struct device_attribute sriov_vf_device_attr = __ATTR_RO(sriov_vf_device); static struct device_attribute sriov_drivers_autoprobe_attr = __ATTR(sriov_drivers_autoprobe, (S_IRUGO|S_IWUSR|S_IWGRP), sriov_drivers_autoprobe_show, sriov_drivers_autoprobe_store); @@ -1748,6 +1778,9 @@ static const struct attribute_group pci_dev_hp_attr_group = { static struct attribute *sriov_dev_attrs[] = { &sriov_totalvfs_attr.attr, &sriov_numvfs_attr.attr, + &sriov_offset_attr.attr, + &sriov_stride_attr.attr, + &sriov_vf_device_attr.attr, &sriov_drivers_autoprobe_attr.attr, NULL, }; @@ -1795,6 +1828,6 @@ static const struct attribute_group *pci_dev_attr_groups[] = { NULL, }; -struct device_type pci_dev_type = { +const struct device_type pci_dev_type = { .groups = pci_dev_attr_groups, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6078dfc11b11..50f148f154ab 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2966,6 +2966,107 @@ bool pci_acs_path_enabled(struct pci_dev *start, } /** + * pci_rebar_find_pos - find position of resize ctrl reg for BAR + * @pdev: PCI device + * @bar: BAR to find + * + * Helper to find the position of the ctrl register for a BAR. + * Returns -ENOTSUPP if resizable BARs are not supported at all. + * Returns -ENOENT if no ctrl register for the BAR could be found. + */ +static int pci_rebar_find_pos(struct pci_dev *pdev, int bar) +{ + unsigned int pos, nbars, i; + u32 ctrl; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); + if (!pos) + return -ENOTSUPP; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> + PCI_REBAR_CTRL_NBAR_SHIFT; + + for (i = 0; i < nbars; i++, pos += 8) { + int bar_idx; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; + if (bar_idx == bar) + return pos; + } + + return -ENOENT; +} + +/** + * pci_rebar_get_possible_sizes - get possible sizes for BAR + * @pdev: PCI device + * @bar: BAR to query + * + * Get the possible sizes of a resizable BAR as bitmask defined in the spec + * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable. + */ +u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar) +{ + int pos; + u32 cap; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return 0; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap); + return (cap & PCI_REBAR_CAP_SIZES) >> 4; +} + +/** + * pci_rebar_get_current_size - get the current size of a BAR + * @pdev: PCI device + * @bar: BAR to set size to + * + * Read the size of a BAR from the resizable BAR config. + * Returns size if found or negative error code. + */ +int pci_rebar_get_current_size(struct pci_dev *pdev, int bar) +{ + int pos; + u32 ctrl; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return pos; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> 8; +} + +/** + * pci_rebar_set_size - set a new size for a BAR + * @pdev: PCI device + * @bar: BAR to set size to + * @size: new size as defined in the spec (0=1MB, 19=512GB) + * + * Set the new size of a BAR as defined in the spec. + * Returns zero if resizing was successful, error code otherwise. + */ +int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) +{ + int pos; + u32 ctrl; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return pos; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; + ctrl |= size << 8; + pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); + return 0; +} + +/** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) @@ -3471,7 +3572,7 @@ EXPORT_SYMBOL(devm_pci_remap_cfgspace); * All operations are managed and will be undone on driver detach. * * Returns a pointer to the remapped memory or an ERR_PTR() encoded error code - * on failure. Usage example: + * on failure. Usage example:: * * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); * base = devm_pci_remap_cfg_resource(&pdev->dev, res); @@ -4146,35 +4247,6 @@ static void pci_dev_restore(struct pci_dev *dev) } /** - * __pci_reset_function - reset a PCI device function - * @dev: PCI device to reset - * - * Some devices allow an individual function to be reset without affecting - * other functions in the same device. The PCI device must be responsive - * to PCI config space in order to use this function. - * - * The device function is presumed to be unused when this function is called. - * Resetting the device will make the contents of PCI configuration space - * random, so any caller of this must be prepared to reinitialise the - * device including MSI, bus mastering, BARs, decoding IO and memory spaces, - * etc. - * - * Returns 0 if the device function was successfully reset or negative if the - * device doesn't support resetting a single function. - */ -int __pci_reset_function(struct pci_dev *dev) -{ - int ret; - - pci_dev_lock(dev); - ret = __pci_reset_function_locked(dev); - pci_dev_unlock(dev); - - return ret; -} -EXPORT_SYMBOL_GPL(__pci_reset_function); - -/** * __pci_reset_function_locked - reset a PCI device function while holding * the @dev mutex lock. * @dev: PCI device to reset @@ -4199,6 +4271,14 @@ int __pci_reset_function_locked(struct pci_dev *dev) might_sleep(); + /* + * A reset method returns -ENOTTY if it doesn't support this device + * and we should try the next method. + * + * If it returns 0 (success), we're finished. If it returns any + * other error, we're also finished: this indicates that further + * reset mechanisms might be broken on the device. + */ rc = pci_dev_specific_reset(dev, 0); if (rc != -ENOTTY) return rc; @@ -4264,8 +4344,8 @@ int pci_probe_reset_function(struct pci_dev *dev) * * This function does not just reset the PCI portion of a device, but * clears all the state associated with the device. This function differs - * from __pci_reset_function in that it saves and restores device state - * over the reset. + * from __pci_reset_function_locked() in that it saves and restores device state + * over the reset and takes the PCI device lock. * * Returns 0 if the device function was successfully reset or negative if the * device doesn't support resetting a single function. @@ -4300,7 +4380,7 @@ EXPORT_SYMBOL_GPL(pci_reset_function); * * This function does not just reset the PCI portion of a device, but * clears all the state associated with the device. This function differs - * from __pci_reset_function() in that it saves and restores device state + * from __pci_reset_function_locked() in that it saves and restores device state * over the reset. It also differs from pci_reset_function() in that it * requires the PCI device lock to be held. * diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index a6560c9baa52..39eaacd122d8 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -192,7 +192,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev) } extern const struct attribute_group *pci_dev_groups[]; extern const struct attribute_group *pcibus_groups[]; -extern struct device_type pci_dev_type; +extern const struct device_type pci_dev_type; extern const struct attribute_group *pci_bus_groups[]; @@ -263,6 +263,7 @@ struct pci_sriov { u16 num_VFs; /* number of VFs available */ u16 offset; /* first VF Routing ID offset */ u16 stride; /* following VF stride */ + u16 vf_device; /* VF device ID */ u32 pgsz; /* page size for BAR alignment */ u8 link; /* Function Dependency Link */ u8 max_VF_buses; /* max buses consumed by VFs */ @@ -366,4 +367,12 @@ int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment, struct resource *res); #endif +u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar); +int pci_rebar_get_current_size(struct pci_dev *pdev, int bar); +int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size); +static inline u64 pci_rebar_size_to_bytes(int size) +{ + return 1ULL << (size + 20); +} + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 890efcc574cb..744805232155 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -390,7 +390,14 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, * If the error is reported by an end point, we think this * error is related to the upstream link of the end point. */ - pci_walk_bus(dev->bus, cb, &result_data); + if (state == pci_channel_io_normal) + /* + * the error is non fatal so the bus is ok, just invoke + * the callback for the function that logged the error. + */ + cb(dev, &result_data); + else + pci_walk_bus(dev->bus, cb, &result_data); } return result_data.result; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 1dfa10cc566b..d240ffab24c1 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -450,24 +450,25 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link, if (!(link->aspm_support & ASPM_STATE_L1_2_MASK)) return; - /* Choose the greater of the two T_cmn_mode_rstr_time */ - val1 = (upreg->l1ss_cap >> 8) & 0xFF; - val2 = (upreg->l1ss_cap >> 8) & 0xFF; + /* Choose the greater of the two Port Common_Mode_Restore_Times */ + val1 = (upreg->l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8; + val2 = (dwreg->l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8; if (val1 > val2) link->l1ss.ctl1 |= val1 << 8; else link->l1ss.ctl1 |= val2 << 8; + /* * We currently use LTR L1.2 threshold to be fixed constant picked from * Intel's coreboot. */ link->l1ss.ctl1 |= LTR_L1_2_THRESHOLD_BITS; - /* Choose the greater of the two T_pwr_on */ - val1 = (upreg->l1ss_cap >> 19) & 0x1F; - scale1 = (upreg->l1ss_cap >> 16) & 0x03; - val2 = (dwreg->l1ss_cap >> 19) & 0x1F; - scale2 = (dwreg->l1ss_cap >> 16) & 0x03; + /* Choose the greater of the two Port T_POWER_ON times */ + val1 = (upreg->l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19; + scale1 = (upreg->l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16; + val2 = (dwreg->l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19; + scale2 = (dwreg->l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16; if (calc_l1ss_pwron(link->pdev, scale1, val1) > calc_l1ss_pwron(link->downstream, scale2, val2)) @@ -646,21 +647,26 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) if (enable_req & ASPM_STATE_L1_2_MASK) { - /* Program T_pwr_on in both ports */ + /* Program T_POWER_ON times in both ports */ pci_write_config_dword(parent, up_cap_ptr + PCI_L1SS_CTL2, link->l1ss.ctl2); pci_write_config_dword(child, dw_cap_ptr + PCI_L1SS_CTL2, link->l1ss.ctl2); - /* Program T_cmn_mode in parent */ + /* Program Common_Mode_Restore_Time in upstream device */ pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1, - 0xFF00, link->l1ss.ctl1); - - /* Program LTR L1.2 threshold in both ports */ - pci_clear_and_set_dword(parent, dw_cap_ptr + PCI_L1SS_CTL1, - 0xE3FF0000, link->l1ss.ctl1); + PCI_L1SS_CTL1_CM_RESTORE_TIME, + link->l1ss.ctl1); + + /* Program LTR_L1.2_THRESHOLD time in both ports */ + pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_LTR_L12_TH_VALUE | + PCI_L1SS_CTL1_LTR_L12_TH_SCALE, + link->l1ss.ctl1); pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1, - 0xE3FF0000, link->l1ss.ctl1); + PCI_L1SS_CTL1_LTR_L12_TH_VALUE | + PCI_L1SS_CTL1_LTR_L12_TH_SCALE, + link->l1ss.ctl1); } val = 0; @@ -802,10 +808,14 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) /* * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe - * hierarchies. + * hierarchies. Note that some PCIe host implementations omit + * the root ports entirely, in which case a downstream port on + * a switch may become the root of the link state chain for all + * its subordinate endpoints. */ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT || - pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) { + pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE || + !pdev->bus->parent->self) { link->root = link; } else { struct pcie_link_state *parent; diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index fafdb165dd2e..df290aa58dce 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -226,6 +226,9 @@ static void pcie_pme_work_fn(struct work_struct *work) break; pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta); + if (rtsta == (u32) ~0) + break; + if (rtsta & PCI_EXP_RTSTA_PME) { /* * Clear PME status of the port. If there are other @@ -273,7 +276,7 @@ static irqreturn_t pcie_pme_irq(int irq, void *context) spin_lock_irqsave(&data->lock, flags); pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta); - if (!(rtsta & PCI_EXP_RTSTA_PME)) { + if (rtsta == (u32) ~0 || !(rtsta & PCI_EXP_RTSTA_PME)) { spin_unlock_irqrestore(&data->lock, flags); return IRQ_NONE; } diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 313a21df1692..3cd5eb48644a 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -43,134 +43,113 @@ static void release_pcie_device(struct device *dev) kfree(to_pcie_device(dev)); } -/** - * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode - * for given port - * @dev: PCI Express port to handle - * @irqs: Array of interrupt vectors to populate - * @mask: Bitmask of port capabilities returned by get_port_device_capability() - * - * Return value: 0 on success, error code on failure +/* + * Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if + * services are enabled in "mask". Return the number of MSI/MSI-X vectors + * required to accommodate the largest Message Number. */ -static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) +static int pcie_message_numbers(struct pci_dev *dev, int mask, + u32 *pme, u32 *aer, u32 *dpc) { - int nr_entries, entry, nvec = 0; + u32 nvec = 0, pos, reg32; + u16 reg16; /* - * Allocate as many entries as the port wants, so that we can check - * which of them will be useful. Moreover, if nr_entries is correctly - * equal to the number of entries this port actually uses, we'll happily - * go through without any tricks. + * The Interrupt Message Number indicates which vector is used, i.e., + * the MSI-X table entry or the MSI offset between the base Message + * Data and the generated interrupt message. See PCIe r3.1, sec + * 7.8.2, 7.10.10, 7.31.2. */ - nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES, - PCI_IRQ_MSIX | PCI_IRQ_MSI); - if (nr_entries < 0) - return nr_entries; if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) { - u16 reg16; - - /* - * Per PCIe r3.1, sec 6.1.6, "PME and Hot-Plug Event - * interrupts (when both are implemented) always share the - * same MSI or MSI-X vector, as indicated by the Interrupt - * Message Number field in the PCI Express Capabilities - * register". - * - * Per sec 7.8.2, "For MSI, the [Interrupt Message Number] - * indicates the offset between the base Message Data and - * the interrupt message that is generated." - * - * "For MSI-X, the [Interrupt Message Number] indicates - * which MSI-X Table entry is used to generate the - * interrupt message." - */ pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16); - entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9; - if (entry >= nr_entries) - goto out_free_irqs; - - irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, entry); - irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, entry); - - nvec = max(nvec, entry + 1); + *pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9; + nvec = *pme + 1; } if (mask & PCIE_PORT_SERVICE_AER) { - u32 reg32, pos; - - /* - * Per PCIe r3.1, sec 7.10.10, the Advanced Error Interrupt - * Message Number in the Root Error Status register - * indicates which MSI/MSI-X vector is used for AER. - * - * "For MSI, the [Advanced Error Interrupt Message Number] - * indicates the offset between the base Message Data and - * the interrupt message that is generated." - * - * "For MSI-X, the [Advanced Error Interrupt Message - * Number] indicates which MSI-X Table entry is used to - * generate the interrupt message." - */ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); - pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); - entry = reg32 >> 27; - if (entry >= nr_entries) - goto out_free_irqs; - - irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry); - - nvec = max(nvec, entry + 1); + if (pos) { + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, + ®32); + *aer = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27; + nvec = max(nvec, *aer + 1); + } } if (mask & PCIE_PORT_SERVICE_DPC) { - u16 reg16, pos; - - /* - * Per PCIe r4.0 (v0.9), sec 7.9.15.2, the DPC Interrupt - * Message Number in the DPC Capability register indicates - * which MSI/MSI-X vector is used for DPC. - * - * "For MSI, the [DPC Interrupt Message Number] indicates - * the offset between the base Message Data and the - * interrupt message that is generated." - * - * "For MSI-X, the [DPC Interrupt Message Number] indicates - * which MSI-X Table entry is used to generate the - * interrupt message." - */ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC); - pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, ®16); - entry = reg16 & 0x1f; - if (entry >= nr_entries) - goto out_free_irqs; + if (pos) { + pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, + ®16); + *dpc = reg16 & PCI_EXP_DPC_IRQ; + nvec = max(nvec, *dpc + 1); + } + } + + return nvec; +} - irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, entry); +/** + * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode + * for given port + * @dev: PCI Express port to handle + * @irqs: Array of interrupt vectors to populate + * @mask: Bitmask of port capabilities returned by get_port_device_capability() + * + * Return value: 0 on success, error code on failure + */ +static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) +{ + int nr_entries, nvec; + u32 pme = 0, aer = 0, dpc = 0; - nvec = max(nvec, entry + 1); + /* Allocate the maximum possible number of MSI/MSI-X vectors */ + nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES, + PCI_IRQ_MSIX | PCI_IRQ_MSI); + if (nr_entries < 0) + return nr_entries; + + /* See how many and which Interrupt Message Numbers we actually use */ + nvec = pcie_message_numbers(dev, mask, &pme, &aer, &dpc); + if (nvec > nr_entries) { + pci_free_irq_vectors(dev); + return -EIO; } /* - * If nvec is equal to the allocated number of entries, we can just use - * what we have. Otherwise, the port has some extra entries not for the - * services we know and we need to work around that. + * If we allocated more than we need, free them and reallocate fewer. + * + * Reallocating may change the specific vectors we get, so + * pci_irq_vector() must be done *after* the reallocation. + * + * If we're using MSI, hardware is *allowed* to change the Interrupt + * Message Numbers when we free and reallocate the vectors, but we + * assume it won't because we allocate enough vectors for the + * biggest Message Number we found. */ if (nvec != nr_entries) { - /* Drop the temporary MSI-X setup */ pci_free_irq_vectors(dev); - /* Now allocate the MSI-X vectors for real */ nr_entries = pci_alloc_irq_vectors(dev, nvec, nvec, PCI_IRQ_MSIX | PCI_IRQ_MSI); if (nr_entries < 0) return nr_entries; } - return 0; + /* PME and hotplug share an MSI/MSI-X vector */ + if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) { + irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme); + irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme); + } -out_free_irqs: - pci_free_irq_vectors(dev); - return -EIO; + if (mask & PCIE_PORT_SERVICE_AER) + irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, aer); + + if (mask & PCIE_PORT_SERVICE_DPC) + irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, dpc); + + return 0; } /** diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 083276e03c38..b350d12b39e3 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -246,6 +246,7 @@ static struct pci_driver pcie_portdriver = { .probe = pcie_portdrv_probe, .remove = pcie_portdrv_remove, + .shutdown = pcie_portdrv_remove, .err_handler = &pcie_portdrv_err_handler, diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ff94b69738a8..14e0ea1ff38b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -959,7 +959,21 @@ static void pci_enable_crs(struct pci_dev *pdev) PCI_EXP_RTCTL_CRSSVE); } +static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, + unsigned int available_buses); + /* + * pci_scan_bridge_extend() - Scan buses behind a bridge + * @bus: Parent bus the bridge is on + * @dev: Bridge itself + * @max: Starting subordinate number of buses behind this bridge + * @available_buses: Total number of buses available for this bridge and + * the devices below. After the minimal bus space has + * been allocated the remaining buses will be + * distributed equally between hotplug-capable bridges. + * @pass: Either %0 (scan already configured bridges) or %1 (scan bridges + * that need to be reconfigured. + * * If it's a bridge, configure it and scan the bus behind it. * For CardBus bridges, we don't scan behind as the devices will * be handled by the bridge driver itself. @@ -969,7 +983,9 @@ static void pci_enable_crs(struct pci_dev *pdev) * them, we proceed to assigning numbers to the remaining buses in * order to avoid overlaps between old and new bus numbers. */ -int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) +static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, + int max, unsigned int available_buses, + int pass) { struct pci_bus *child; int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); @@ -1076,9 +1092,13 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) child = pci_add_new_bus(bus, dev, max+1); if (!child) goto out; - pci_bus_insert_busn_res(child, max+1, 0xff); + pci_bus_insert_busn_res(child, max+1, + bus->busn_res.end); } max++; + if (available_buses) + available_buses--; + buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) | ((unsigned int)(child->busn_res.start) << 8) @@ -1100,7 +1120,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) if (!is_cardbus) { child->bridge_ctl = bctl; - max = pci_scan_child_bus(child); + max = pci_scan_child_bus_extend(child, available_buses); } else { /* * For CardBus bridges, we leave 4 bus numbers @@ -1168,6 +1188,28 @@ out: return max; } + +/* + * pci_scan_bridge() - Scan buses behind a bridge + * @bus: Parent bus the bridge is on + * @dev: Bridge itself + * @max: Starting subordinate number of buses behind this bridge + * @pass: Either %0 (scan already configured bridges) or %1 (scan bridges + * that need to be reconfigured. + * + * If it's a bridge, configure it and scan the bus behind it. + * For CardBus bridges, we don't scan behind as the devices will + * be handled by the bridge driver itself. + * + * We need to process bridges in two passes -- first we scan those + * already configured by the BIOS and after we are done with all of + * them, we proceed to assigning numbers to the remaining buses in + * order to avoid overlaps between old and new bus numbers. + */ +int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) +{ + return pci_scan_bridge_extend(bus, dev, max, 0, pass); +} EXPORT_SYMBOL(pci_scan_bridge); /* @@ -2396,9 +2438,24 @@ void __weak pcibios_fixup_bus(struct pci_bus *bus) /* nothing to do, expected to be removed in the future */ } -unsigned int pci_scan_child_bus(struct pci_bus *bus) +/** + * pci_scan_child_bus_extend() - Scan devices below a bus + * @bus: Bus to scan for devices + * @available_buses: Total number of buses available (%0 does not try to + * extend beyond the minimal) + * + * Scans devices below @bus including subordinate buses. Returns new + * subordinate number including all the found devices. Passing + * @available_buses causes the remaining bus space to be distributed + * equally between hotplug-capable bridges to allow future extension of the + * hierarchy. + */ +static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, + unsigned int available_buses) { - unsigned int devfn, pass, max = bus->busn_res.start; + unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0; + unsigned int start = bus->busn_res.start; + unsigned int devfn, cmax, max = start; struct pci_dev *dev; dev_dbg(&bus->dev, "scanning bus\n"); @@ -2408,7 +2465,8 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) pci_scan_slot(bus, devfn); /* Reserve buses for SR-IOV capability. */ - max += pci_iov_bus_range(bus); + used_buses = pci_iov_bus_range(bus); + max += used_buses; /* * After performing arch-dependent fixup of the bus, look behind @@ -2420,19 +2478,73 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) bus->is_added = 1; } - for (pass = 0; pass < 2; pass++) - list_for_each_entry(dev, &bus->devices, bus_list) { - if (pci_is_bridge(dev)) - max = pci_scan_bridge(bus, dev, max, pass); + /* + * Calculate how many hotplug bridges and normal bridges there + * are on this bus. We will distribute the additional available + * buses between hotplug bridges. + */ + for_each_pci_bridge(dev, bus) { + if (dev->is_hotplug_bridge) + hotplug_bridges++; + else + normal_bridges++; + } + + /* + * Scan bridges that are already configured. We don't touch them + * unless they are misconfigured (which will be done in the second + * scan below). + */ + for_each_pci_bridge(dev, bus) { + cmax = max; + max = pci_scan_bridge_extend(bus, dev, max, 0, 0); + used_buses += cmax - max; + } + + /* Scan bridges that need to be reconfigured */ + for_each_pci_bridge(dev, bus) { + unsigned int buses = 0; + + if (!hotplug_bridges && normal_bridges == 1) { + /* + * There is only one bridge on the bus (upstream + * port) so it gets all available buses which it + * can then distribute to the possible hotplug + * bridges below. + */ + buses = available_buses; + } else if (dev->is_hotplug_bridge) { + /* + * Distribute the extra buses between hotplug + * bridges if any. + */ + buses = available_buses / hotplug_bridges; + buses = min(buses, available_buses - used_buses); } + cmax = max; + max = pci_scan_bridge_extend(bus, dev, cmax, buses, 1); + used_buses += max - cmax; + } + /* * Make sure a hotplug bridge has at least the minimum requested - * number of buses. + * number of buses but allow it to grow up to the maximum available + * bus number of there is room. */ - if (bus->self && bus->self->is_hotplug_bridge && pci_hotplug_bus_size) { - if (max - bus->busn_res.start < pci_hotplug_bus_size - 1) - max = bus->busn_res.start + pci_hotplug_bus_size - 1; + if (bus->self && bus->self->is_hotplug_bridge) { + used_buses = max_t(unsigned int, available_buses, + pci_hotplug_bus_size - 1); + if (max - start < used_buses) { + max = start + used_buses; + + /* Do not allocate more buses than we have room left */ + if (max > bus->busn_res.end) + max = bus->busn_res.end; + + dev_dbg(&bus->dev, "%pR extended by %#02x\n", + &bus->busn_res, max - start); + } } /* @@ -2445,6 +2557,18 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max); return max; } + +/** + * pci_scan_child_bus() - Scan devices below a bus + * @bus: Bus to scan for devices + * + * Scans devices below @bus including subordinate buses. Returns new + * subordinate number including all the found devices. + */ +unsigned int pci_scan_child_bus(struct pci_bus *bus) +{ + return pci_scan_child_bus_extend(bus, 0); +} EXPORT_SYMBOL_GPL(pci_scan_child_bus); /** @@ -2737,3 +2861,38 @@ void __init pci_sort_breadthfirst(void) { bus_sort_breadthfirst(&pci_bus_type, &pci_sort_bf_cmp); } + +int pci_hp_add_bridge(struct pci_dev *dev) +{ + struct pci_bus *parent = dev->bus; + int busnr, start = parent->busn_res.start; + unsigned int available_buses = 0; + int end = parent->busn_res.end; + + for (busnr = start; busnr <= end; busnr++) { + if (!pci_find_bus(pci_domain_nr(parent), busnr)) + break; + } + if (busnr-- > end) { + dev_err(&dev->dev, "No bus number available for hot-added bridge\n"); + return -1; + } + + /* Scan bridges that are already configured */ + busnr = pci_scan_bridge(parent, dev, busnr, 0); + + /* + * Distribute the available bus numbers between hotplug-capable + * bridges to make extending the chain later possible. + */ + available_buses = end - busnr; + + /* Scan bridges that need to be reconfigured */ + pci_scan_bridge_extend(parent, dev, busnr, available_buses, 1); + + if (!dev->subordinate) + return -1; + + return 0; +} +EXPORT_SYMBOL_GPL(pci_hp_add_bridge); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index c1063a420f0c..cbe85e921ede 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4211,17 +4211,32 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags) #endif } +static bool pci_quirk_cavium_acs_match(struct pci_dev *dev) +{ + /* + * Effectively selects all downstream ports for whole ThunderX 1 + * family by 0xf800 mask (which represents 8 SoCs), while the lower + * bits of device ID are used to indicate which subdevice is used + * within the SoC. + */ + return (pci_is_pcie(dev) && + (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) && + ((dev->device & 0xf800) == 0xa000)); +} + static int pci_quirk_cavium_acs(struct pci_dev *dev, u16 acs_flags) { /* - * Cavium devices matching this quirk do not perform peer-to-peer - * with other functions, allowing masking out these bits as if they - * were unimplemented in the ACS capability. + * Cavium root ports don't advertise an ACS capability. However, + * the RTL internally implements similar protection as if ACS had + * Request Redirection, Completion Redirection, Source Validation, + * and Upstream Forwarding features enabled. Assert that the + * hardware implements and enables equivalent ACS functionality for + * these flags. */ - acs_flags &= ~(PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR | - PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT); + acs_flags &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_SV | PCI_ACS_UF); - if (!((dev->device >= 0xa000) && (dev->device <= 0xa0ff))) + if (!pci_quirk_cavium_acs_match(dev)) return -ENOTTY; return acs_flags ? 0 : 1; diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 73a03d382590..2fa0dbde36b7 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -19,9 +19,9 @@ static void pci_stop_dev(struct pci_dev *dev) pci_pme_active(dev, false); if (dev->is_added) { + device_release_driver(&dev->dev); pci_proc_detach_device(dev); pci_remove_sysfs_dev_files(dev); - device_release_driver(&dev->dev); dev->is_added = 0; } diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index b6edb187d160..1f5e6af96c83 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -147,12 +147,8 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) return NULL; rom = ioremap(start, *size); - if (!rom) { - /* restore enable if ioremap fails */ - if (!(res->flags & IORESOURCE_ROM_ENABLE)) - pci_disable_rom(pdev); - return NULL; - } + if (!rom) + goto err_ioremap; /* * Try to find the true size of the ROM since sometimes the PCI window @@ -160,7 +156,18 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) * True size is important if the ROM is going to be copied. */ *size = pci_get_rom_size(pdev, rom, *size); + if (!*size) + goto invalid_rom; + return rom; + +invalid_rom: + iounmap(rom); +err_ioremap: + /* restore enable if ioremap fails */ + if (!(res->flags & IORESOURCE_ROM_ENABLE)) + pci_disable_rom(pdev); + return NULL; } EXPORT_SYMBOL(pci_map_rom); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 958da7db9033..b1ad466199ad 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1518,13 +1518,16 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge, break; } } + +#define PCI_RES_TYPE_MASK \ + (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\ + IORESOURCE_MEM_64) + static void pci_bridge_release_resources(struct pci_bus *bus, unsigned long type) { struct pci_dev *dev = bus->self; struct resource *r; - unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH | IORESOURCE_MEM_64; unsigned old_flags = 0; struct resource *b_res; int idx = 1; @@ -1567,7 +1570,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus, */ release_child_resources(r); if (!release_resource(r)) { - type = old_flags = r->flags & type_mask; + type = old_flags = r->flags & PCI_RES_TYPE_MASK; dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n", PCI_BRIDGE_RESOURCES + idx, r); /* keep the old size */ @@ -1758,8 +1761,6 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) enum release_type rel_type = leaf_only; LIST_HEAD(fail_head); struct pci_dev_resource *fail_res; - unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH | IORESOURCE_MEM_64; int pci_try_num = 1; enum enable_type enable_local; @@ -1818,7 +1819,7 @@ again: */ list_for_each_entry(fail_res, &fail_head, list) pci_bus_release_bridge_resources(fail_res->dev->bus, - fail_res->flags & type_mask, + fail_res->flags & PCI_RES_TYPE_MASK, rel_type); /* restore size and flags */ @@ -1853,6 +1854,175 @@ void __init pci_assign_unassigned_resources(void) } } +static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, + struct list_head *add_list, resource_size_t available) +{ + struct pci_dev_resource *dev_res; + + if (res->parent) + return; + + if (resource_size(res) >= available) + return; + + dev_res = res_to_dev_res(add_list, res); + if (!dev_res) + return; + + /* Is there room to extend the window? */ + if (available - resource_size(res) <= dev_res->add_size) + return; + + dev_res->add_size = available - resource_size(res); + dev_dbg(&bridge->dev, "bridge window %pR extended by %pa\n", res, + &dev_res->add_size); +} + +static void pci_bus_distribute_available_resources(struct pci_bus *bus, + struct list_head *add_list, resource_size_t available_io, + resource_size_t available_mmio, resource_size_t available_mmio_pref) +{ + resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref; + unsigned int normal_bridges = 0, hotplug_bridges = 0; + struct resource *io_res, *mmio_res, *mmio_pref_res; + struct pci_dev *dev, *bridge = bus->self; + + io_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; + mmio_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; + mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; + + /* + * Update additional resource list (add_list) to fill all the + * extra resource space available for this port except the space + * calculated in __pci_bus_size_bridges() which covers all the + * devices currently connected to the port and below. + */ + extend_bridge_window(bridge, io_res, add_list, available_io); + extend_bridge_window(bridge, mmio_res, add_list, available_mmio); + extend_bridge_window(bridge, mmio_pref_res, add_list, + available_mmio_pref); + + /* + * Calculate the total amount of extra resource space we can + * pass to bridges below this one. This is basically the + * extra space reduced by the minimal required space for the + * non-hotplug bridges. + */ + remaining_io = available_io; + remaining_mmio = available_mmio; + remaining_mmio_pref = available_mmio_pref; + + /* + * Calculate how many hotplug bridges and normal bridges there + * are on this bus. We will distribute the additional available + * resources between hotplug bridges. + */ + for_each_pci_bridge(dev, bus) { + if (dev->is_hotplug_bridge) + hotplug_bridges++; + else + normal_bridges++; + } + + for_each_pci_bridge(dev, bus) { + const struct resource *res; + + if (dev->is_hotplug_bridge) + continue; + + /* + * Reduce the available resource space by what the + * bridge and devices below it occupy. + */ + res = &dev->resource[PCI_BRIDGE_RESOURCES + 0]; + if (!res->parent && available_io > resource_size(res)) + remaining_io -= resource_size(res); + + res = &dev->resource[PCI_BRIDGE_RESOURCES + 1]; + if (!res->parent && available_mmio > resource_size(res)) + remaining_mmio -= resource_size(res); + + res = &dev->resource[PCI_BRIDGE_RESOURCES + 2]; + if (!res->parent && available_mmio_pref > resource_size(res)) + remaining_mmio_pref -= resource_size(res); + } + + /* + * Go over devices on this bus and distribute the remaining + * resource space between hotplug bridges. + */ + for_each_pci_bridge(dev, bus) { + struct pci_bus *b; + + b = dev->subordinate; + if (!b) + continue; + + if (!hotplug_bridges && normal_bridges == 1) { + /* + * There is only one bridge on the bus (upstream + * port) so it gets all available resources + * which it can then distribute to the possible + * hotplug bridges below. + */ + pci_bus_distribute_available_resources(b, add_list, + available_io, available_mmio, + available_mmio_pref); + } else if (dev->is_hotplug_bridge) { + resource_size_t align, io, mmio, mmio_pref; + + /* + * Distribute available extra resources equally + * between hotplug-capable downstream ports + * taking alignment into account. + * + * Here hotplug_bridges is always != 0. + */ + align = pci_resource_alignment(bridge, io_res); + io = div64_ul(available_io, hotplug_bridges); + io = min(ALIGN(io, align), remaining_io); + remaining_io -= io; + + align = pci_resource_alignment(bridge, mmio_res); + mmio = div64_ul(available_mmio, hotplug_bridges); + mmio = min(ALIGN(mmio, align), remaining_mmio); + remaining_mmio -= mmio; + + align = pci_resource_alignment(bridge, mmio_pref_res); + mmio_pref = div64_ul(available_mmio_pref, + hotplug_bridges); + mmio_pref = min(ALIGN(mmio_pref, align), + remaining_mmio_pref); + remaining_mmio_pref -= mmio_pref; + + pci_bus_distribute_available_resources(b, add_list, io, + mmio, mmio_pref); + } + } +} + +static void +pci_bridge_distribute_available_resources(struct pci_dev *bridge, + struct list_head *add_list) +{ + resource_size_t available_io, available_mmio, available_mmio_pref; + const struct resource *res; + + if (!bridge->is_hotplug_bridge) + return; + + /* Take the initial extra resources from the hotplug port */ + res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; + available_io = resource_size(res); + res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; + available_mmio = resource_size(res); + res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; + available_mmio_pref = resource_size(res); + + pci_bus_distribute_available_resources(bridge->subordinate, + add_list, available_io, available_mmio, available_mmio_pref); +} + void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) { struct pci_bus *parent = bridge->subordinate; @@ -1862,11 +2032,17 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) LIST_HEAD(fail_head); struct pci_dev_resource *fail_res; int retval; - unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH | IORESOURCE_MEM_64; again: __pci_bus_size_bridges(parent, &add_list); + + /* + * Distribute remaining resources (if any) equally between + * hotplug bridges below. This makes it possible to extend the + * hierarchy later without running out of resources. + */ + pci_bridge_distribute_available_resources(bridge, &add_list); + __pci_bridge_assign_resources(bridge, &add_list, &fail_head); BUG_ON(!list_empty(&add_list)); tried_times++; @@ -1889,7 +2065,7 @@ again: */ list_for_each_entry(fail_res, &fail_head, list) pci_bus_release_bridge_resources(fail_res->dev->bus, - fail_res->flags & type_mask, + fail_res->flags & PCI_RES_TYPE_MASK, whole_subtree); /* restore size and flags */ @@ -1914,6 +2090,104 @@ enable_all: } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); +int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) +{ + struct pci_dev_resource *dev_res; + struct pci_dev *next; + LIST_HEAD(saved); + LIST_HEAD(added); + LIST_HEAD(failed); + unsigned int i; + int ret; + + /* Walk to the root hub, releasing bridge BARs when possible */ + next = bridge; + do { + bridge = next; + for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END; + i++) { + struct resource *res = &bridge->resource[i]; + + if ((res->flags ^ type) & PCI_RES_TYPE_MASK) + continue; + + /* Ignore BARs which are still in use */ + if (res->child) + continue; + + ret = add_to_list(&saved, bridge, res, 0, 0); + if (ret) + goto cleanup; + + dev_info(&bridge->dev, "BAR %d: releasing %pR\n", + i, res); + + if (res->parent) + release_resource(res); + res->start = 0; + res->end = 0; + break; + } + if (i == PCI_BRIDGE_RESOURCE_END) + break; + + next = bridge->bus ? bridge->bus->self : NULL; + } while (next); + + if (list_empty(&saved)) + return -ENOENT; + + __pci_bus_size_bridges(bridge->subordinate, &added); + __pci_bridge_assign_resources(bridge, &added, &failed); + BUG_ON(!list_empty(&added)); + + if (!list_empty(&failed)) { + ret = -ENOSPC; + goto cleanup; + } + + list_for_each_entry(dev_res, &saved, list) { + /* Skip the bridge we just assigned resources for. */ + if (bridge == dev_res->dev) + continue; + + bridge = dev_res->dev; + pci_setup_bridge(bridge->subordinate); + } + + free_list(&saved); + return 0; + +cleanup: + /* restore size and flags */ + list_for_each_entry(dev_res, &failed, list) { + struct resource *res = dev_res->res; + + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; + } + free_list(&failed); + + /* Revert to the old configuration */ + list_for_each_entry(dev_res, &saved, list) { + struct resource *res = dev_res->res; + + bridge = dev_res->dev; + i = res - bridge->resource; + + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; + + pci_claim_resource(bridge, i); + pci_setup_bridge(bridge->subordinate); + } + free_list(&saved); + + return ret; +} + void pci_assign_unassigned_bus_resources(struct pci_bus *bus) { struct pci_dev *dev; @@ -1921,10 +2195,9 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) want additional resources */ down_read(&pci_bus_sem); - list_for_each_entry(dev, &bus->devices, bus_list) - if (pci_is_bridge(dev) && pci_has_subordinate(dev)) - __pci_bus_size_bridges(dev->subordinate, - &add_list); + for_each_pci_bridge(dev, bus) + if (pci_has_subordinate(dev)) + __pci_bus_size_bridges(dev->subordinate, &add_list); up_read(&pci_bus_sem); __pci_bus_assign_resources(bus, &add_list, NULL); BUG_ON(!list_empty(&add_list)); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index e576e1a8d978..bf0089ef2177 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -396,6 +396,64 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz return 0; } +void pci_release_resource(struct pci_dev *dev, int resno) +{ + struct resource *res = dev->resource + resno; + + dev_info(&dev->dev, "BAR %d: releasing %pR\n", resno, res); + release_resource(res); + res->end = resource_size(res) - 1; + res->start = 0; + res->flags |= IORESOURCE_UNSET; +} +EXPORT_SYMBOL(pci_release_resource); + +int pci_resize_resource(struct pci_dev *dev, int resno, int size) +{ + struct resource *res = dev->resource + resno; + int old, ret; + u32 sizes; + u16 cmd; + + /* Make sure the resource isn't assigned before resizing it. */ + if (!(res->flags & IORESOURCE_UNSET)) + return -EBUSY; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_MEMORY) + return -EBUSY; + + sizes = pci_rebar_get_possible_sizes(dev, resno); + if (!sizes) + return -ENOTSUPP; + + if (!(sizes & BIT(size))) + return -EINVAL; + + old = pci_rebar_get_current_size(dev, resno); + if (old < 0) + return old; + + ret = pci_rebar_set_size(dev, resno, size); + if (ret) + return ret; + + res->end = res->start + pci_rebar_size_to_bytes(size) - 1; + + /* Check if the new config works by trying to assign everything. */ + ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); + if (ret) + goto error_resize; + + return 0; + +error_resize: + pci_rebar_set_size(dev, resno, old); + res->end = res->start + pci_rebar_size_to_bytes(old) - 1; + return ret; +} +EXPORT_SYMBOL(pci_resize_resource); + int pci_enable_resources(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index af81b2dec42e..da45dbea20ce 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -943,7 +943,7 @@ static u32 __iomem *pff_ev_reg(struct switchtec_dev *stdev, #define EV_PAR(i, r)[i] = {offsetof(struct part_cfg_regs, r), part_ev_reg} #define EV_PFF(i, r)[i] = {offsetof(struct pff_csr_regs, r), pff_ev_reg} -const struct event_reg { +static const struct event_reg { size_t offset; u32 __iomem *(*map_reg)(struct switchtec_dev *stdev, size_t offset, int index); diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index 4fe4cc4ae19a..5c0170597037 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -77,9 +77,8 @@ int __ref cb_alloc(struct pcmcia_socket *s) max = bus->busn_res.start; for (pass = 0; pass < 2; pass++) - list_for_each_entry(dev, &bus->devices, bus_list) - if (pci_is_bridge(dev)) - max = pci_scan_bridge(bus, dev, max, pass); + for_each_pci_bridge(dev, bus) + max = pci_scan_bridge(bus, dev, max, pass); /* * Size all resources below the CardBus controller. |