diff options
Diffstat (limited to 'drivers/pci')
45 files changed, 1840 insertions, 726 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 826b5016a101..dfc99b3cb958 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -176,10 +176,7 @@ static void pci_clip_resource_to_region(struct pci_bus *bus, 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 long type_mask, - resource_size_t (*alignf)(void *, - const struct resource *, - resource_size_t, - resource_size_t), + resource_alignf alignf, void *alignf_data, struct pci_bus_region *region) { @@ -250,10 +247,7 @@ static int pci_bus_alloc_from_region(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 long type_mask, - resource_size_t (*alignf)(void *, - const struct resource *, - resource_size_t, - resource_size_t), + resource_alignf alignf, void *alignf_data) { #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index d2d17d37d3e0..4fe3b0cb72ec 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -13,11 +13,11 @@ #include <linux/err.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_pci.h> #include <linux/pci.h> #include <linux/phy/phy.h> @@ -113,9 +113,9 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, writel(value, pcie->base + offset); } -static u64 dra7xx_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) +static u64 dra7xx_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 cpu_addr) { - return pci_addr & DRA7XX_CPU_TO_BUS_ADDR; + return cpu_addr & DRA7XX_CPU_TO_BUS_ADDR; } static int dra7xx_pcie_link_up(struct dw_pcie *pci) @@ -474,7 +474,7 @@ static int dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, return ret; } - dw_pcie_ep_init_notify(ep); + pci_epc_init_notify(ep->epc); return 0; } diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index a33fa98a252e..88d7163d64e7 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -54,43 +54,11 @@ struct exynos_pcie { struct dw_pcie pci; void __iomem *elbi_base; - struct clk *clk; - struct clk *bus_clk; + struct clk_bulk_data *clks; struct phy *phy; struct regulator_bulk_data supplies[2]; }; -static int exynos_pcie_init_clk_resources(struct exynos_pcie *ep) -{ - struct device *dev = ep->pci.dev; - int ret; - - ret = clk_prepare_enable(ep->clk); - if (ret) { - dev_err(dev, "cannot enable pcie rc clock"); - return ret; - } - - ret = clk_prepare_enable(ep->bus_clk); - if (ret) { - dev_err(dev, "cannot enable pcie bus clock"); - goto err_bus_clk; - } - - return 0; - -err_bus_clk: - clk_disable_unprepare(ep->clk); - - return ret; -} - -static void exynos_pcie_deinit_clk_resources(struct exynos_pcie *ep) -{ - clk_disable_unprepare(ep->bus_clk); - clk_disable_unprepare(ep->clk); -} - static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg) { writel(val, base + reg); @@ -332,17 +300,9 @@ static int exynos_pcie_probe(struct platform_device *pdev) if (IS_ERR(ep->elbi_base)) return PTR_ERR(ep->elbi_base); - ep->clk = devm_clk_get(dev, "pcie"); - if (IS_ERR(ep->clk)) { - dev_err(dev, "Failed to get pcie rc clock\n"); - return PTR_ERR(ep->clk); - } - - ep->bus_clk = devm_clk_get(dev, "pcie_bus"); - if (IS_ERR(ep->bus_clk)) { - dev_err(dev, "Failed to get pcie bus clock\n"); - return PTR_ERR(ep->bus_clk); - } + ret = devm_clk_bulk_get_all_enable(dev, &ep->clks); + if (ret < 0) + return ret; ep->supplies[0].supply = "vdd18"; ep->supplies[1].supply = "vdd10"; @@ -351,10 +311,6 @@ static int exynos_pcie_probe(struct platform_device *pdev) if (ret) return ret; - ret = exynos_pcie_init_clk_resources(ep); - if (ret) - return ret; - ret = regulator_bulk_enable(ARRAY_SIZE(ep->supplies), ep->supplies); if (ret) return ret; @@ -369,7 +325,6 @@ static int exynos_pcie_probe(struct platform_device *pdev) fail_probe: phy_exit(ep->phy); - exynos_pcie_deinit_clk_resources(ep); regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies); return ret; @@ -383,7 +338,6 @@ static void exynos_pcie_remove(struct platform_device *pdev) exynos_pcie_assert_core_reset(ep); phy_power_off(ep->phy); phy_exit(ep->phy); - exynos_pcie_deinit_clk_resources(ep); regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies); } diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 917c69edee1d..964d67756eb2 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -11,14 +11,13 @@ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/mfd/syscon.h> #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> #include <linux/mfd/syscon/imx7-iomuxc-gpr.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_address.h> #include <linux/pci.h> #include <linux/platform_device.h> @@ -107,8 +106,7 @@ struct imx6_pcie_drvdata { struct imx6_pcie { struct dw_pcie *pci; - int reset_gpio; - bool gpio_active_high; + struct gpio_desc *reset_gpiod; bool link_is_up; struct clk_bulk_data clks[IMX6_PCIE_MAX_CLKS]; struct regmap *iomuxc_gpr; @@ -721,9 +719,7 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) } /* Some boards don't have PCIe reset GPIO. */ - if (gpio_is_valid(imx6_pcie->reset_gpio)) - gpio_set_value_cansleep(imx6_pcie->reset_gpio, - imx6_pcie->gpio_active_high); + gpiod_set_value_cansleep(imx6_pcie->reset_gpiod, 1); } static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) @@ -771,10 +767,9 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) } /* Some boards don't have PCIe reset GPIO. */ - if (gpio_is_valid(imx6_pcie->reset_gpio)) { + if (imx6_pcie->reset_gpiod) { msleep(100); - gpio_set_value_cansleep(imx6_pcie->reset_gpio, - !imx6_pcie->gpio_active_high); + gpiod_set_value_cansleep(imx6_pcie->reset_gpiod, 0); /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ msleep(100); } @@ -1131,7 +1126,7 @@ static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie, return ret; } - dw_pcie_ep_init_notify(ep); + pci_epc_init_notify(ep->epc); /* Start LTSSM. */ imx6_pcie_ltssm_enable(dev); @@ -1285,22 +1280,11 @@ static int imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(pci->dbi_base); /* Fetch GPIOs */ - imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0); - imx6_pcie->gpio_active_high = of_property_read_bool(node, - "reset-gpio-active-high"); - if (gpio_is_valid(imx6_pcie->reset_gpio)) { - ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio, - imx6_pcie->gpio_active_high ? - GPIOF_OUT_INIT_HIGH : - GPIOF_OUT_INIT_LOW, - "PCIe reset"); - if (ret) { - dev_err(dev, "unable to get reset gpio\n"); - return ret; - } - } else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) { - return imx6_pcie->reset_gpio; - } + imx6_pcie->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(imx6_pcie->reset_gpiod)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->reset_gpiod), + "unable to get reset gpio\n"); + gpiod_set_consumer_name(imx6_pcie->reset_gpiod, "PCIe reset"); if (imx6_pcie->drvdata->clks_cnt >= IMX6_PCIE_MAX_CLKS) return dev_err_probe(dev, -ENOMEM, "clks_cnt is too big\n"); diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 483c95406513..52c6420ae200 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -1329,7 +1329,7 @@ static int ks_pcie_probe(struct platform_device *pdev) goto err_ep_init; } - dw_pcie_ep_init_notify(&pci->ep); + pci_epc_init_notify(pci->ep.epc); break; default: diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index 7dde6d5fa4d8..a4a800699f89 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -104,7 +104,7 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id) dev_dbg(pci->dev, "Link up\n"); } else if (val & PEX_PF0_PME_MES_DR_LDD) { dev_dbg(pci->dev, "Link down\n"); - pci_epc_linkdown(pci->ep.epc); + dw_pcie_ep_linkdown(&pci->ep); } else if (val & PEX_PF0_PME_MES_DR_HRD) { dev_dbg(pci->dev, "Hot reset\n"); } @@ -286,7 +286,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) return ret; } - dw_pcie_ep_init_notify(&pci->ep); + pci_epc_init_notify(pci->ep.epc); return ls_pcie_ep_interrupt_init(pcie, pdev); } diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 6477c83262c2..db9482a113e9 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -9,7 +9,6 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/of_gpio.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/reset.h> diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index 6dfdda59f328..643115f74092 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -242,18 +242,24 @@ static struct pci_ops al_child_pci_ops = { .write = pci_generic_config_write, }; -static void al_pcie_config_prepare(struct al_pcie *pcie) +static int al_pcie_config_prepare(struct al_pcie *pcie) { struct al_pcie_target_bus_cfg *target_bus_cfg; struct dw_pcie_rp *pp = &pcie->pci->pp; unsigned int ecam_bus_mask; + struct resource_entry *ft; u32 cfg_control_offset; + struct resource *bus; u8 subordinate_bus; u8 secondary_bus; u32 cfg_control; u32 reg; - struct resource *bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS)->res; + ft = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS); + if (!ft) + return -ENODEV; + + bus = ft->res; target_bus_cfg = &pcie->target_bus_cfg; ecam_bus_mask = (pcie->ecam_size >> PCIE_ECAM_BUS_SHIFT) - 1; @@ -287,6 +293,8 @@ static void al_pcie_config_prepare(struct al_pcie *pcie) FIELD_PREP(CFG_CONTROL_SEC_BUS_MASK, secondary_bus); al_pcie_controller_writel(pcie, cfg_control_offset, reg); + + return 0; } static int al_pcie_host_init(struct dw_pcie_rp *pp) @@ -305,7 +313,9 @@ static int al_pcie_host_init(struct dw_pcie_rp *pp) if (rc) return rc; - al_pcie_config_prepare(pcie); + rc = al_pcie_config_prepare(pcie); + if (rc) + return rc; return 0; } diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index a4630b92489b..f8e7283dacd4 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -94,7 +94,7 @@ static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u regmap_write(artpec6_pcie->regmap, offset, val); } -static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) +static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 cpu_addr) { struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); struct dw_pcie_rp *pp = &pci->pp; @@ -102,13 +102,13 @@ static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) switch (artpec6_pcie->mode) { case DW_PCIE_RC_TYPE: - return pci_addr - pp->cfg0_base; + return cpu_addr - pp->cfg0_base; case DW_PCIE_EP_TYPE: - return pci_addr - ep->phys_base; + return cpu_addr - ep->phys_base; default: dev_err(pci->dev, "UNKNOWN device type\n"); } - return pci_addr; + return cpu_addr; } static int artpec6_pcie_establish_link(struct dw_pcie *pci) @@ -452,7 +452,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) return ret; } - dw_pcie_ep_init_notify(&pci->ep); + pci_epc_init_notify(pci->ep.epc); break; default: diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 47391d7d3a73..43ba5c6738df 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -16,30 +16,6 @@ #include <linux/pci-epf.h> /** - * dw_pcie_ep_linkup - Notify EPF drivers about Link Up event - * @ep: DWC EP device - */ -void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) -{ - struct pci_epc *epc = ep->epc; - - pci_epc_linkup(epc); -} -EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup); - -/** - * dw_pcie_ep_init_notify - Notify EPF drivers about EPC initialization complete - * @ep: DWC EP device - */ -void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep) -{ - struct pci_epc *epc = ep->epc; - - pci_epc_init_notify(epc); -} -EXPORT_SYMBOL_GPL(dw_pcie_ep_init_notify); - -/** * dw_pcie_ep_get_func_from_ep - Get the struct dw_pcie_ep_func corresponding to * the endpoint function * @ep: DWC EP device @@ -161,7 +137,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, if (!ep->bar_to_atu[bar]) free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows); else - free_win = ep->bar_to_atu[bar]; + free_win = ep->bar_to_atu[bar] - 1; if (free_win >= pci->num_ib_windows) { dev_err(pci->dev, "No free inbound window\n"); @@ -175,15 +151,18 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, return ret; } - ep->bar_to_atu[bar] = free_win; + /* + * Always increment free_win before assignment, since value 0 is used to identify + * unallocated mapping. + */ + ep->bar_to_atu[bar] = free_win + 1; set_bit(free_win, ep->ib_window_map); return 0; } -static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no, - phys_addr_t phys_addr, - u64 pci_addr, size_t size) +static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, + struct dw_pcie_ob_atu_cfg *atu) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); u32 free_win; @@ -195,13 +174,13 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no, return -EINVAL; } - ret = dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM, - phys_addr, pci_addr, size); + atu->index = free_win; + ret = dw_pcie_prog_outbound_atu(pci, atu); if (ret) return ret; set_bit(free_win, ep->ob_window_map); - ep->outbound_addr[free_win] = phys_addr; + ep->outbound_addr[free_win] = atu->cpu_addr; return 0; } @@ -212,7 +191,10 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar = epf_bar->barno; - u32 atu_index = ep->bar_to_atu[bar]; + u32 atu_index = ep->bar_to_atu[bar] - 1; + + if (!ep->bar_to_atu[bar]) + return; __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags); @@ -233,6 +215,13 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, int ret, type; u32 reg; + /* + * DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs + * 1 and 2 to form a 64-bit BAR. + */ + if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1)) + return -EINVAL; + reg = PCI_BASE_ADDRESS_0 + (4 * bar); if (!(flags & PCI_BASE_ADDRESS_SPACE)) @@ -301,8 +290,14 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, int ret; struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - - ret = dw_pcie_ep_outbound_atu(ep, func_no, addr, pci_addr, size); + struct dw_pcie_ob_atu_cfg atu = { 0 }; + + atu.func_no = func_no; + atu.type = PCIE_ATU_TYPE_MEM; + atu.cpu_addr = addr; + atu.pci_addr = pci_addr; + atu.size = size; + ret = dw_pcie_ep_outbound_atu(ep, &atu); if (ret) { dev_err(pci->dev, "Failed to enable address\n"); return ret; @@ -632,7 +627,6 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep) struct dw_pcie *pci = to_dw_pcie_from_ep(ep); dw_pcie_edma_remove(pci); - ep->epc->init_complete = false; } EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup); @@ -674,6 +668,34 @@ static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap) return 0; } +static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) +{ + unsigned int offset; + unsigned int nbars; + u32 reg, i; + + offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + + dw_pcie_dbi_ro_wr_en(pci); + + if (offset) { + reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >> + PCI_REBAR_CTRL_NBAR_SHIFT; + + /* + * PCIe r6.0, sec 7.8.6.2 require us to support at least one + * size in the range from 1 MB to 512 GB. Advertise support + * for 1 MB BAR size only. + */ + for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) + dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0); + } + + dw_pcie_setup(pci); + dw_pcie_dbi_ro_wr_dis(pci); +} + /** * dw_pcie_ep_init_registers - Initialize DWC EP specific registers * @ep: DWC EP device @@ -688,13 +710,11 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) struct dw_pcie_ep_func *ep_func; struct device *dev = pci->dev; struct pci_epc *epc = ep->epc; - unsigned int offset, ptm_cap_base; - unsigned int nbars; + u32 ptm_cap_base, reg; u8 hdr_type; u8 func_no; - int i, ret; void *addr; - u32 reg; + int ret; hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) & PCI_HEADER_TYPE_MASK; @@ -757,25 +777,8 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) if (ep->ops->init) ep->ops->init(ep); - offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM); - dw_pcie_dbi_ro_wr_en(pci); - - if (offset) { - reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); - nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >> - PCI_REBAR_CTRL_NBAR_SHIFT; - - /* - * PCIe r6.0, sec 7.8.6.2 require us to support at least one - * size in the range from 1 MB to 512 GB. Advertise support - * for 1 MB BAR size only. - */ - for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) - dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, BIT(4)); - } - /* * PTM responder capability can be disabled only after disabling * PTM root capability. @@ -792,8 +795,7 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) dw_pcie_dbi_ro_wr_dis(pci); } - dw_pcie_setup(pci); - dw_pcie_dbi_ro_wr_dis(pci); + dw_pcie_ep_init_non_sticky_registers(pci); return 0; @@ -805,6 +807,43 @@ err_remove_edma: EXPORT_SYMBOL_GPL(dw_pcie_ep_init_registers); /** + * dw_pcie_ep_linkup - Notify EPF drivers about Link Up event + * @ep: DWC EP device + */ +void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) +{ + struct pci_epc *epc = ep->epc; + + pci_epc_linkup(epc); +} +EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup); + +/** + * dw_pcie_ep_linkdown - Notify EPF drivers about Link Down event + * @ep: DWC EP device + * + * Non-sticky registers are also initialized before sending the notification to + * the EPF drivers. This is needed since the registers need to be initialized + * before the link comes back again. + */ +void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct pci_epc *epc = ep->epc; + + /* + * Initialize the non-sticky DWC registers as they would've reset post + * Link Down. This is specifically needed for drivers not supporting + * PERST# as they have no way to reinitialize the registers before the + * link comes back again. + */ + dw_pcie_ep_init_non_sticky_registers(pci); + + pci_epc_linkdown(epc); +} +EXPORT_SYMBOL_GPL(dw_pcie_ep_linkdown); + +/** * dw_pcie_ep_init - Initialize the endpoint device * @ep: DWC EP device * diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index d15a5c2d5b48..a0822d5371bc 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -398,6 +398,32 @@ static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) return 0; } +static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct resource_entry *win; + struct resource *res; + + win = resource_list_first_type(&pp->bridge->windows, IORESOURCE_MEM); + if (win) { + res = devm_kzalloc(pci->dev, sizeof(*res), GFP_KERNEL); + if (!res) + return; + + /* + * Allocate MSG TLP region of size 'region_align' at the end of + * the host bridge window. + */ + res->start = win->res->end - pci->region_align + 1; + res->end = win->res->end; + res->name = "msg"; + res->flags = win->res->flags | IORESOURCE_BUSY; + + if (!devm_request_resource(pci->dev, win->res, res)) + pp->msg_res = res; + } +} + int dw_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -484,6 +510,18 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) dw_pcie_iatu_detect(pci); + /* + * Allocate the resource for MSG TLP before programming the iATU + * outbound window in dw_pcie_setup_rc(). Since the allocation depends + * on the value of 'region_align', this has to be done after + * dw_pcie_iatu_detect(). + * + * Glue drivers need to set 'use_atu_msg' before dw_pcie_host_init() to + * make use of the generic MSG TLP implementation. + */ + if (pp->use_atu_msg) + dw_pcie_host_request_msg_tlp_res(pp); + ret = dw_pcie_edma_detect(pci); if (ret) goto err_free_msi; @@ -554,6 +592,7 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, { struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct dw_pcie_ob_atu_cfg atu = { 0 }; int type, ret; u32 busdev; @@ -576,8 +615,12 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, else type = PCIE_ATU_TYPE_CFG1; - ret = dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev, - pp->cfg0_size); + atu.type = type; + atu.cpu_addr = pp->cfg0_base; + atu.pci_addr = busdev; + atu.size = pp->cfg0_size; + + ret = dw_pcie_prog_outbound_atu(pci, &atu); if (ret) return NULL; @@ -589,6 +632,7 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, { struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct dw_pcie_ob_atu_cfg atu = { 0 }; int ret; ret = pci_generic_config_read(bus, devfn, where, size, val); @@ -596,9 +640,12 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, return ret; if (pp->cfg0_io_shared) { - ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, - pp->io_base, pp->io_bus_addr, - pp->io_size); + atu.type = PCIE_ATU_TYPE_IO; + atu.cpu_addr = pp->io_base; + atu.pci_addr = pp->io_bus_addr; + atu.size = pp->io_size; + + ret = dw_pcie_prog_outbound_atu(pci, &atu); if (ret) return PCIBIOS_SET_FAILED; } @@ -611,6 +658,7 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, { struct dw_pcie_rp *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct dw_pcie_ob_atu_cfg atu = { 0 }; int ret; ret = pci_generic_config_write(bus, devfn, where, size, val); @@ -618,9 +666,12 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, return ret; if (pp->cfg0_io_shared) { - ret = dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, - pp->io_base, pp->io_bus_addr, - pp->io_size); + atu.type = PCIE_ATU_TYPE_IO; + atu.cpu_addr = pp->io_base; + atu.pci_addr = pp->io_bus_addr; + atu.size = pp->io_size; + + ret = dw_pcie_prog_outbound_atu(pci, &atu); if (ret) return PCIBIOS_SET_FAILED; } @@ -655,6 +706,7 @@ static struct pci_ops dw_pcie_ops = { static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct dw_pcie_ob_atu_cfg atu = { 0 }; struct resource_entry *entry; int i, ret; @@ -682,10 +734,19 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) if (pci->num_ob_windows <= ++i) break; - ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_MEM, - entry->res->start, - entry->res->start - entry->offset, - resource_size(entry->res)); + atu.index = i; + atu.type = PCIE_ATU_TYPE_MEM; + atu.cpu_addr = entry->res->start; + atu.pci_addr = entry->res->start - entry->offset; + + /* Adjust iATU size if MSG TLP region was allocated before */ + if (pp->msg_res && pp->msg_res->parent == entry->res) + atu.size = resource_size(entry->res) - + resource_size(pp->msg_res); + else + atu.size = resource_size(entry->res); + + ret = dw_pcie_prog_outbound_atu(pci, &atu); if (ret) { dev_err(pci->dev, "Failed to set MEM range %pr\n", entry->res); @@ -695,10 +756,13 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) if (pp->io_size) { if (pci->num_ob_windows > ++i) { - ret = dw_pcie_prog_outbound_atu(pci, i, PCIE_ATU_TYPE_IO, - pp->io_base, - pp->io_bus_addr, - pp->io_size); + atu.index = i; + atu.type = PCIE_ATU_TYPE_IO; + atu.cpu_addr = pp->io_base; + atu.pci_addr = pp->io_bus_addr; + atu.size = pp->io_size; + + ret = dw_pcie_prog_outbound_atu(pci, &atu); if (ret) { dev_err(pci->dev, "Failed to set IO range %pr\n", entry->res); @@ -713,6 +777,8 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n", pci->num_ob_windows); + pp->msg_atu_index = i; + i = 0; resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) { if (resource_type(entry->res) != IORESOURCE_MEM) @@ -818,11 +884,47 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) } EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); +static int dw_pcie_pme_turn_off(struct dw_pcie *pci) +{ + struct dw_pcie_ob_atu_cfg atu = { 0 }; + void __iomem *mem; + int ret; + + if (pci->num_ob_windows <= pci->pp.msg_atu_index) + return -ENOSPC; + + if (!pci->pp.msg_res) + return -ENOSPC; + + atu.code = PCIE_MSG_CODE_PME_TURN_OFF; + atu.routing = PCIE_MSG_TYPE_R_BC; + atu.type = PCIE_ATU_TYPE_MSG; + atu.size = resource_size(pci->pp.msg_res); + atu.index = pci->pp.msg_atu_index; + + atu.cpu_addr = pci->pp.msg_res->start; + + ret = dw_pcie_prog_outbound_atu(pci, &atu); + if (ret) + return ret; + + mem = ioremap(atu.cpu_addr, pci->region_align); + if (!mem) + return -ENOMEM; + + /* A dummy write is converted to a Msg TLP */ + writel(0, mem); + + iounmap(mem); + + return 0; +} + int dw_pcie_suspend_noirq(struct dw_pcie *pci) { u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); u32 val; - int ret; + int ret = 0; /* * If L1SS is supported, then do not put the link into L2 as some @@ -834,10 +936,13 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) if (dw_pcie_get_ltssm(pci) <= DW_PCIE_LTSSM_DETECT_ACT) return 0; - if (!pci->pp.ops->pme_turn_off) - return 0; + if (pci->pp.ops->pme_turn_off) + pci->pp.ops->pme_turn_off(&pci->pp); + else + ret = dw_pcie_pme_turn_off(pci); - pci->pp.ops->pme_turn_off(&pci->pp); + if (ret) + return ret; ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE, PCIE_PME_TO_L2_TIMEOUT_US/10, diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 8490c5d6ff9f..771b9d9be077 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -154,7 +154,7 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) dw_pcie_ep_deinit(&pci->ep); } - dw_pcie_ep_init_notify(&pci->ep); + pci_epc_init_notify(pci->ep.epc); break; default: diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 250cf7f40b85..26ecf0472f86 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -465,56 +465,61 @@ static inline u32 dw_pcie_enable_ecrc(u32 val) return val | PCIE_ATU_TD; } -static int __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no, - int index, int type, u64 cpu_addr, - u64 pci_addr, u64 size) +int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, + const struct dw_pcie_ob_atu_cfg *atu) { + u64 cpu_addr = atu->cpu_addr; u32 retries, val; u64 limit_addr; if (pci->ops && pci->ops->cpu_addr_fixup) cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr); - limit_addr = cpu_addr + size - 1; + limit_addr = cpu_addr + atu->size - 1; if ((limit_addr & ~pci->region_limit) != (cpu_addr & ~pci->region_limit) || !IS_ALIGNED(cpu_addr, pci->region_align) || - !IS_ALIGNED(pci_addr, pci->region_align) || !size) { + !IS_ALIGNED(atu->pci_addr, pci->region_align) || !atu->size) { return -EINVAL; } - dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LOWER_BASE, + dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_BASE, lower_32_bits(cpu_addr)); - dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_BASE, + dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_BASE, upper_32_bits(cpu_addr)); - dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LIMIT, + dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LIMIT, lower_32_bits(limit_addr)); if (dw_pcie_ver_is_ge(pci, 460A)) - dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_LIMIT, + dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_LIMIT, upper_32_bits(limit_addr)); - dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LOWER_TARGET, - lower_32_bits(pci_addr)); - dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_TARGET, - upper_32_bits(pci_addr)); + dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_TARGET, + lower_32_bits(atu->pci_addr)); + dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_TARGET, + upper_32_bits(atu->pci_addr)); - val = type | PCIE_ATU_FUNC_NUM(func_no); + val = atu->type | atu->routing | PCIE_ATU_FUNC_NUM(atu->func_no); if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) && dw_pcie_ver_is_ge(pci, 460A)) val |= PCIE_ATU_INCREASE_REGION_SIZE; if (dw_pcie_ver_is(pci, 490A)) val = dw_pcie_enable_ecrc(val); - dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_REGION_CTRL1, val); + dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val); - dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE); + val = PCIE_ATU_ENABLE; + if (atu->type == PCIE_ATU_TYPE_MSG) { + /* The data-less messages only for now */ + val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code; + } + dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2, val); /* * Make sure ATU enable takes effect before any subsequent config * and I/O accesses. */ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_atu_ob(pci, index, PCIE_ATU_REGION_CTRL2); + val = dw_pcie_readl_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2); if (val & PCIE_ATU_ENABLE) return 0; @@ -526,21 +531,6 @@ static int __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no, return -ETIMEDOUT; } -int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, - u64 cpu_addr, u64 pci_addr, u64 size) -{ - return __dw_pcie_prog_outbound_atu(pci, 0, index, type, - cpu_addr, pci_addr, size); -} - -int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index, - int type, u64 cpu_addr, u64 pci_addr, - u64 size) -{ - return __dw_pcie_prog_outbound_atu(pci, func_no, index, type, - cpu_addr, pci_addr, size); -} - static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg) { return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg); @@ -655,7 +645,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) if (dw_pcie_link_up(pci)) break; - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + msleep(LINK_WAIT_SLEEP_MS); } if (retries >= LINK_WAIT_MAX_RETRIES) { diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index f8e5431a207b..069c72f318a7 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -63,8 +63,7 @@ /* Parameters for the waiting for link up routine */ #define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_USLEEP_MIN 90000 -#define LINK_WAIT_USLEEP_MAX 100000 +#define LINK_WAIT_SLEEP_MS 90 /* Parameters for the waiting for iATU enabled routine */ #define LINK_WAIT_MAX_IATU_RETRIES 5 @@ -148,11 +147,13 @@ #define PCIE_ATU_TYPE_IO 0x2 #define PCIE_ATU_TYPE_CFG0 0x4 #define PCIE_ATU_TYPE_CFG1 0x5 +#define PCIE_ATU_TYPE_MSG 0x10 #define PCIE_ATU_TD BIT(8) #define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20) #define PCIE_ATU_REGION_CTRL2 0x004 #define PCIE_ATU_ENABLE BIT(31) #define PCIE_ATU_BAR_MODE_ENABLE BIT(30) +#define PCIE_ATU_INHIBIT_PAYLOAD BIT(22) #define PCIE_ATU_FUNC_NUM_MATCH_EN BIT(19) #define PCIE_ATU_LOWER_BASE 0x008 #define PCIE_ATU_UPPER_BASE 0x00C @@ -299,6 +300,17 @@ enum dw_pcie_ltssm { DW_PCIE_LTSSM_UNKNOWN = 0xFFFFFFFF, }; +struct dw_pcie_ob_atu_cfg { + int index; + int type; + u8 func_no; + u8 code; + u8 routing; + u64 cpu_addr; + u64 pci_addr; + u64 size; +}; + struct dw_pcie_host_ops { int (*init)(struct dw_pcie_rp *pp); void (*deinit)(struct dw_pcie_rp *pp); @@ -328,6 +340,9 @@ struct dw_pcie_rp { struct pci_host_bridge *bridge; raw_spinlock_t lock; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); + bool use_atu_msg; + int msg_atu_index; + struct resource *msg_res; }; struct dw_pcie_ep_ops { @@ -433,10 +448,8 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val); int dw_pcie_link_up(struct dw_pcie *pci); void dw_pcie_upconfig_setup(struct dw_pcie *pci); int dw_pcie_wait_for_link(struct dw_pcie *pci); -int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, - u64 cpu_addr, u64 pci_addr, u64 size); -int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index, - int type, u64 cpu_addr, u64 pci_addr, u64 size); +int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, + const struct dw_pcie_ob_atu_cfg *atu); int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, u64 cpu_addr, u64 pci_addr, u64 size); int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, @@ -668,9 +681,9 @@ static inline void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, #ifdef CONFIG_PCIE_DW_EP void dw_pcie_ep_linkup(struct dw_pcie_ep *ep); +void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep); int dw_pcie_ep_init(struct dw_pcie_ep *ep); int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep); -void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep); void dw_pcie_ep_deinit(struct dw_pcie_ep *ep); void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep); int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no); @@ -688,18 +701,18 @@ static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) { } -static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep) +static inline void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep) { - return 0; } -static inline int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) +static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep) { return 0; } -static inline void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep) +static inline int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) { + return 0; } static inline void dw_pcie_ep_deinit(struct dw_pcie_ep *ep) diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 98bbc83182b4..278205db60a2 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -442,7 +442,7 @@ static int keembay_pcie_probe(struct platform_device *pdev) return ret; } - dw_pcie_ep_init_notify(&pci->ep); + pci_epc_init_notify(pci->ep.epc); break; default: diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index d5523f302102..0a29136491b8 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -12,12 +12,10 @@ #include <linux/compiler.h> #include <linux/delay.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/mfd/syscon.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_pci.h> #include <linux/phy/phy.h> #include <linux/pci.h> @@ -78,16 +76,16 @@ struct kirin_pcie { void *phy_priv; /* only for PCIE_KIRIN_INTERNAL_PHY */ /* DWC PERST# */ - int gpio_id_dwc_perst; + struct gpio_desc *id_dwc_perst_gpio; /* Per-slot PERST# */ int num_slots; - int gpio_id_reset[MAX_PCI_SLOTS]; + struct gpio_desc *id_reset_gpio[MAX_PCI_SLOTS]; const char *reset_names[MAX_PCI_SLOTS]; /* Per-slot clkreq */ int n_gpio_clkreq; - int gpio_id_clkreq[MAX_PCI_SLOTS]; + struct gpio_desc *id_clkreq_gpio[MAX_PCI_SLOTS]; const char *clkreq_names[MAX_PCI_SLOTS]; }; @@ -381,15 +379,20 @@ static int kirin_pcie_get_gpio_enable(struct kirin_pcie *pcie, pcie->n_gpio_clkreq = ret; for (i = 0; i < pcie->n_gpio_clkreq; i++) { - pcie->gpio_id_clkreq[i] = of_get_named_gpio(dev->of_node, - "hisilicon,clken-gpios", i); - if (pcie->gpio_id_clkreq[i] < 0) - return pcie->gpio_id_clkreq[i]; + pcie->id_clkreq_gpio[i] = devm_gpiod_get_index(dev, + "hisilicon,clken", i, + GPIOD_OUT_LOW); + if (IS_ERR(pcie->id_clkreq_gpio[i])) + return dev_err_probe(dev, PTR_ERR(pcie->id_clkreq_gpio[i]), + "unable to get a valid clken gpio\n"); pcie->clkreq_names[i] = devm_kasprintf(dev, GFP_KERNEL, "pcie_clkreq_%d", i); if (!pcie->clkreq_names[i]) return -ENOMEM; + + gpiod_set_consumer_name(pcie->id_clkreq_gpio[i], + pcie->clkreq_names[i]); } return 0; @@ -400,29 +403,33 @@ static int kirin_pcie_parse_port(struct kirin_pcie *pcie, struct device_node *node) { struct device *dev = &pdev->dev; - struct device_node *parent, *child; int ret, slot, i; - for_each_available_child_of_node(node, parent) { - for_each_available_child_of_node(parent, child) { + for_each_available_child_of_node_scoped(node, parent) { + for_each_available_child_of_node_scoped(parent, child) { i = pcie->num_slots; - pcie->gpio_id_reset[i] = of_get_named_gpio(child, - "reset-gpios", 0); - if (pcie->gpio_id_reset[i] < 0) - continue; + pcie->id_reset_gpio[i] = devm_fwnode_gpiod_get_index(dev, + of_fwnode_handle(child), + "reset", 0, GPIOD_OUT_LOW, + NULL); + if (IS_ERR(pcie->id_reset_gpio[i])) { + if (PTR_ERR(pcie->id_reset_gpio[i]) == -ENOENT) + continue; + return dev_err_probe(dev, PTR_ERR(pcie->id_reset_gpio[i]), + "unable to get a valid reset gpio\n"); + } pcie->num_slots++; if (pcie->num_slots > MAX_PCI_SLOTS) { dev_err(dev, "Too many PCI slots!\n"); - ret = -EINVAL; - goto put_node; + return -EINVAL; } ret = of_pci_get_devfn(child); if (ret < 0) { dev_err(dev, "failed to parse devfn: %d\n", ret); - goto put_node; + return ret; } slot = PCI_SLOT(ret); @@ -430,19 +437,15 @@ static int kirin_pcie_parse_port(struct kirin_pcie *pcie, pcie->reset_names[i] = devm_kasprintf(dev, GFP_KERNEL, "pcie_perst_%d", slot); - if (!pcie->reset_names[i]) { - ret = -ENOMEM; - goto put_node; - } + if (!pcie->reset_names[i]) + return -ENOMEM; + + gpiod_set_consumer_name(pcie->id_reset_gpio[i], + pcie->reset_names[i]); } } return 0; - -put_node: - of_node_put(child); - of_node_put(parent); - return ret; } static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie, @@ -463,14 +466,11 @@ static long kirin_pcie_get_resource(struct kirin_pcie *kirin_pcie, return PTR_ERR(kirin_pcie->apb); /* pcie internal PERST# gpio */ - kirin_pcie->gpio_id_dwc_perst = of_get_named_gpio(dev->of_node, - "reset-gpios", 0); - if (kirin_pcie->gpio_id_dwc_perst == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (!gpio_is_valid(kirin_pcie->gpio_id_dwc_perst)) { - dev_err(dev, "unable to get a valid gpio pin\n"); - return -ENODEV; - } + kirin_pcie->id_dwc_perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(kirin_pcie->id_dwc_perst_gpio)) + return dev_err_probe(dev, PTR_ERR(kirin_pcie->id_dwc_perst_gpio), + "unable to get a valid gpio pin\n"); + gpiod_set_consumer_name(kirin_pcie->id_dwc_perst_gpio, "pcie_perst_bridge"); ret = kirin_pcie_get_gpio_enable(kirin_pcie, pdev); if (ret) @@ -553,7 +553,7 @@ static int kirin_pcie_add_bus(struct pci_bus *bus) /* Send PERST# to each slot */ for (i = 0; i < kirin_pcie->num_slots; i++) { - ret = gpio_direction_output(kirin_pcie->gpio_id_reset[i], 1); + ret = gpiod_direction_output_raw(kirin_pcie->id_reset_gpio[i], 1); if (ret) { dev_err(pci->dev, "PERST# %s error: %d\n", kirin_pcie->reset_names[i], ret); @@ -623,44 +623,6 @@ static int kirin_pcie_host_init(struct dw_pcie_rp *pp) return 0; } -static int kirin_pcie_gpio_request(struct kirin_pcie *kirin_pcie, - struct device *dev) -{ - int ret, i; - - for (i = 0; i < kirin_pcie->num_slots; i++) { - if (!gpio_is_valid(kirin_pcie->gpio_id_reset[i])) { - dev_err(dev, "unable to get a valid %s gpio\n", - kirin_pcie->reset_names[i]); - return -ENODEV; - } - - ret = devm_gpio_request(dev, kirin_pcie->gpio_id_reset[i], - kirin_pcie->reset_names[i]); - if (ret) - return ret; - } - - for (i = 0; i < kirin_pcie->n_gpio_clkreq; i++) { - if (!gpio_is_valid(kirin_pcie->gpio_id_clkreq[i])) { - dev_err(dev, "unable to get a valid %s gpio\n", - kirin_pcie->clkreq_names[i]); - return -ENODEV; - } - - ret = devm_gpio_request(dev, kirin_pcie->gpio_id_clkreq[i], - kirin_pcie->clkreq_names[i]); - if (ret) - return ret; - - ret = gpio_direction_output(kirin_pcie->gpio_id_clkreq[i], 0); - if (ret) - return ret; - } - - return 0; -} - static const struct dw_pcie_ops kirin_dw_pcie_ops = { .read_dbi = kirin_pcie_read_dbi, .write_dbi = kirin_pcie_write_dbi, @@ -680,7 +642,7 @@ static int kirin_pcie_power_off(struct kirin_pcie *kirin_pcie) return hi3660_pcie_phy_power_off(kirin_pcie); for (i = 0; i < kirin_pcie->n_gpio_clkreq; i++) - gpio_direction_output(kirin_pcie->gpio_id_clkreq[i], 1); + gpiod_direction_output_raw(kirin_pcie->id_clkreq_gpio[i], 1); phy_power_off(kirin_pcie->phy); phy_exit(kirin_pcie->phy); @@ -707,10 +669,6 @@ static int kirin_pcie_power_on(struct platform_device *pdev, if (IS_ERR(kirin_pcie->phy)) return PTR_ERR(kirin_pcie->phy); - ret = kirin_pcie_gpio_request(kirin_pcie, dev); - if (ret) - return ret; - ret = phy_init(kirin_pcie->phy); if (ret) goto err; @@ -723,11 +681,9 @@ static int kirin_pcie_power_on(struct platform_device *pdev, /* perst assert Endpoint */ usleep_range(REF_2_PERST_MIN, REF_2_PERST_MAX); - if (!gpio_request(kirin_pcie->gpio_id_dwc_perst, "pcie_perst_bridge")) { - ret = gpio_direction_output(kirin_pcie->gpio_id_dwc_perst, 1); - if (ret) - goto err; - } + ret = gpiod_direction_output_raw(kirin_pcie->id_dwc_perst_gpio, 1); + if (ret) + goto err; usleep_range(PERST_2_ACCESS_MIN, PERST_2_ACCESS_MAX); diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 2fb8c15e7a91..02a2a871a91f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -482,7 +482,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) val &= ~PARF_MSTR_AXI_CLK_EN; writel_relaxed(val, pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL); - dw_pcie_ep_init_notify(&pcie_ep->pci.ep); + pci_epc_init_notify(pcie_ep->pci.ep.epc); /* Enable LTSSM */ val = readl_relaxed(pcie_ep->parf + PARF_LTSSM); @@ -507,6 +507,7 @@ static void qcom_pcie_perst_assert(struct dw_pcie *pci) return; } + pci_epc_deinit_notify(pci->ep.epc); dw_pcie_ep_cleanup(&pci->ep); qcom_pcie_disable_resources(pcie_ep); pcie_ep->link_status = QCOM_PCIE_EP_LINK_DISABLED; @@ -640,12 +641,12 @@ static irqreturn_t qcom_pcie_ep_global_irq_thread(int irq, void *data) if (FIELD_GET(PARF_INT_ALL_LINK_DOWN, status)) { dev_dbg(dev, "Received Linkdown event\n"); pcie_ep->link_status = QCOM_PCIE_EP_LINK_DOWN; - pci_epc_linkdown(pci->ep.epc); + dw_pcie_ep_linkdown(&pci->ep); } else if (FIELD_GET(PARF_INT_ALL_BME, status)) { - dev_dbg(dev, "Received BME event. Link is enabled!\n"); + dev_dbg(dev, "Received Bus Master Enable event\n"); pcie_ep->link_status = QCOM_PCIE_EP_LINK_ENABLED; qcom_pcie_ep_icc_update(pcie_ep); - pci_epc_bme_notify(pci->ep.epc); + pci_epc_bus_master_enable_notify(pci->ep.epc); } else if (FIELD_GET(PARF_INT_ALL_PM_TURNOFF, status)) { dev_dbg(dev, "Received PM Turn-off event! Entering L23\n"); val = readl_relaxed(pcie_ep->parf + PARF_PM_CTRL); diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 14772edcf0d3..436076612c8f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/pci.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index cfeccc2f9ee1..237a6a8818de 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -437,7 +437,7 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar) rcar_gen4_pcie_ep_deinit(rcar); } - dw_pcie_ep_init_notify(ep); + pci_epc_init_notify(ep->epc); return ret; } diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 93f5433c5c55..3ecbc63d9b31 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -13,7 +13,6 @@ #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/delay.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/interconnect.h> #include <linux/interrupt.h> @@ -21,7 +20,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_pci.h> #include <linux/pci.h> #include <linux/phy/phy.h> @@ -1715,6 +1713,7 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie) if (ret) dev_err(pcie->dev, "Failed to go Detect state: %d\n", ret); + pci_epc_deinit_notify(pcie->pci.ep.epc); dw_pcie_ep_cleanup(&pcie->pci.ep); reset_control_assert(pcie->core_rst); @@ -1903,7 +1902,7 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) goto fail_init_complete; } - dw_pcie_ep_init_notify(ep); + pci_epc_init_notify(ep->epc); /* Program the private control to allow sending LTR upstream */ if (pcie->of_data->has_ltr_req_fix) { diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index a2b844268e28..d6e73811216e 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -410,7 +410,7 @@ static int uniphier_pcie_ep_probe(struct platform_device *pdev) return ret; } - dw_pcie_ep_init_notify(&priv->pci.ep); + pci_epc_init_notify(priv->pci.ep.epc); return 0; } diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 71ecd7ddcc8a..8b3e1a079cf3 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -23,7 +23,6 @@ #include <linux/platform_device.h> #include <linux/msi.h> #include <linux/of_address.h> -#include <linux/of_gpio.h> #include <linux/of_pci.h> #include "../pci.h" diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index 45b71806182d..c96b2de163b5 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -73,10 +73,6 @@ int pci_host_common_probe(struct platform_device *pdev) if (IS_ERR(cfg)) return PTR_ERR(cfg); - /* Do not reassign resources if probe only */ - if (!pci_has_flag(PCI_PROBE_ONLY)) - pci_add_flags(PCI_REASSIGN_ALL_BUS); - bridge->sysdata = cfg; bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->msi_domain = true; diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 5992280e8110..cdd5be16021d 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -1130,8 +1130,8 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where, PCI_CAPABILITY_LIST) { /* ROM BARs are unimplemented */ *val = 0; - } else if (where >= PCI_INTERRUPT_LINE && where + size <= - PCI_INTERRUPT_PIN) { + } else if ((where >= PCI_INTERRUPT_LINE && where + size <= PCI_INTERRUPT_PIN) || + (where >= PCI_INTERRUPT_PIN && where + size <= PCI_MIN_GNT)) { /* * Interrupt Line and Interrupt PIN are hard-wired to zero * because this front-end only supports message-signaled diff --git a/drivers/pci/devres.c b/drivers/pci/devres.c index 2c562b9eaf80..3780a9f9ec00 100644 --- a/drivers/pci/devres.c +++ b/drivers/pci/devres.c @@ -4,14 +4,249 @@ #include "pci.h" /* - * PCI iomap devres + * On the state of PCI's devres implementation: + * + * The older devres API for PCI has two significant problems: + * + * 1. It is very strongly tied to the statically allocated mapping table in + * struct pcim_iomap_devres below. This is mostly solved in the sense of the + * pcim_ functions in this file providing things like ranged mapping by + * bypassing this table, whereas the functions that were present in the old + * API still enter the mapping addresses into the table for users of the old + * API. + * + * 2. The region-request-functions in pci.c do become managed IF the device has + * been enabled with pcim_enable_device() instead of pci_enable_device(). + * This resulted in the API becoming inconsistent: Some functions have an + * obviously managed counter-part (e.g., pci_iomap() <-> pcim_iomap()), + * whereas some don't and are never managed, while others don't and are + * _sometimes_ managed (e.g. pci_request_region()). + * + * Consequently, in the new API, region requests performed by the pcim_ + * functions are automatically cleaned up through the devres callback + * pcim_addr_resource_release(). + * + * Users of pcim_enable_device() + pci_*region*() are redirected in + * pci.c to the managed functions here in this file. This isn't exactly + * perfect, but the only alternative way would be to port ALL drivers + * using said combination to pcim_ functions. + * + * TODO: + * Remove the legacy table entirely once all calls to pcim_iomap_table() in + * the kernel have been removed. */ -#define PCIM_IOMAP_MAX PCI_STD_NUM_BARS +/* + * Legacy struct storing addresses to whole mapped BARs. + */ struct pcim_iomap_devres { - void __iomem *table[PCIM_IOMAP_MAX]; + void __iomem *table[PCI_STD_NUM_BARS]; +}; + +/* Used to restore the old INTx state on driver detach. */ +struct pcim_intx_devres { + int orig_intx; +}; + +enum pcim_addr_devres_type { + /* Default initializer. */ + PCIM_ADDR_DEVRES_TYPE_INVALID, + + /* A requested region spanning an entire BAR. */ + PCIM_ADDR_DEVRES_TYPE_REGION, + + /* + * A requested region spanning an entire BAR, and a mapping for + * the entire BAR. + */ + PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING, + + /* + * A mapping within a BAR, either spanning the whole BAR or just a + * range. Without a requested region. + */ + PCIM_ADDR_DEVRES_TYPE_MAPPING, +}; + +/* + * This struct envelops IO or MEM addresses, i.e., mappings and region + * requests, because those are very frequently requested and released + * together. + */ +struct pcim_addr_devres { + enum pcim_addr_devres_type type; + void __iomem *baseaddr; + unsigned long offset; + unsigned long len; + int bar; }; +static inline void pcim_addr_devres_clear(struct pcim_addr_devres *res) +{ + memset(res, 0, sizeof(*res)); + res->bar = -1; +} + +/* + * The following functions, __pcim_*_region*, exist as counterparts to the + * versions from pci.c - which, unfortunately, can be in "hybrid mode", i.e., + * sometimes managed, sometimes not. + * + * To separate the APIs cleanly, we define our own, simplified versions here. + */ + +/** + * __pcim_request_region_range - Request a ranged region + * @pdev: PCI device the region belongs to + * @bar: BAR the range is within + * @offset: offset from the BAR's start address + * @maxlen: length in bytes, beginning at @offset + * @name: name associated with the request + * @req_flags: flags for the request, e.g., for kernel-exclusive requests + * + * Returns: 0 on success, a negative error code on failure. + * + * Request a range within a device's PCI BAR. Sanity check the input. + */ +static int __pcim_request_region_range(struct pci_dev *pdev, int bar, + unsigned long offset, + unsigned long maxlen, + const char *name, int req_flags) +{ + resource_size_t start = pci_resource_start(pdev, bar); + resource_size_t len = pci_resource_len(pdev, bar); + unsigned long dev_flags = pci_resource_flags(pdev, bar); + + if (start == 0 || len == 0) /* Unused BAR. */ + return 0; + if (len <= offset) + return -EINVAL; + + start += offset; + len -= offset; + + if (len > maxlen && maxlen != 0) + len = maxlen; + + if (dev_flags & IORESOURCE_IO) { + if (!request_region(start, len, name)) + return -EBUSY; + } else if (dev_flags & IORESOURCE_MEM) { + if (!__request_mem_region(start, len, name, req_flags)) + return -EBUSY; + } else { + /* That's not a device we can request anything on. */ + return -ENODEV; + } + + return 0; +} + +static void __pcim_release_region_range(struct pci_dev *pdev, int bar, + unsigned long offset, + unsigned long maxlen) +{ + resource_size_t start = pci_resource_start(pdev, bar); + resource_size_t len = pci_resource_len(pdev, bar); + unsigned long flags = pci_resource_flags(pdev, bar); + + if (len <= offset || start == 0) + return; + + if (len == 0 || maxlen == 0) /* This an unused BAR. Do nothing. */ + return; + + start += offset; + len -= offset; + + if (len > maxlen) + len = maxlen; + + if (flags & IORESOURCE_IO) + release_region(start, len); + else if (flags & IORESOURCE_MEM) + release_mem_region(start, len); +} + +static int __pcim_request_region(struct pci_dev *pdev, int bar, + const char *name, int flags) +{ + unsigned long offset = 0; + unsigned long len = pci_resource_len(pdev, bar); + + return __pcim_request_region_range(pdev, bar, offset, len, name, flags); +} + +static void __pcim_release_region(struct pci_dev *pdev, int bar) +{ + unsigned long offset = 0; + unsigned long len = pci_resource_len(pdev, bar); + + __pcim_release_region_range(pdev, bar, offset, len); +} + +static void pcim_addr_resource_release(struct device *dev, void *resource_raw) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pcim_addr_devres *res = resource_raw; + + switch (res->type) { + case PCIM_ADDR_DEVRES_TYPE_REGION: + __pcim_release_region(pdev, res->bar); + break; + case PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING: + pci_iounmap(pdev, res->baseaddr); + __pcim_release_region(pdev, res->bar); + break; + case PCIM_ADDR_DEVRES_TYPE_MAPPING: + pci_iounmap(pdev, res->baseaddr); + break; + default: + break; + } +} + +static struct pcim_addr_devres *pcim_addr_devres_alloc(struct pci_dev *pdev) +{ + struct pcim_addr_devres *res; + + res = devres_alloc_node(pcim_addr_resource_release, sizeof(*res), + GFP_KERNEL, dev_to_node(&pdev->dev)); + if (res) + pcim_addr_devres_clear(res); + return res; +} + +/* Just for consistency and readability. */ +static inline void pcim_addr_devres_free(struct pcim_addr_devres *res) +{ + devres_free(res); +} + +/* + * Used by devres to identify a pcim_addr_devres. + */ +static int pcim_addr_resources_match(struct device *dev, + void *a_raw, void *b_raw) +{ + struct pcim_addr_devres *a, *b; + + a = a_raw; + b = b_raw; + + if (a->type != b->type) + return 0; + + switch (a->type) { + case PCIM_ADDR_DEVRES_TYPE_REGION: + case PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING: + return a->bar == b->bar; + case PCIM_ADDR_DEVRES_TYPE_MAPPING: + return a->baseaddr == b->baseaddr; + default: + return 0; + } +} static void devm_pci_unmap_iospace(struct device *dev, void *ptr) { @@ -92,8 +327,8 @@ 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:: + * Returns a pointer to the remapped memory or an IOMEM_ERR_PTR() encoded error + * code on failure. Usage example:: * * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); * base = devm_pci_remap_cfg_resource(&pdev->dev, res); @@ -140,96 +375,147 @@ void __iomem *devm_pci_remap_cfg_resource(struct device *dev, } EXPORT_SYMBOL(devm_pci_remap_cfg_resource); +static void __pcim_clear_mwi(void *pdev_raw) +{ + struct pci_dev *pdev = pdev_raw; + + pci_clear_mwi(pdev); +} + /** * pcim_set_mwi - a device-managed pci_set_mwi() - * @dev: the PCI device for which MWI is enabled + * @pdev: the PCI device for which MWI is enabled * * Managed pci_set_mwi(). * * RETURNS: An appropriate -ERRNO error value on error, or zero for success. */ -int pcim_set_mwi(struct pci_dev *dev) +int pcim_set_mwi(struct pci_dev *pdev) { - struct pci_devres *dr; + int ret; - dr = find_pci_dr(dev); - if (!dr) - return -ENOMEM; + ret = devm_add_action(&pdev->dev, __pcim_clear_mwi, pdev); + if (ret != 0) + return ret; + + ret = pci_set_mwi(pdev); + if (ret != 0) + devm_remove_action(&pdev->dev, __pcim_clear_mwi, pdev); - dr->mwi = 1; - return pci_set_mwi(dev); + return ret; } EXPORT_SYMBOL(pcim_set_mwi); +static inline bool mask_contains_bar(int mask, int bar) +{ + return mask & BIT(bar); +} -static void pcim_release(struct device *gendev, void *res) +/* + * This is a copy of pci_intx() used to bypass the problem of recursive + * function calls due to the hybrid nature of pci_intx(). + */ +static void __pcim_intx(struct pci_dev *pdev, int enable) { - struct pci_dev *dev = to_pci_dev(gendev); - struct pci_devres *this = res; - int i; + u16 pci_command, new; - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) - if (this->region_mask & (1 << i)) - pci_release_region(dev, i); + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + + if (enable) + new = pci_command & ~PCI_COMMAND_INTX_DISABLE; + else + new = pci_command | PCI_COMMAND_INTX_DISABLE; - if (this->mwi) - pci_clear_mwi(dev); + if (new != pci_command) + pci_write_config_word(pdev, PCI_COMMAND, new); +} - if (this->restore_intx) - pci_intx(dev, this->orig_intx); +static void pcim_intx_restore(struct device *dev, void *data) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pcim_intx_devres *res = data; - if (this->enabled && !this->pinned) - pci_disable_device(dev); + __pcim_intx(pdev, res->orig_intx); } -/* - * TODO: After the last four callers in pci.c are ported, find_pci_dr() - * needs to be made static again. - */ -struct pci_devres *find_pci_dr(struct pci_dev *pdev) +static struct pcim_intx_devres *get_or_create_intx_devres(struct device *dev) { - if (pci_is_managed(pdev)) - return devres_find(&pdev->dev, pcim_release, NULL, NULL); - return NULL; + struct pcim_intx_devres *res; + + res = devres_find(dev, pcim_intx_restore, NULL, NULL); + if (res) + return res; + + res = devres_alloc(pcim_intx_restore, sizeof(*res), GFP_KERNEL); + if (res) + devres_add(dev, res); + + return res; } -static struct pci_devres *get_pci_dr(struct pci_dev *pdev) +/** + * pcim_intx - managed pci_intx() + * @pdev: the PCI device to operate on + * @enable: boolean: whether to enable or disable PCI INTx + * + * Returns: 0 on success, -ENOMEM on error. + * + * Enable/disable PCI INTx for device @pdev. + * Restore the original state on driver detach. + */ +int pcim_intx(struct pci_dev *pdev, int enable) { - struct pci_devres *dr, *new_dr; + struct pcim_intx_devres *res; - dr = devres_find(&pdev->dev, pcim_release, NULL, NULL); - if (dr) - return dr; + res = get_or_create_intx_devres(&pdev->dev); + if (!res) + return -ENOMEM; - new_dr = devres_alloc(pcim_release, sizeof(*new_dr), GFP_KERNEL); - if (!new_dr) - return NULL; - return devres_get(&pdev->dev, new_dr, NULL, NULL); + res->orig_intx = !enable; + __pcim_intx(pdev, enable); + + return 0; +} + +static void pcim_disable_device(void *pdev_raw) +{ + struct pci_dev *pdev = pdev_raw; + + if (!pdev->pinned) + pci_disable_device(pdev); } /** * pcim_enable_device - Managed pci_enable_device() * @pdev: PCI device to be initialized * - * Managed pci_enable_device(). + * Returns: 0 on success, negative error code on failure. + * + * Managed pci_enable_device(). Device will automatically be disabled on + * driver detach. */ int pcim_enable_device(struct pci_dev *pdev) { - struct pci_devres *dr; - int rc; + int ret; + + ret = devm_add_action(&pdev->dev, pcim_disable_device, pdev); + if (ret != 0) + return ret; + + /* + * We prefer removing the action in case of an error over + * devm_add_action_or_reset() because the latter could theoretically be + * disturbed by users having pinned the device too soon. + */ + ret = pci_enable_device(pdev); + if (ret != 0) { + devm_remove_action(&pdev->dev, pcim_disable_device, pdev); + return ret; + } - dr = get_pci_dr(pdev); - if (unlikely(!dr)) - return -ENOMEM; - if (dr->enabled) - return 0; + pdev->is_managed = true; - rc = pci_enable_device(pdev); - if (!rc) { - pdev->is_managed = 1; - dr->enabled = 1; - } - return rc; + return ret; } EXPORT_SYMBOL(pcim_enable_device); @@ -237,36 +523,32 @@ EXPORT_SYMBOL(pcim_enable_device); * pcim_pin_device - Pin managed PCI device * @pdev: PCI device to pin * - * Pin managed PCI device @pdev. Pinned device won't be disabled on - * driver detach. @pdev must have been enabled with - * pcim_enable_device(). + * Pin managed PCI device @pdev. Pinned device won't be disabled on driver + * detach. @pdev must have been enabled with pcim_enable_device(). */ void pcim_pin_device(struct pci_dev *pdev) { - struct pci_devres *dr; - - dr = find_pci_dr(pdev); - WARN_ON(!dr || !dr->enabled); - if (dr) - dr->pinned = 1; + pdev->pinned = true; } EXPORT_SYMBOL(pcim_pin_device); static void pcim_iomap_release(struct device *gendev, void *res) { - struct pci_dev *dev = to_pci_dev(gendev); - struct pcim_iomap_devres *this = res; - int i; - - for (i = 0; i < PCIM_IOMAP_MAX; i++) - if (this->table[i]) - pci_iounmap(dev, this->table[i]); + /* + * Do nothing. This is legacy code. + * + * Cleanup of the mappings is now done directly through the callbacks + * registered when creating them. + */ } /** - * pcim_iomap_table - access iomap allocation table + * pcim_iomap_table - access iomap allocation table (DEPRECATED) * @pdev: PCI device to access iomap table for * + * Returns: + * Const pointer to array of __iomem pointers on success, NULL on failure. + * * Access iomap allocation table for @dev. If iomap table doesn't * exist and @pdev is managed, it will be allocated. All iomaps * recorded in the iomap table are automatically unmapped on driver @@ -275,6 +557,11 @@ static void pcim_iomap_release(struct device *gendev, void *res) * This function might sleep when the table is first allocated but can * be safely called without context and guaranteed to succeed once * allocated. + * + * This function is DEPRECATED. Do not use it in new code. Instead, obtain a + * mapping's address directly from one of the pcim_* mapping functions. For + * example: + * void __iomem \*mappy = pcim_iomap(pdev, bar, length); */ void __iomem * const *pcim_iomap_table(struct pci_dev *pdev) { @@ -293,27 +580,114 @@ void __iomem * const *pcim_iomap_table(struct pci_dev *pdev) } EXPORT_SYMBOL(pcim_iomap_table); +/* + * Fill the legacy mapping-table, so that drivers using the old API can + * still get a BAR's mapping address through pcim_iomap_table(). + */ +static int pcim_add_mapping_to_legacy_table(struct pci_dev *pdev, + void __iomem *mapping, int bar) +{ + void __iomem **legacy_iomap_table; + + if (bar >= PCI_STD_NUM_BARS) + return -EINVAL; + + legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); + if (!legacy_iomap_table) + return -ENOMEM; + + /* The legacy mechanism doesn't allow for duplicate mappings. */ + WARN_ON(legacy_iomap_table[bar]); + + legacy_iomap_table[bar] = mapping; + + return 0; +} + +/* + * Remove a mapping. The table only contains whole-BAR mappings, so this will + * never interfere with ranged mappings. + */ +static void pcim_remove_mapping_from_legacy_table(struct pci_dev *pdev, + void __iomem *addr) +{ + int bar; + void __iomem **legacy_iomap_table; + + legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); + if (!legacy_iomap_table) + return; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (legacy_iomap_table[bar] == addr) { + legacy_iomap_table[bar] = NULL; + return; + } + } +} + +/* + * The same as pcim_remove_mapping_from_legacy_table(), but identifies the + * mapping by its BAR index. + */ +static void pcim_remove_bar_from_legacy_table(struct pci_dev *pdev, int bar) +{ + void __iomem **legacy_iomap_table; + + if (bar >= PCI_STD_NUM_BARS) + return; + + legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); + if (!legacy_iomap_table) + return; + + legacy_iomap_table[bar] = NULL; +} + /** * pcim_iomap - Managed pcim_iomap() * @pdev: PCI device to iomap for * @bar: BAR to iomap * @maxlen: Maximum length of iomap * - * Managed pci_iomap(). Map is automatically unmapped on driver - * detach. + * Returns: __iomem pointer on success, NULL on failure. + * + * Managed pci_iomap(). Map is automatically unmapped on driver detach. If + * desired, unmap manually only with pcim_iounmap(). + * + * This SHOULD only be used once per BAR. + * + * NOTE: + * Contrary to the other pcim_* functions, this function does not return an + * IOMEM_ERR_PTR() on failure, but a simple NULL. This is done for backwards + * compatibility. */ void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen) { - void __iomem **tbl; - - BUG_ON(bar >= PCIM_IOMAP_MAX); + void __iomem *mapping; + struct pcim_addr_devres *res; - tbl = (void __iomem **)pcim_iomap_table(pdev); - if (!tbl || tbl[bar]) /* duplicate mappings not allowed */ + res = pcim_addr_devres_alloc(pdev); + if (!res) return NULL; + res->type = PCIM_ADDR_DEVRES_TYPE_MAPPING; - tbl[bar] = pci_iomap(pdev, bar, maxlen); - return tbl[bar]; + mapping = pci_iomap(pdev, bar, maxlen); + if (!mapping) + goto err_iomap; + res->baseaddr = mapping; + + if (pcim_add_mapping_to_legacy_table(pdev, mapping, bar) != 0) + goto err_table; + + devres_add(&pdev->dev, res); + return mapping; + +err_table: + pci_iounmap(pdev, mapping); +err_iomap: + pcim_addr_devres_free(res); + return NULL; } EXPORT_SYMBOL(pcim_iomap); @@ -322,102 +696,314 @@ EXPORT_SYMBOL(pcim_iomap); * @pdev: PCI device to iounmap for * @addr: Address to unmap * - * Managed pci_iounmap(). @addr must have been mapped using pcim_iomap(). + * Managed pci_iounmap(). @addr must have been mapped using a pcim_* mapping + * function. */ void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr) { - void __iomem **tbl; - int i; + struct pcim_addr_devres res_searched; - pci_iounmap(pdev, addr); + pcim_addr_devres_clear(&res_searched); + res_searched.type = PCIM_ADDR_DEVRES_TYPE_MAPPING; + res_searched.baseaddr = addr; - tbl = (void __iomem **)pcim_iomap_table(pdev); - BUG_ON(!tbl); + if (devres_release(&pdev->dev, pcim_addr_resource_release, + pcim_addr_resources_match, &res_searched) != 0) { + /* Doesn't exist. User passed nonsense. */ + return; + } - for (i = 0; i < PCIM_IOMAP_MAX; i++) - if (tbl[i] == addr) { - tbl[i] = NULL; - return; - } - WARN_ON(1); + pcim_remove_mapping_from_legacy_table(pdev, addr); } EXPORT_SYMBOL(pcim_iounmap); /** + * pcim_iomap_region - Request and iomap a PCI BAR + * @pdev: PCI device to map IO resources for + * @bar: Index of a BAR to map + * @name: Name associated with the request + * + * Returns: __iomem pointer on success, an IOMEM_ERR_PTR on failure. + * + * Mapping and region will get automatically released on driver detach. If + * desired, release manually only with pcim_iounmap_region(). + */ +static void __iomem *pcim_iomap_region(struct pci_dev *pdev, int bar, + const char *name) +{ + int ret; + struct pcim_addr_devres *res; + + res = pcim_addr_devres_alloc(pdev); + if (!res) + return IOMEM_ERR_PTR(-ENOMEM); + + res->type = PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING; + res->bar = bar; + + ret = __pcim_request_region(pdev, bar, name, 0); + if (ret != 0) + goto err_region; + + res->baseaddr = pci_iomap(pdev, bar, 0); + if (!res->baseaddr) { + ret = -EINVAL; + goto err_iomap; + } + + devres_add(&pdev->dev, res); + return res->baseaddr; + +err_iomap: + __pcim_release_region(pdev, bar); +err_region: + pcim_addr_devres_free(res); + + return IOMEM_ERR_PTR(ret); +} + +/** + * pcim_iounmap_region - Unmap and release a PCI BAR + * @pdev: PCI device to operate on + * @bar: Index of BAR to unmap and release + * + * Unmap a BAR and release its region manually. Only pass BARs that were + * previously mapped by pcim_iomap_region(). + */ +static void pcim_iounmap_region(struct pci_dev *pdev, int bar) +{ + struct pcim_addr_devres res_searched; + + pcim_addr_devres_clear(&res_searched); + res_searched.type = PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING; + res_searched.bar = bar; + + devres_release(&pdev->dev, pcim_addr_resource_release, + pcim_addr_resources_match, &res_searched); +} + +/** * pcim_iomap_regions - Request and iomap PCI BARs * @pdev: PCI device to map IO resources for * @mask: Mask of BARs to request and iomap - * @name: Name used when requesting regions + * @name: Name associated with the requests + * + * Returns: 0 on success, negative error code on failure. * * Request and iomap regions specified by @mask. */ int pcim_iomap_regions(struct pci_dev *pdev, int mask, const char *name) { - void __iomem * const *iomap; - int i, rc; + int ret; + int bar; + void __iomem *mapping; - iomap = pcim_iomap_table(pdev); - if (!iomap) - return -ENOMEM; + for (bar = 0; bar < DEVICE_COUNT_RESOURCE; bar++) { + if (!mask_contains_bar(mask, bar)) + continue; - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - unsigned long len; + mapping = pcim_iomap_region(pdev, bar, name); + if (IS_ERR(mapping)) { + ret = PTR_ERR(mapping); + goto err; + } + ret = pcim_add_mapping_to_legacy_table(pdev, mapping, bar); + if (ret != 0) + goto err; + } - if (!(mask & (1 << i))) - continue; + return 0; - rc = -EINVAL; - len = pci_resource_len(pdev, i); - if (!len) - goto err_inval; +err: + while (--bar >= 0) { + pcim_iounmap_region(pdev, bar); + pcim_remove_bar_from_legacy_table(pdev, bar); + } - rc = pci_request_region(pdev, i, name); - if (rc) - goto err_inval; + return ret; +} +EXPORT_SYMBOL(pcim_iomap_regions); - rc = -ENOMEM; - if (!pcim_iomap(pdev, i, 0)) - goto err_region; +static int _pcim_request_region(struct pci_dev *pdev, int bar, const char *name, + int request_flags) +{ + int ret; + struct pcim_addr_devres *res; + + res = pcim_addr_devres_alloc(pdev); + if (!res) + return -ENOMEM; + res->type = PCIM_ADDR_DEVRES_TYPE_REGION; + res->bar = bar; + + ret = __pcim_request_region(pdev, bar, name, request_flags); + if (ret != 0) { + pcim_addr_devres_free(res); + return ret; } + devres_add(&pdev->dev, res); return 0; +} - err_region: - pci_release_region(pdev, i); - err_inval: - while (--i >= 0) { - if (!(mask & (1 << i))) - continue; - pcim_iounmap(pdev, iomap[i]); - pci_release_region(pdev, i); +/** + * pcim_request_region - Request a PCI BAR + * @pdev: PCI device to requestion region for + * @bar: Index of BAR to request + * @name: Name associated with the request + * + * Returns: 0 on success, a negative error code on failure. + * + * Request region specified by @bar. + * + * The region will automatically be released on driver detach. If desired, + * release manually only with pcim_release_region(). + */ +int pcim_request_region(struct pci_dev *pdev, int bar, const char *name) +{ + return _pcim_request_region(pdev, bar, name, 0); +} + +/** + * pcim_request_region_exclusive - Request a PCI BAR exclusively + * @pdev: PCI device to requestion region for + * @bar: Index of BAR to request + * @name: Name associated with the request + * + * Returns: 0 on success, a negative error code on failure. + * + * Request region specified by @bar exclusively. + * + * The region will automatically be released on driver detach. If desired, + * release manually only with pcim_release_region(). + */ +int pcim_request_region_exclusive(struct pci_dev *pdev, int bar, const char *name) +{ + return _pcim_request_region(pdev, bar, name, IORESOURCE_EXCLUSIVE); +} + +/** + * pcim_release_region - Release a PCI BAR + * @pdev: PCI device to operate on + * @bar: Index of BAR to release + * + * Release a region manually that was previously requested by + * pcim_request_region(). + */ +void pcim_release_region(struct pci_dev *pdev, int bar) +{ + struct pcim_addr_devres res_searched; + + pcim_addr_devres_clear(&res_searched); + res_searched.type = PCIM_ADDR_DEVRES_TYPE_REGION; + res_searched.bar = bar; + + devres_release(&pdev->dev, pcim_addr_resource_release, + pcim_addr_resources_match, &res_searched); +} + + +/** + * pcim_release_all_regions - Release all regions of a PCI-device + * @pdev: the PCI device + * + * Release all regions previously requested through pcim_request_region() + * or pcim_request_all_regions(). + * + * Can be called from any context, i.e., not necessarily as a counterpart to + * pcim_request_all_regions(). + */ +static void pcim_release_all_regions(struct pci_dev *pdev) +{ + int bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) + pcim_release_region(pdev, bar); +} + +/** + * pcim_request_all_regions - Request all regions + * @pdev: PCI device to map IO resources for + * @name: name associated with the request + * + * Returns: 0 on success, negative error code on failure. + * + * Requested regions will automatically be released at driver detach. If + * desired, release individual regions with pcim_release_region() or all of + * them at once with pcim_release_all_regions(). + */ +static int pcim_request_all_regions(struct pci_dev *pdev, const char *name) +{ + int ret; + int bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + ret = pcim_request_region(pdev, bar, name); + if (ret != 0) + goto err; } - return rc; + return 0; + +err: + pcim_release_all_regions(pdev); + + return ret; } -EXPORT_SYMBOL(pcim_iomap_regions); /** * pcim_iomap_regions_request_all - Request all BARs and iomap specified ones + * (DEPRECATED) * @pdev: PCI device to map IO resources for * @mask: Mask of BARs to iomap - * @name: Name used when requesting regions + * @name: Name associated with the requests + * + * Returns: 0 on success, negative error code on failure. * * Request all PCI BARs and iomap regions specified by @mask. + * + * To release these resources manually, call pcim_release_region() for the + * regions and pcim_iounmap() for the mappings. + * + * This function is DEPRECATED. Don't use it in new code. Instead, use one + * of the pcim_* region request functions in combination with a pcim_* + * mapping function. */ int pcim_iomap_regions_request_all(struct pci_dev *pdev, int mask, const char *name) { - int request_mask = ((1 << 6) - 1) & ~mask; - int rc; + int bar; + int ret; + void __iomem **legacy_iomap_table; + + ret = pcim_request_all_regions(pdev, name); + if (ret != 0) + return ret; - rc = pci_request_selected_regions(pdev, request_mask, name); - if (rc) - return rc; + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (!mask_contains_bar(mask, bar)) + continue; + if (!pcim_iomap(pdev, bar, 0)) + goto err; + } + + return 0; + +err: + /* + * If bar is larger than 0, then pcim_iomap() above has most likely + * failed because of -EINVAL. If it is equal 0, most likely the table + * couldn't be created, indicating -ENOMEM. + */ + ret = bar > 0 ? -EINVAL : -ENOMEM; + legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev); - rc = pcim_iomap_regions(pdev, mask, name); - if (rc) - pci_release_selected_regions(pdev, request_mask); - return rc; + while (--bar >= 0) + pcim_iounmap(pdev, legacy_iomap_table[bar]); + + pcim_release_all_regions(pdev); + + return ret; } EXPORT_SYMBOL(pcim_iomap_regions_request_all); @@ -430,19 +1016,58 @@ EXPORT_SYMBOL(pcim_iomap_regions_request_all); */ void pcim_iounmap_regions(struct pci_dev *pdev, int mask) { - void __iomem * const *iomap; int i; - iomap = pcim_iomap_table(pdev); - if (!iomap) - return; - - for (i = 0; i < PCIM_IOMAP_MAX; i++) { - if (!(mask & (1 << i))) + for (i = 0; i < PCI_STD_NUM_BARS; i++) { + if (!mask_contains_bar(mask, i)) continue; - pcim_iounmap(pdev, iomap[i]); - pci_release_region(pdev, i); + pcim_iounmap_region(pdev, i); + pcim_remove_bar_from_legacy_table(pdev, i); } } EXPORT_SYMBOL(pcim_iounmap_regions); + +/** + * pcim_iomap_range - Create a ranged __iomap mapping within a PCI BAR + * @pdev: PCI device to map IO resources for + * @bar: Index of the BAR + * @offset: Offset from the begin of the BAR + * @len: Length in bytes for the mapping + * + * Returns: __iomem pointer on success, an IOMEM_ERR_PTR on failure. + * + * Creates a new IO-Mapping within the specified @bar, ranging from @offset to + * @offset + @len. + * + * The mapping will automatically get unmapped on driver detach. If desired, + * release manually only with pcim_iounmap(). + */ +void __iomem *pcim_iomap_range(struct pci_dev *pdev, int bar, + unsigned long offset, unsigned long len) +{ + void __iomem *mapping; + struct pcim_addr_devres *res; + + res = pcim_addr_devres_alloc(pdev); + if (!res) + return IOMEM_ERR_PTR(-ENOMEM); + + mapping = pci_iomap_range(pdev, bar, offset, len); + if (!mapping) { + pcim_addr_devres_free(res); + return IOMEM_ERR_PTR(-EINVAL); + } + + res->type = PCIM_ADDR_DEVRES_TYPE_MAPPING; + res->baseaddr = mapping; + + /* + * Ranged mappings don't get added to the legacy-table, since the table + * only ever keeps track of whole BARs. + */ + + devres_add(&pdev->dev, res); + return mapping; +} +EXPORT_SYMBOL(pcim_iomap_range); diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index 2c54d80107cf..5832989e55e8 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -716,7 +716,7 @@ static void pci_epf_mhi_dma_deinit(struct pci_epf_mhi *epf_mhi) epf_mhi->dma_chan_rx = NULL; } -static int pci_epf_mhi_core_init(struct pci_epf *epf) +static int pci_epf_mhi_epc_init(struct pci_epf *epf) { struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); const struct pci_epf_mhi_ep_info *info = epf_mhi->info; @@ -753,9 +753,35 @@ static int pci_epf_mhi_core_init(struct pci_epf *epf) if (!epf_mhi->epc_features) return -ENODATA; + if (info->flags & MHI_EPF_USE_DMA) { + ret = pci_epf_mhi_dma_init(epf_mhi); + if (ret) { + dev_err(dev, "Failed to initialize DMA: %d\n", ret); + return ret; + } + } + return 0; } +static void pci_epf_mhi_epc_deinit(struct pci_epf *epf) +{ + struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); + const struct pci_epf_mhi_ep_info *info = epf_mhi->info; + struct pci_epf_bar *epf_bar = &epf->bar[info->bar_num]; + struct mhi_ep_cntrl *mhi_cntrl = &epf_mhi->mhi_cntrl; + struct pci_epc *epc = epf->epc; + + if (mhi_cntrl->mhi_dev) { + mhi_ep_power_down(mhi_cntrl); + if (info->flags & MHI_EPF_USE_DMA) + pci_epf_mhi_dma_deinit(epf_mhi); + mhi_ep_unregister_controller(mhi_cntrl); + } + + pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, epf_bar); +} + static int pci_epf_mhi_link_up(struct pci_epf *epf) { struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); @@ -765,14 +791,6 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf) struct device *dev = &epf->dev; int ret; - if (info->flags & MHI_EPF_USE_DMA) { - ret = pci_epf_mhi_dma_init(epf_mhi); - if (ret) { - dev_err(dev, "Failed to initialize DMA: %d\n", ret); - return ret; - } - } - mhi_cntrl->mmio = epf_mhi->mmio; mhi_cntrl->irq = epf_mhi->irq; mhi_cntrl->mru = info->mru; @@ -819,7 +837,7 @@ static int pci_epf_mhi_link_down(struct pci_epf *epf) return 0; } -static int pci_epf_mhi_bme(struct pci_epf *epf) +static int pci_epf_mhi_bus_master_enable(struct pci_epf *epf) { struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); const struct pci_epf_mhi_ep_info *info = epf_mhi->info; @@ -882,8 +900,8 @@ static void pci_epf_mhi_unbind(struct pci_epf *epf) /* * Forcefully power down the MHI EP stack. Only way to bring the MHI EP - * stack back to working state after successive bind is by getting BME - * from host. + * stack back to working state after successive bind is by getting Bus + * Master Enable event from host. */ if (mhi_cntrl->mhi_dev) { mhi_ep_power_down(mhi_cntrl); @@ -897,10 +915,11 @@ static void pci_epf_mhi_unbind(struct pci_epf *epf) } static const struct pci_epc_event_ops pci_epf_mhi_event_ops = { - .core_init = pci_epf_mhi_core_init, + .epc_init = pci_epf_mhi_epc_init, + .epc_deinit = pci_epf_mhi_epc_deinit, .link_up = pci_epf_mhi_link_up, .link_down = pci_epf_mhi_link_down, - .bme = pci_epf_mhi_bme, + .bus_master_enable = pci_epf_mhi_bus_master_enable, }; static int pci_epf_mhi_probe(struct pci_epf *epf, diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 977fb79c1567..7c2ed6eae53a 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -686,25 +686,6 @@ reset_handler: msecs_to_jiffies(1)); } -static void pci_epf_test_unbind(struct pci_epf *epf) -{ - struct pci_epf_test *epf_test = epf_get_drvdata(epf); - struct pci_epc *epc = epf->epc; - int bar; - - cancel_delayed_work(&epf_test->cmd_handler); - pci_epf_test_clean_dma_chan(epf_test); - for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { - if (!epf_test->reg[bar]) - continue; - - pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, - &epf->bar[bar]); - pci_epf_free_space(epf, epf_test->reg[bar], bar, - PRIMARY_INTERFACE); - } -} - static int pci_epf_test_set_bar(struct pci_epf *epf) { int bar, ret; @@ -731,23 +712,36 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) return 0; } -static int pci_epf_test_core_init(struct pci_epf *epf) +static void pci_epf_test_clear_bar(struct pci_epf *epf) +{ + struct pci_epf_test *epf_test = epf_get_drvdata(epf); + struct pci_epc *epc = epf->epc; + int bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (!epf_test->reg[bar]) + continue; + + pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, + &epf->bar[bar]); + } +} + +static int pci_epf_test_epc_init(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epf_header *header = epf->header; - const struct pci_epc_features *epc_features; + const struct pci_epc_features *epc_features = epf_test->epc_features; struct pci_epc *epc = epf->epc; struct device *dev = &epf->dev; bool linkup_notifier = false; - bool msix_capable = false; - bool msi_capable = true; int ret; - epc_features = pci_epc_get_features(epc, epf->func_no, epf->vfunc_no); - if (epc_features) { - msix_capable = epc_features->msix_capable; - msi_capable = epc_features->msi_capable; - } + epf_test->dma_supported = true; + + ret = pci_epf_test_init_dma_chan(epf_test); + if (ret) + epf_test->dma_supported = false; if (epf->vfunc_no <= 1) { ret = pci_epc_write_header(epc, epf->func_no, epf->vfunc_no, header); @@ -761,7 +755,7 @@ static int pci_epf_test_core_init(struct pci_epf *epf) if (ret) return ret; - if (msi_capable) { + if (epc_features->msi_capable) { ret = pci_epc_set_msi(epc, epf->func_no, epf->vfunc_no, epf->msi_interrupts); if (ret) { @@ -770,7 +764,7 @@ static int pci_epf_test_core_init(struct pci_epf *epf) } } - if (msix_capable) { + if (epc_features->msix_capable) { ret = pci_epc_set_msix(epc, epf->func_no, epf->vfunc_no, epf->msix_interrupts, epf_test->test_reg_bar, @@ -788,6 +782,15 @@ static int pci_epf_test_core_init(struct pci_epf *epf) return 0; } +static void pci_epf_test_epc_deinit(struct pci_epf *epf) +{ + struct pci_epf_test *epf_test = epf_get_drvdata(epf); + + cancel_delayed_work(&epf_test->cmd_handler); + pci_epf_test_clean_dma_chan(epf_test); + pci_epf_test_clear_bar(epf); +} + static int pci_epf_test_link_up(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); @@ -798,9 +801,20 @@ static int pci_epf_test_link_up(struct pci_epf *epf) return 0; } +static int pci_epf_test_link_down(struct pci_epf *epf) +{ + struct pci_epf_test *epf_test = epf_get_drvdata(epf); + + cancel_delayed_work_sync(&epf_test->cmd_handler); + + return 0; +} + static const struct pci_epc_event_ops pci_epf_test_event_ops = { - .core_init = pci_epf_test_core_init, + .epc_init = pci_epf_test_epc_init, + .epc_deinit = pci_epf_test_epc_deinit, .link_up = pci_epf_test_link_up, + .link_down = pci_epf_test_link_down, }; static int pci_epf_test_alloc_space(struct pci_epf *epf) @@ -810,19 +824,15 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) size_t msix_table_size = 0; size_t test_reg_bar_size; size_t pba_size = 0; - bool msix_capable; void *base; enum pci_barno test_reg_bar = epf_test->test_reg_bar; enum pci_barno bar; - const struct pci_epc_features *epc_features; + const struct pci_epc_features *epc_features = epf_test->epc_features; size_t test_reg_size; - epc_features = epf_test->epc_features; - test_reg_bar_size = ALIGN(sizeof(struct pci_epf_test_reg), 128); - msix_capable = epc_features->msix_capable; - if (msix_capable) { + if (epc_features->msix_capable) { msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts; epf_test->msix_table_offset = test_reg_bar_size; /* Align to QWORD or 8 Bytes */ @@ -857,6 +867,20 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) return 0; } +static void pci_epf_test_free_space(struct pci_epf *epf) +{ + struct pci_epf_test *epf_test = epf_get_drvdata(epf); + int bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (!epf_test->reg[bar]) + continue; + + pci_epf_free_space(epf, epf_test->reg[bar], bar, + PRIMARY_INTERFACE); + } +} + static int pci_epf_test_bind(struct pci_epf *epf) { int ret; @@ -885,13 +909,20 @@ static int pci_epf_test_bind(struct pci_epf *epf) if (ret) return ret; - epf_test->dma_supported = true; + return 0; +} - ret = pci_epf_test_init_dma_chan(epf_test); - if (ret) - epf_test->dma_supported = false; +static void pci_epf_test_unbind(struct pci_epf *epf) +{ + struct pci_epf_test *epf_test = epf_get_drvdata(epf); + struct pci_epc *epc = epf->epc; - return 0; + cancel_delayed_work(&epf_test->cmd_handler); + if (epc->init_complete) { + pci_epf_test_clean_dma_chan(epf_test); + pci_epf_test_clear_bar(epf); + } + pci_epf_test_free_space(epf); } static const struct pci_epf_device_id pci_epf_test_ids[] = { diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 8e779eecd62d..874cb097b093 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -799,8 +799,9 @@ err_config_interrupt: */ static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) { - epf_ntb_db_bar_clear(ntb); epf_ntb_mw_bar_clear(ntb, ntb->num_mws); + epf_ntb_db_bar_clear(ntb); + epf_ntb_config_sspad_bar_clear(ntb); } #define EPF_NTB_R(_name) \ @@ -1018,8 +1019,10 @@ static int vpci_scan_bus(void *sysdata) struct epf_ntb *ndev = sysdata; vpci_bus = pci_scan_bus(ndev->vbus_number, &vpci_ops, sysdata); - if (vpci_bus) - pr_err("create pci bus\n"); + if (!vpci_bus) { + pr_err("create pci bus failed\n"); + return -EINVAL; + } pci_bus_add_devices(vpci_bus); @@ -1335,13 +1338,19 @@ static int epf_ntb_bind(struct pci_epf *epf) ret = pci_register_driver(&vntb_pci_driver); if (ret) { dev_err(dev, "failure register vntb pci driver\n"); - goto err_bar_alloc; + goto err_epc_cleanup; } - vpci_scan_bus(ntb); + ret = vpci_scan_bus(ntb); + if (ret) + goto err_unregister; return 0; +err_unregister: + pci_unregister_driver(&vntb_pci_driver); +err_epc_cleanup: + epf_ntb_epc_cleanup(ntb); err_bar_alloc: epf_ntb_config_spad_bar_free(ntb); diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 3b21e28f9b59..d712c7a866d2 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -23,7 +23,6 @@ struct pci_epf_group { struct config_group group; struct config_group primary_epc_group; struct config_group secondary_epc_group; - struct config_group *type_group; struct delayed_work cfs_work; struct pci_epf *epf; int index; diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 47d27ec7439d..84309dfe0c68 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -14,7 +14,9 @@ #include <linux/pci-epf.h> #include <linux/pci-ep-cfs.h> -static struct class *pci_epc_class; +static const struct class pci_epc_class = { + .name = "pci_epc", +}; static void devm_pci_epc_release(struct device *dev, void *res) { @@ -60,7 +62,7 @@ struct pci_epc *pci_epc_get(const char *epc_name) struct device *dev; struct class_dev_iter iter; - class_dev_iter_init(&iter, pci_epc_class, NULL, NULL); + class_dev_iter_init(&iter, &pci_epc_class, NULL, NULL); while ((dev = class_dev_iter_next(&iter))) { if (strcmp(epc_name, dev_name(dev))) continue; @@ -727,9 +729,9 @@ void pci_epc_linkdown(struct pci_epc *epc) EXPORT_SYMBOL_GPL(pci_epc_linkdown); /** - * pci_epc_init_notify() - Notify the EPF device that EPC device's core - * initialization is completed. - * @epc: the EPC device whose core initialization is completed + * pci_epc_init_notify() - Notify the EPF device that EPC device initialization + * is completed. + * @epc: the EPC device whose initialization is completed * * Invoke to Notify the EPF device that the EPC device's initialization * is completed. @@ -744,8 +746,8 @@ void pci_epc_init_notify(struct pci_epc *epc) mutex_lock(&epc->list_lock); list_for_each_entry(epf, &epc->pci_epf, list) { mutex_lock(&epf->lock); - if (epf->event_ops && epf->event_ops->core_init) - epf->event_ops->core_init(epf); + if (epf->event_ops && epf->event_ops->epc_init) + epf->event_ops->epc_init(epf); mutex_unlock(&epf->lock); } epc->init_complete = true; @@ -756,7 +758,7 @@ EXPORT_SYMBOL_GPL(pci_epc_init_notify); /** * pci_epc_notify_pending_init() - Notify the pending EPC device initialization * complete to the EPF device - * @epc: the EPC device whose core initialization is pending to be notified + * @epc: the EPC device whose initialization is pending to be notified * @epf: the EPF device to be notified * * Invoke to notify the pending EPC device initialization complete to the EPF @@ -767,22 +769,20 @@ void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf) { if (epc->init_complete) { mutex_lock(&epf->lock); - if (epf->event_ops && epf->event_ops->core_init) - epf->event_ops->core_init(epf); + if (epf->event_ops && epf->event_ops->epc_init) + epf->event_ops->epc_init(epf); mutex_unlock(&epf->lock); } } EXPORT_SYMBOL_GPL(pci_epc_notify_pending_init); /** - * pci_epc_bme_notify() - Notify the EPF device that the EPC device has received - * the BME event from the Root complex - * @epc: the EPC device that received the BME event + * pci_epc_deinit_notify() - Notify the EPF device about EPC deinitialization + * @epc: the EPC device whose deinitialization is completed * - * Invoke to Notify the EPF device that the EPC device has received the Bus - * Master Enable (BME) event from the Root complex + * Invoke to notify the EPF device that the EPC deinitialization is completed. */ -void pci_epc_bme_notify(struct pci_epc *epc) +void pci_epc_deinit_notify(struct pci_epc *epc) { struct pci_epf *epf; @@ -792,13 +792,41 @@ void pci_epc_bme_notify(struct pci_epc *epc) mutex_lock(&epc->list_lock); list_for_each_entry(epf, &epc->pci_epf, list) { mutex_lock(&epf->lock); - if (epf->event_ops && epf->event_ops->bme) - epf->event_ops->bme(epf); + if (epf->event_ops && epf->event_ops->epc_deinit) + epf->event_ops->epc_deinit(epf); mutex_unlock(&epf->lock); } + epc->init_complete = false; mutex_unlock(&epc->list_lock); } -EXPORT_SYMBOL_GPL(pci_epc_bme_notify); +EXPORT_SYMBOL_GPL(pci_epc_deinit_notify); + +/** + * pci_epc_bus_master_enable_notify() - Notify the EPF device that the EPC + * device has received the Bus Master + * Enable event from the Root complex + * @epc: the EPC device that received the Bus Master Enable event + * + * Notify the EPF device that the EPC device has generated the Bus Master Enable + * event due to host setting the Bus Master Enable bit in the Command register. + */ +void pci_epc_bus_master_enable_notify(struct pci_epc *epc) +{ + struct pci_epf *epf; + + if (IS_ERR_OR_NULL(epc)) + return; + + mutex_lock(&epc->list_lock); + list_for_each_entry(epf, &epc->pci_epf, list) { + mutex_lock(&epf->lock); + if (epf->event_ops && epf->event_ops->bus_master_enable) + epf->event_ops->bus_master_enable(epf); + mutex_unlock(&epf->lock); + } + mutex_unlock(&epc->list_lock); +} +EXPORT_SYMBOL_GPL(pci_epc_bus_master_enable_notify); /** * pci_epc_destroy() - destroy the EPC device @@ -867,7 +895,7 @@ __pci_epc_create(struct device *dev, const struct pci_epc_ops *ops, INIT_LIST_HEAD(&epc->pci_epf); device_initialize(&epc->dev); - epc->dev.class = pci_epc_class; + epc->dev.class = &pci_epc_class; epc->dev.parent = dev; epc->dev.release = pci_epc_release; epc->ops = ops; @@ -927,20 +955,13 @@ EXPORT_SYMBOL_GPL(__devm_pci_epc_create); static int __init pci_epc_init(void) { - pci_epc_class = class_create("pci_epc"); - if (IS_ERR(pci_epc_class)) { - pr_err("failed to create pci epc class --> %ld\n", - PTR_ERR(pci_epc_class)); - return PTR_ERR(pci_epc_class); - } - - return 0; + return class_register(&pci_epc_class); } module_init(pci_epc_init); static void __exit pci_epc_exit(void) { - class_destroy(pci_epc_class); + class_unregister(&pci_epc_class); } module_exit(pci_epc_exit); diff --git a/drivers/pci/hotplug/acpiphp_ampere_altra.c b/drivers/pci/hotplug/acpiphp_ampere_altra.c index 3fddd04851b6..f5c9e741c1d4 100644 --- a/drivers/pci/hotplug/acpiphp_ampere_altra.c +++ b/drivers/pci/hotplug/acpiphp_ampere_altra.c @@ -124,4 +124,5 @@ static struct platform_driver altra_led_driver = { module_platform_driver(altra_led_driver); MODULE_AUTHOR("D Scott Phillips <scott@os.amperecomputing.com>"); +MODULE_DESCRIPTION("ACPI PCI Hot Plug Extension for Ampere Altra"); MODULE_LICENSE("GPL"); diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index e0a614acee05..273dd8c66f4e 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -46,6 +46,9 @@ extern int pciehp_poll_time; /** * struct controller - PCIe hotplug controller * @pcie: pointer to the controller's PCIe port service device + * @dsn: cached copy of Device Serial Number of Function 0 in the hotplug slot + * (PCIe r6.2 sec 7.9.3); used to determine whether a hotplugged device + * was replaced with a different one during system sleep * @slot_cap: cached copy of the Slot Capabilities register * @inband_presence_disabled: In-Band Presence Detect Disable supported by * controller and disabled per spec recommendation (PCIe r5.0, appendix I @@ -87,6 +90,7 @@ extern int pciehp_poll_time; */ struct controller { struct pcie_device *pcie; + u64 dsn; u32 slot_cap; /* capabilities and quirks */ unsigned int inband_presence_disabled:1; diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index ddd55ad97a58..ff458e692fed 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -284,6 +284,32 @@ static int pciehp_suspend(struct pcie_device *dev) return 0; } +static bool pciehp_device_replaced(struct controller *ctrl) +{ + struct pci_dev *pdev __free(pci_dev_put); + u32 reg; + + pdev = pci_get_slot(ctrl->pcie->port->subordinate, PCI_DEVFN(0, 0)); + if (!pdev) + return true; + + if (pci_read_config_dword(pdev, PCI_VENDOR_ID, ®) || + reg != (pdev->vendor | (pdev->device << 16)) || + pci_read_config_dword(pdev, PCI_CLASS_REVISION, ®) || + reg != (pdev->revision | (pdev->class << 8))) + return true; + + if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL && + (pci_read_config_dword(pdev, PCI_SUBSYSTEM_VENDOR_ID, ®) || + reg != (pdev->subsystem_vendor | (pdev->subsystem_device << 16)))) + return true; + + if (pci_get_dsn(pdev) != ctrl->dsn) + return true; + + return false; +} + static int pciehp_resume_noirq(struct pcie_device *dev) { struct controller *ctrl = get_service_data(dev); @@ -293,9 +319,23 @@ static int pciehp_resume_noirq(struct pcie_device *dev) ctrl->cmd_busy = true; /* clear spurious events from rediscovery of inserted card */ - if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) + if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) { pcie_clear_hotplug_events(ctrl); + /* + * If hotplugged device was replaced with a different one + * during system sleep, mark the old device disconnected + * (to prevent its driver from accessing the new device) + * and synthesize a Presence Detect Changed event. + */ + if (pciehp_device_replaced(ctrl)) { + ctrl_dbg(ctrl, "device replaced during system sleep\n"); + pci_walk_bus(ctrl->pcie->port->subordinate, + pci_dev_set_disconnected, NULL); + pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); + } + } + return 0; } #endif diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b1d0a1b3917d..061f01f60db4 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -1055,6 +1055,11 @@ struct controller *pcie_init(struct pcie_device *dev) } } + pdev = pci_get_slot(subordinate, PCI_DEVFN(0, 0)); + if (pdev) + ctrl->dsn = pci_get_dsn(pdev); + pci_dev_put(pdev); + return ctrl; } diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index ad12515a4a12..65e50bee1a8c 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -72,6 +72,10 @@ int pciehp_configure_device(struct controller *ctrl) pci_bus_add_devices(parent); down_read_nested(&ctrl->reset_lock, ctrl->depth); + dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); + ctrl->dsn = pci_get_dsn(dev); + pci_dev_put(dev); + out: pci_unlock_rescan_remove(); return ret; diff --git a/drivers/pci/iomap.c b/drivers/pci/iomap.c index c9725428e387..a715a4803c95 100644 --- a/drivers/pci/iomap.c +++ b/drivers/pci/iomap.c @@ -23,6 +23,10 @@ * * @maxlen specifies the maximum length to map. If you want to get access to * the complete BAR from offset to the end, pass %0 here. + * + * NOTE: + * This function is never managed, even if you initialized with + * pcim_enable_device(). * */ void __iomem *pci_iomap_range(struct pci_dev *dev, int bar, @@ -63,6 +67,10 @@ EXPORT_SYMBOL(pci_iomap_range); * * @maxlen specifies the maximum length to map. If you want to get access to * the complete BAR from offset to the end, pass %0 here. + * + * NOTE: + * This function is never managed, even if you initialized with + * pcim_enable_device(). * */ void __iomem *pci_iomap_wc_range(struct pci_dev *dev, int bar, @@ -106,6 +114,10 @@ EXPORT_SYMBOL_GPL(pci_iomap_wc_range); * * @maxlen specifies the maximum length to map. If you want to get access to * the complete BAR without checking for its length first, pass %0 here. + * + * NOTE: + * This function is never managed, even if you initialized with + * pcim_enable_device(). If you need automatic cleanup, use pcim_iomap(). * */ void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) { @@ -127,6 +139,10 @@ EXPORT_SYMBOL(pci_iomap); * * @maxlen specifies the maximum length to map. If you want to get access to * the complete BAR without checking for its length first, pass %0 here. + * + * NOTE: + * This function is never managed, even if you initialized with + * pcim_enable_device(). * */ void __iomem *pci_iomap_wc(struct pci_dev *dev, int bar, unsigned long maxlen) { diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 51e3dd0ea5ab..48ba95e4ab05 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -234,27 +234,61 @@ int of_get_pci_domain_nr(struct device_node *node) EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); /** - * of_pci_check_probe_only - Setup probe only mode if linux,pci-probe-only - * is present and valid + * of_pci_preserve_config - Return true if the boot configuration needs to + * be preserved + * @node: Device tree node. + * + * Look for "linux,pci-probe-only" property for a given PCI controller's + * node and return true if found. Also look in the chosen node if the + * property is not found in the given controller's node. Having this + * property ensures that the kernel doesn't reconfigure the BARs and bridge + * windows that are already done by the platform firmware. + * + * Return: true if the property exists; false otherwise. */ -void of_pci_check_probe_only(void) +bool of_pci_preserve_config(struct device_node *node) { - u32 val; + u32 val = 0; int ret; - ret = of_property_read_u32(of_chosen, "linux,pci-probe-only", &val); + if (!node) { + pr_warn("device node is NULL, trying with of_chosen\n"); + node = of_chosen; + } + +retry: + ret = of_property_read_u32(node, "linux,pci-probe-only", &val); if (ret) { - if (ret == -ENODATA || ret == -EOVERFLOW) - pr_warn("linux,pci-probe-only without valid value, ignoring\n"); - return; + if (ret == -ENODATA || ret == -EOVERFLOW) { + pr_warn("Incorrect value for linux,pci-probe-only in %pOF, ignoring\n", + node); + return false; + } + if (ret == -EINVAL) { + if (node == of_chosen) + return false; + + node = of_chosen; + goto retry; + } } if (val) + return true; + else + return false; +} + +/** + * of_pci_check_probe_only - Setup probe only mode if linux,pci-probe-only + * is present and valid + */ +void of_pci_check_probe_only(void) +{ + if (of_pci_preserve_config(of_chosen)) pci_add_flags(PCI_PROBE_ONLY); else pci_clear_flags(PCI_PROBE_ONLY); - - pr_info("PROBE_ONLY %s\n", val ? "enabled" : "disabled"); } EXPORT_SYMBOL_GPL(of_pci_check_probe_only); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 004575091596..9cc447da9475 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -119,6 +119,28 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) return (phys_addr_t)mcfg_addr; } +bool pci_acpi_preserve_config(struct pci_host_bridge *host_bridge) +{ + if (ACPI_HANDLE(&host_bridge->dev)) { + union acpi_object *obj; + + /* + * Evaluate the "PCI Boot Configuration" _DSM Function. If it + * exists and returns 0, we must preserve any PCI resource + * assignments made by firmware for this host bridge. + */ + obj = acpi_evaluate_dsm_typed(ACPI_HANDLE(&host_bridge->dev), + &pci_acpi_dsm_guid, + 1, DSM_PCI_PRESERVE_BOOT_CONFIG, + NULL, ACPI_TYPE_INTEGER); + if (obj && obj->integer.value == 0) + return true; + ACPI_FREE(obj); + } + + return false; +} + /* _HPX PCI Setting Record (Type 0); same as _HPP */ struct hpx_type0 { u32 revision; /* Not present in _HPP */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 59e0949fb079..8cf0941f70d1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -946,30 +946,67 @@ void pci_request_acs(void) } static const char *disable_acs_redir_param; +static const char *config_acs_param; -/** - * pci_disable_acs_redir - disable ACS redirect capabilities - * @dev: the PCI device - * - * For only devices specified in the disable_acs_redir parameter. - */ -static void pci_disable_acs_redir(struct pci_dev *dev) +struct pci_acs { + u16 cap; + u16 ctrl; + u16 fw_ctrl; +}; + +static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps, + const char *p, u16 mask, u16 flags) { + char *delimit; int ret = 0; - const char *p; - int pos; - u16 ctrl; - if (!disable_acs_redir_param) + if (!p) return; - p = disable_acs_redir_param; while (*p) { + if (!mask) { + /* Check for ACS flags */ + delimit = strstr(p, "@"); + if (delimit) { + int end; + u32 shift = 0; + + end = delimit - p - 1; + + while (end > -1) { + if (*(p + end) == '0') { + mask |= 1 << shift; + shift++; + end--; + } else if (*(p + end) == '1') { + mask |= 1 << shift; + flags |= 1 << shift; + shift++; + end--; + } else if ((*(p + end) == 'x') || (*(p + end) == 'X')) { + shift++; + end--; + } else { + pci_err(dev, "Invalid ACS flags... Ignoring\n"); + return; + } + } + p = delimit + 1; + } else { + pci_err(dev, "ACS Flags missing\n"); + return; + } + } + + if (mask & ~(PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR | PCI_ACS_CR | + PCI_ACS_UF | PCI_ACS_EC | PCI_ACS_DT)) { + pci_err(dev, "Invalid ACS flags specified\n"); + return; + } + ret = pci_dev_str_match(dev, p, &p); if (ret < 0) { - pr_info_once("PCI: Can't parse disable_acs_redir parameter: %s\n", - disable_acs_redir_param); - + pr_info_once("PCI: Can't parse ACS command line parameter\n"); break; } else if (ret == 1) { /* Found a match */ @@ -989,56 +1026,38 @@ static void pci_disable_acs_redir(struct pci_dev *dev) if (!pci_dev_specific_disable_acs_redir(dev)) return; - pos = dev->acs_cap; - if (!pos) { - pci_warn(dev, "cannot disable ACS redirect for this hardware as it does not have ACS capabilities\n"); - return; - } + pci_dbg(dev, "ACS mask = %#06x\n", mask); + pci_dbg(dev, "ACS flags = %#06x\n", flags); - pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); + /* If mask is 0 then we copy the bit from the firmware setting. */ + caps->ctrl = (caps->ctrl & ~mask) | (caps->fw_ctrl & mask); + caps->ctrl |= flags; - /* P2P Request & Completion Redirect */ - ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC); - - pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); - - pci_info(dev, "disabled ACS redirect\n"); + pci_info(dev, "Configured ACS to %#06x\n", caps->ctrl); } /** * pci_std_enable_acs - enable ACS on devices using standard ACS capabilities * @dev: the PCI device + * @caps: default ACS controls */ -static void pci_std_enable_acs(struct pci_dev *dev) +static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps) { - int pos; - u16 cap; - u16 ctrl; - - pos = dev->acs_cap; - if (!pos) - return; - - pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); - pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); - /* Source Validation */ - ctrl |= (cap & PCI_ACS_SV); + caps->ctrl |= (caps->cap & PCI_ACS_SV); /* P2P Request Redirect */ - ctrl |= (cap & PCI_ACS_RR); + caps->ctrl |= (caps->cap & PCI_ACS_RR); /* P2P Completion Redirect */ - ctrl |= (cap & PCI_ACS_CR); + caps->ctrl |= (caps->cap & PCI_ACS_CR); /* Upstream Forwarding */ - ctrl |= (cap & PCI_ACS_UF); + caps->ctrl |= (caps->cap & PCI_ACS_UF); /* Enable Translation Blocking for external devices and noats */ if (pci_ats_disabled() || dev->external_facing || dev->untrusted) - ctrl |= (cap & PCI_ACS_TB); - - pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); + caps->ctrl |= (caps->cap & PCI_ACS_TB); } /** @@ -1047,23 +1066,33 @@ static void pci_std_enable_acs(struct pci_dev *dev) */ static void pci_enable_acs(struct pci_dev *dev) { - if (!pci_acs_enable) - goto disable_acs_redir; + struct pci_acs caps; + int pos; + + pos = dev->acs_cap; + if (!pos) + return; - if (!pci_dev_specific_enable_acs(dev)) - goto disable_acs_redir; + pci_read_config_word(dev, pos + PCI_ACS_CAP, &caps.cap); + pci_read_config_word(dev, pos + PCI_ACS_CTRL, &caps.ctrl); + caps.fw_ctrl = caps.ctrl; - pci_std_enable_acs(dev); + /* If an iommu is present we start with kernel default caps */ + if (pci_acs_enable) { + if (pci_dev_specific_enable_acs(dev)) + pci_std_enable_acs(dev, &caps); + } -disable_acs_redir: /* - * Note: pci_disable_acs_redir() must be called even if ACS was not - * enabled by the kernel because it may have been enabled by - * platform firmware. So if we are told to disable it, we should - * always disable it after setting the kernel's default - * preferences. + * Always apply caps from the command line, even if there is no iommu. + * Trust that the admin has a reason to change the ACS settings. */ - pci_disable_acs_redir(dev); + __pci_config_acs(dev, &caps, disable_acs_redir_param, + PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC, + ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC)); + __pci_config_acs(dev, &caps, config_acs_param, 0, 0); + + pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl); } /** @@ -2218,12 +2247,6 @@ void pci_disable_enabled_device(struct pci_dev *dev) */ void pci_disable_device(struct pci_dev *dev) { - struct pci_devres *dr; - - dr = find_pci_dr(dev); - if (dr) - dr->enabled = 0; - dev_WARN_ONCE(&dev->dev, atomic_read(&dev->enable_cnt) <= 0, "disabling already-disabled device"); @@ -3872,7 +3895,15 @@ EXPORT_SYMBOL(pci_enable_atomic_ops_to_root); */ void pci_release_region(struct pci_dev *pdev, int bar) { - struct pci_devres *dr; + /* + * This is done for backwards compatibility, because the old PCI devres + * API had a mode in which the function became managed if it had been + * enabled with pcim_enable_device() instead of pci_enable_device(). + */ + if (pci_is_managed(pdev)) { + pcim_release_region(pdev, bar); + return; + } if (pci_resource_len(pdev, bar) == 0) return; @@ -3882,10 +3913,6 @@ void pci_release_region(struct pci_dev *pdev, int bar) else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) release_mem_region(pci_resource_start(pdev, bar), pci_resource_len(pdev, bar)); - - dr = find_pci_dr(pdev); - if (dr) - dr->region_mask &= ~(1 << bar); } EXPORT_SYMBOL(pci_release_region); @@ -3896,6 +3923,8 @@ EXPORT_SYMBOL(pci_release_region); * @res_name: Name to be associated with resource. * @exclusive: whether the region access is exclusive or not * + * Returns: 0 on success, negative error code on failure. + * * Mark the PCI region associated with PCI device @pdev BAR @bar as * being reserved by owner @res_name. Do not access any * address inside the PCI regions unless this call returns @@ -3911,7 +3940,12 @@ EXPORT_SYMBOL(pci_release_region); static int __pci_request_region(struct pci_dev *pdev, int bar, const char *res_name, int exclusive) { - struct pci_devres *dr; + if (pci_is_managed(pdev)) { + if (exclusive == IORESOURCE_EXCLUSIVE) + return pcim_request_region_exclusive(pdev, bar, res_name); + + return pcim_request_region(pdev, bar, res_name); + } if (pci_resource_len(pdev, bar) == 0) return 0; @@ -3927,10 +3961,6 @@ static int __pci_request_region(struct pci_dev *pdev, int bar, goto err_out; } - dr = find_pci_dr(pdev); - if (dr) - dr->region_mask |= 1 << bar; - return 0; err_out: @@ -3945,6 +3975,8 @@ err_out: * @bar: BAR to be reserved * @res_name: Name to be associated with resource * + * Returns: 0 on success, negative error code on failure. + * * Mark the PCI region associated with PCI device @pdev BAR @bar as * being reserved by owner @res_name. Do not access any * address inside the PCI regions unless this call returns @@ -3952,6 +3984,11 @@ err_out: * * Returns 0 on success, or %EBUSY on error. A warning * message is also printed on failure. + * + * NOTE: + * This is a "hybrid" function: It's normally unmanaged, but becomes managed + * when pcim_enable_device() has been called in advance. This hybrid feature is + * DEPRECATED! If you want managed cleanup, use the pcim_* functions instead. */ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) { @@ -4002,6 +4039,13 @@ err_out: * @pdev: PCI device whose resources are to be reserved * @bars: Bitmask of BARs to be requested * @res_name: Name to be associated with resource + * + * Returns: 0 on success, negative error code on failure. + * + * NOTE: + * This is a "hybrid" function: It's normally unmanaged, but becomes managed + * when pcim_enable_device() has been called in advance. This hybrid feature is + * DEPRECATED! If you want managed cleanup, use the pcim_* functions instead. */ int pci_request_selected_regions(struct pci_dev *pdev, int bars, const char *res_name) @@ -4010,6 +4054,19 @@ int pci_request_selected_regions(struct pci_dev *pdev, int bars, } EXPORT_SYMBOL(pci_request_selected_regions); +/** + * pci_request_selected_regions_exclusive - Request regions exclusively + * @pdev: PCI device to request regions from + * @bars: bit mask of BARs to request + * @res_name: name to be associated with the requests + * + * Returns: 0 on success, negative error code on failure. + * + * NOTE: + * This is a "hybrid" function: It's normally unmanaged, but becomes managed + * when pcim_enable_device() has been called in advance. This hybrid feature is + * DEPRECATED! If you want managed cleanup, use the pcim_* functions instead. + */ int pci_request_selected_regions_exclusive(struct pci_dev *pdev, int bars, const char *res_name) { @@ -4027,7 +4084,6 @@ EXPORT_SYMBOL(pci_request_selected_regions_exclusive); * successful call to pci_request_regions(). Call this function only * after all use of the PCI regions has ceased. */ - void pci_release_regions(struct pci_dev *pdev) { pci_release_selected_regions(pdev, (1 << PCI_STD_NUM_BARS) - 1); @@ -4046,6 +4102,11 @@ EXPORT_SYMBOL(pci_release_regions); * * Returns 0 on success, or %EBUSY on error. A warning * message is also printed on failure. + * + * NOTE: + * This is a "hybrid" function: It's normally unmanaged, but becomes managed + * when pcim_enable_device() has been called in advance. This hybrid feature is + * DEPRECATED! If you want managed cleanup, use the pcim_* functions instead. */ int pci_request_regions(struct pci_dev *pdev, const char *res_name) { @@ -4059,6 +4120,8 @@ EXPORT_SYMBOL(pci_request_regions); * @pdev: PCI device whose resources are to be reserved * @res_name: Name to be associated with resource. * + * Returns: 0 on success, negative error code on failure. + * * Mark all PCI regions associated with PCI device @pdev as being reserved * by owner @res_name. Do not access any address inside the PCI regions * unless this call returns successfully. @@ -4068,6 +4131,11 @@ EXPORT_SYMBOL(pci_request_regions); * * Returns 0 on success, or %EBUSY on error. A warning message is also * printed on failure. + * + * NOTE: + * This is a "hybrid" function: It's normally unmanaged, but becomes managed + * when pcim_enable_device() has been called in advance. This hybrid feature is + * DEPRECATED! If you want managed cleanup, use the pcim_* functions instead. */ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) { @@ -4399,11 +4467,22 @@ void pci_disable_parity(struct pci_dev *dev) * @enable: boolean: whether to enable or disable PCI INTx * * Enables/disables PCI INTx for device @pdev + * + * NOTE: + * This is a "hybrid" function: It's normally unmanaged, but becomes managed + * when pcim_enable_device() has been called in advance. This hybrid feature is + * DEPRECATED! If you want managed cleanup, use pcim_intx() instead. */ void pci_intx(struct pci_dev *pdev, int enable) { u16 pci_command, new; + /* Preserve the "hybrid" behavior for backwards compatibility */ + if (pci_is_managed(pdev)) { + WARN_ON_ONCE(pcim_intx(pdev, enable) != 0); + return; + } + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); if (enable) @@ -4411,17 +4490,8 @@ void pci_intx(struct pci_dev *pdev, int enable) else new = pci_command | PCI_COMMAND_INTX_DISABLE; - if (new != pci_command) { - struct pci_devres *dr; - + if (new != pci_command) pci_write_config_word(pdev, PCI_COMMAND, new); - - dr = find_pci_dr(pdev); - if (dr && !dr->restore_intx) { - dr->restore_intx = 1; - dr->orig_intx = !enable; - } - } } EXPORT_SYMBOL_GPL(pci_intx); @@ -4753,7 +4823,7 @@ static int pci_bus_max_d3cold_delay(const struct pci_bus *bus) */ int pci_bridge_wait_for_secondary_bus(struct pci_dev *dev, char *reset_type) { - struct pci_dev *child; + struct pci_dev *child __free(pci_dev_put) = NULL; int delay; if (pci_dev_is_disconnected(dev)) @@ -4782,8 +4852,8 @@ int pci_bridge_wait_for_secondary_bus(struct pci_dev *dev, char *reset_type) return 0; } - child = list_first_entry(&dev->subordinate->devices, struct pci_dev, - bus_list); + child = pci_dev_get(list_first_entry(&dev->subordinate->devices, + struct pci_dev, bus_list)); up_read(&pci_bus_sem); /* @@ -4884,6 +4954,9 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) int pci_bridge_secondary_bus_reset(struct pci_dev *dev) { lock_map_assert_held(&dev->cfg_access_lock); + if (!dev->block_cfg_access) + pci_warn_once(dev, "unlocked secondary bus reset via: %pS\n", + __builtin_return_address(0)); pcibios_reset_secondary_bus(dev); return pci_bridge_wait_for_secondary_bus(dev, "bus reset"); @@ -5442,10 +5515,12 @@ static void pci_bus_lock(struct pci_bus *bus) { struct pci_dev *dev; + pci_dev_lock(bus->self); list_for_each_entry(dev, &bus->devices, bus_list) { - pci_dev_lock(dev); if (dev->subordinate) pci_bus_lock(dev->subordinate); + else + pci_dev_lock(dev); } } @@ -5457,8 +5532,10 @@ static void pci_bus_unlock(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->subordinate) pci_bus_unlock(dev->subordinate); - pci_dev_unlock(dev); + else + pci_dev_unlock(dev); } + pci_dev_unlock(bus->self); } /* Return 1 on successful lock, 0 on contention */ @@ -5466,15 +5543,15 @@ static int pci_bus_trylock(struct pci_bus *bus) { struct pci_dev *dev; + if (!pci_dev_trylock(bus->self)) + return 0; + 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); + if (!pci_bus_trylock(dev->subordinate)) goto unlock; - } - } + } else if (!pci_dev_trylock(dev)) + goto unlock; } return 1; @@ -5482,8 +5559,10 @@ unlock: list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) { if (dev->subordinate) pci_bus_unlock(dev->subordinate); - pci_dev_unlock(dev); + else + pci_dev_unlock(dev); } + pci_dev_unlock(bus->self); return 0; } @@ -5515,9 +5594,10 @@ static void pci_slot_lock(struct pci_slot *slot) list_for_each_entry(dev, &slot->bus->devices, bus_list) { if (!dev->slot || dev->slot != slot) continue; - pci_dev_lock(dev); if (dev->subordinate) pci_bus_lock(dev->subordinate); + else + pci_dev_lock(dev); } } @@ -5543,14 +5623,13 @@ static int pci_slot_trylock(struct pci_slot *slot) 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; } - } + } else if (!pci_dev_trylock(dev)) + goto unlock; } return 1; @@ -5561,7 +5640,8 @@ unlock: continue; if (dev->subordinate) pci_bus_unlock(dev->subordinate); - pci_dev_unlock(dev); + else + pci_dev_unlock(dev); } return 0; } @@ -6840,6 +6920,8 @@ static int __init pci_setup(char *str) pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS); } else if (!strncmp(str, "disable_acs_redir=", 18)) { disable_acs_redir_param = str + 18; + } else if (!strncmp(str, "config_acs=", 11)) { + config_acs_param = str + 11; } else { pr_err("PCI: Unknown option `%s'\n", str); } @@ -6864,6 +6946,7 @@ static int __init pci_realloc_setup_params(void) resource_alignment_param = kstrdup(resource_alignment_param, GFP_KERNEL); disable_acs_redir_param = kstrdup(disable_acs_redir_param, GFP_KERNEL); + config_acs_param = kstrdup(config_acs_param, GFP_KERNEL); return 0; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fd44565c4756..089ed4029a86 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -22,6 +22,27 @@ */ #define PCIE_PME_TO_L2_TIMEOUT_US 10000 +/* Message Routing (r[2:0]); PCIe r6.0, sec 2.2.8 */ +#define PCIE_MSG_TYPE_R_RC 0 +#define PCIE_MSG_TYPE_R_ADDR 1 +#define PCIE_MSG_TYPE_R_ID 2 +#define PCIE_MSG_TYPE_R_BC 3 +#define PCIE_MSG_TYPE_R_LOCAL 4 +#define PCIE_MSG_TYPE_R_GATHER 5 + +/* Power Management Messages; PCIe r6.0, sec 2.2.8.2 */ +#define PCIE_MSG_CODE_PME_TURN_OFF 0x19 + +/* INTx Mechanism Messages; PCIe r6.0, sec 2.2.8.1 */ +#define PCIE_MSG_CODE_ASSERT_INTA 0x20 +#define PCIE_MSG_CODE_ASSERT_INTB 0x21 +#define PCIE_MSG_CODE_ASSERT_INTC 0x22 +#define PCIE_MSG_CODE_ASSERT_INTD 0x23 +#define PCIE_MSG_CODE_DEASSERT_INTA 0x24 +#define PCIE_MSG_CODE_DEASSERT_INTB 0x25 +#define PCIE_MSG_CODE_DEASSERT_INTC 0x26 +#define PCIE_MSG_CODE_DEASSERT_INTD 0x27 + extern const unsigned char pcie_link_speed[]; extern bool pci_early_dump; @@ -648,6 +669,7 @@ int of_pci_get_max_link_speed(struct device_node *node); u32 of_pci_get_slot_power_limit(struct device_node *node, u8 *slot_power_limit_value, u8 *slot_power_limit_scale); +bool of_pci_preserve_config(struct device_node *node); int pci_set_of_node(struct pci_dev *dev); void pci_release_of_node(struct pci_dev *dev); void pci_set_bus_of_node(struct pci_bus *bus); @@ -686,6 +708,11 @@ of_pci_get_slot_power_limit(struct device_node *node, return 0; } +static inline bool of_pci_preserve_config(struct device_node *node) +{ + return false; +} + static inline int pci_set_of_node(struct pci_dev *dev) { return 0; } static inline void pci_release_of_node(struct pci_dev *dev) { } static inline void pci_set_bus_of_node(struct pci_bus *bus) { } @@ -732,6 +759,7 @@ static inline void pci_restore_aer_state(struct pci_dev *dev) { } #endif #ifdef CONFIG_ACPI +bool pci_acpi_preserve_config(struct pci_host_bridge *bridge); int pci_acpi_program_hp_params(struct pci_dev *dev); extern const struct attribute_group pci_dev_acpi_attr_group; void pci_set_acpi_fwnode(struct pci_dev *dev); @@ -745,6 +773,10 @@ int acpi_pci_wakeup(struct pci_dev *dev, bool enable); bool acpi_pci_need_resume(struct pci_dev *dev); pci_power_t acpi_pci_choose_state(struct pci_dev *pdev); #else +static inline bool pci_acpi_preserve_config(struct pci_host_bridge *bridge) +{ + return false; +} static inline int pci_dev_acpi_reset(struct pci_dev *dev, bool probe) { return -ENOTTY; @@ -810,26 +842,12 @@ static inline pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) } #endif -/* - * Managed PCI resources. This manages device on/off, INTx/MSI/MSI-X - * on/off and BAR regions. pci_dev itself records MSI/MSI-X status, so - * there's no need to track it separately. pci_devres is initialized - * when a device is enabled using managed PCI device enable interface. - * - * TODO: Struct pci_devres and find_pci_dr() only need to be here because - * they're used in pci.c. Port or move these functions to devres.c and - * then remove them from here. - */ -struct pci_devres { - unsigned int enabled:1; - unsigned int pinned:1; - unsigned int orig_intx:1; - unsigned int restore_intx:1; - unsigned int mwi:1; - u32 region_mask; -}; +int pcim_intx(struct pci_dev *dev, int enable); -struct pci_devres *find_pci_dr(struct pci_dev *pdev); +int pcim_request_region(struct pci_dev *pdev, int bar, const char *name); +int pcim_request_region_exclusive(struct pci_dev *pdev, int bar, + const char *name); +void pcim_release_region(struct pci_dev *pdev, int bar); /* * Config Address for PCI Configuration Mechanism #1 diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index ac6293c24976..13b8586924ea 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1497,6 +1497,22 @@ static int aer_probe(struct pcie_device *dev) return 0; } +static int aer_suspend(struct pcie_device *dev) +{ + struct aer_rpc *rpc = get_service_data(dev); + + aer_disable_rootport(rpc); + return 0; +} + +static int aer_resume(struct pcie_device *dev) +{ + struct aer_rpc *rpc = get_service_data(dev); + + aer_enable_rootport(rpc); + return 0; +} + /** * aer_root_reset - reset Root Port hierarchy, RCEC, or RCiEP * @dev: pointer to Root Port, RCEC, or RCiEP @@ -1561,6 +1577,8 @@ static struct pcie_port_service_driver aerdriver = { .service = PCIE_PORT_SERVICE_AER, .probe = aer_probe, + .suspend = aer_suspend, + .resume = aer_resume, .remove = aer_remove, }; diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index a668820696dc..2b6ef7efa3c1 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -412,13 +412,44 @@ void pci_dpc_init(struct pci_dev *pdev) } } +static void dpc_enable(struct pcie_device *dev) +{ + struct pci_dev *pdev = dev->port; + int dpc = pdev->dpc_cap; + u16 ctl; + + /* + * Clear DPC Interrupt Status so we don't get an interrupt for an + * old event when setting DPC Interrupt Enable. + */ + pci_write_config_word(pdev, dpc + PCI_EXP_DPC_STATUS, + PCI_EXP_DPC_STATUS_INTERRUPT); + + pci_read_config_word(pdev, dpc + PCI_EXP_DPC_CTL, &ctl); + ctl &= ~PCI_EXP_DPC_CTL_EN_MASK; + ctl |= PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN; + pci_write_config_word(pdev, dpc + PCI_EXP_DPC_CTL, ctl); +} + +static void dpc_disable(struct pcie_device *dev) +{ + struct pci_dev *pdev = dev->port; + int dpc = pdev->dpc_cap; + u16 ctl; + + /* Disable DPC triggering and DPC interrupts */ + pci_read_config_word(pdev, dpc + PCI_EXP_DPC_CTL, &ctl); + ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN); + pci_write_config_word(pdev, dpc + PCI_EXP_DPC_CTL, ctl); +} + #define FLAG(x, y) (((x) & (y)) ? '+' : '-') static int dpc_probe(struct pcie_device *dev) { struct pci_dev *pdev = dev->port; struct device *device = &dev->device; int status; - u16 ctl, cap; + u16 cap; if (!pcie_aer_is_native(pdev) && !pcie_ports_dpc_native) return -ENOTSUPP; @@ -433,11 +464,7 @@ static int dpc_probe(struct pcie_device *dev) } pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CAP, &cap); - - pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl); - ctl &= ~PCI_EXP_DPC_CTL_EN_MASK; - ctl |= PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN; - pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl); + dpc_enable(dev); pci_info(pdev, "enabled with IRQ %d\n", dev->irq); pci_info(pdev, "error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n", @@ -450,14 +477,21 @@ static int dpc_probe(struct pcie_device *dev) return status; } -static void dpc_remove(struct pcie_device *dev) +static int dpc_suspend(struct pcie_device *dev) { - struct pci_dev *pdev = dev->port; - u16 ctl; + dpc_disable(dev); + return 0; +} - pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl); - ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN); - pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl); +static int dpc_resume(struct pcie_device *dev) +{ + dpc_enable(dev); + return 0; +} + +static void dpc_remove(struct pcie_device *dev) +{ + dpc_disable(dev); } static struct pcie_port_service_driver dpcdriver = { @@ -465,6 +499,8 @@ static struct pcie_port_service_driver dpcdriver = { .port_type = PCIE_ANY_PORT, .service = PCIE_PORT_SERVICE_DPC, .probe = dpc_probe, + .suspend = dpc_suspend, + .resume = dpc_resume, .remove = dpc_remove, }; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 8e696e547565..20475ca30505 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -889,6 +889,17 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus) dev_set_msi_domain(&bus->dev, d); } +static bool pci_preserve_config(struct pci_host_bridge *host_bridge) +{ + if (pci_acpi_preserve_config(host_bridge)) + return true; + + if (host_bridge->dev.parent && host_bridge->dev.parent->of_node) + return of_pci_preserve_config(host_bridge->dev.parent->of_node); + + return false; +} + static int pci_register_host_bridge(struct pci_host_bridge *bridge) { struct device *parent = bridge->dev.parent; @@ -983,6 +994,9 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) if (nr_node_ids > 1 && pcibus_to_node(bus) == NUMA_NO_NODE) dev_warn(&bus->dev, "Unknown NUMA node; performance will be reduced\n"); + /* Check if the boot configuration by FW needs to be preserved */ + bridge->preserve_config = pci_preserve_config(bridge); + /* Coalesce contiguous windows */ resource_list_for_each_entry_safe(window, n, &resources) { if (list_is_last(&window->node, &resources)) @@ -3080,20 +3094,18 @@ int pci_host_probe(struct pci_host_bridge *bridge) bus = bridge->bus; + /* If we must preserve the resource configuration, claim now */ + if (bridge->preserve_config) + pci_bus_claim_resources(bus); + /* - * We insert PCI resources into the iomem_resource and - * ioport_resource trees in either pci_bus_claim_resources() - * or pci_bus_assign_resources(). + * Assign whatever was left unassigned. If we didn't claim above, + * this will reassign everything. */ - if (pci_has_flag(PCI_PROBE_ONLY)) { - pci_bus_claim_resources(bus); - } else { - pci_bus_size_bridges(bus); - pci_bus_assign_resources(bus); + pci_assign_unassigned_root_bus_resources(bus); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - } + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); pci_bus_add_devices(bus); return 0; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 568410e64ce6..a2ce4e08edf5 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5099,6 +5099,10 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_BROADCOM, 0x1750, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_BROADCOM, 0x1751, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_BROADCOM, 0x1752, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_BROADCOM, 0x1760, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_BROADCOM, 0x1761, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_BROADCOM, 0x1762, pci_quirk_mf_endpoint_acs }, + { PCI_VENDOR_ID_BROADCOM, 0x1763, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_BROADCOM, 0xD714, pci_quirk_brcm_acs }, /* Amazon Annapurna Labs */ { PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS, 0x0031, pci_quirk_al_acs }, diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 909e6a7c3cc3..23082bc0ca37 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -14,6 +14,7 @@ * tighter packing. Prefetchable range support. */ +#include <linux/bitops.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> @@ -21,6 +22,8 @@ #include <linux/errno.h> #include <linux/ioport.h> #include <linux/cache.h> +#include <linux/limits.h> +#include <linux/sizes.h> #include <linux/slab.h> #include <linux/acpi.h> #include "pci.h" @@ -829,11 +832,9 @@ static resource_size_t calculate_memsize(resource_size_t size, size = min_size; if (old_size == 1) old_size = 0; - if (size < old_size) - size = old_size; - size = ALIGN(max(size, add_size) + children_add_size, align); - return size; + size = max(size, add_size) + children_add_size; + return ALIGN(max(size, old_size), align); } resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, @@ -959,7 +960,7 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, for (order = 0; order <= max_order; order++) { resource_size_t align1 = 1; - align1 <<= (order + 20); + align1 <<= order + __ffs(SZ_1M); if (!align) min_align = align1; @@ -972,6 +973,67 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, } /** + * pbus_upstream_space_available - Check no upstream resource limits allocation + * @bus: The bus + * @mask: Mask the resource flag, then compare it with type + * @type: The type of resource from bridge + * @size: The size required from the bridge window + * @align: Required alignment for the resource + * + * Checks that @size can fit inside the upstream bridge resources that are + * already assigned. + * + * Return: %true if enough space is available on all assigned upstream + * resources. + */ +static bool pbus_upstream_space_available(struct pci_bus *bus, unsigned long mask, + unsigned long type, resource_size_t size, + resource_size_t align) +{ + struct resource_constraint constraint = { + .max = RESOURCE_SIZE_MAX, + .align = align, + }; + struct pci_bus *downstream = bus; + struct resource *r; + + while ((bus = bus->parent)) { + if (pci_is_root_bus(bus)) + break; + + pci_bus_for_each_resource(bus, r) { + if (!r || !r->parent || (r->flags & mask) != type) + continue; + + if (resource_size(r) >= size) { + struct resource gap = {}; + + if (find_resource_space(r, &gap, size, &constraint) == 0) { + gap.flags = type; + pci_dbg(bus->self, + "Assigned bridge window %pR to %pR free space at %pR\n", + r, &bus->busn_res, &gap); + return true; + } + } + + if (bus->self) { + pci_info(bus->self, + "Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n", + r, &bus->busn_res, + (unsigned long long)size, + pci_name(downstream->self), + &downstream->busn_res); + } + + return false; + } + } + + return true; +} + +/** * pbus_size_mem() - Size the memory window of a given bus * * @bus: The bus @@ -997,7 +1059,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, struct list_head *realloc_head) { struct pci_dev *dev; - resource_size_t min_align, align, size, size0, size1; + resource_size_t min_align, win_align, align, size, size0, size1; resource_size_t aligns[24]; /* Alignments from 1MB to 8TB */ int order, max_order; struct resource *b_res = find_bus_resource_of_type(bus, @@ -1049,7 +1111,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, * resources. */ align = pci_resource_alignment(dev, r); - order = __ffs(align) - 20; + order = __ffs(align) - __ffs(SZ_1M); if (order < 0) order = 0; if (order >= ARRAY_SIZE(aligns)) { @@ -1076,10 +1138,23 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, } } + win_align = window_alignment(bus, b_res->flags); min_align = calculate_mem_align(aligns, max_order); - min_align = max(min_align, window_alignment(bus, b_res->flags)); + min_align = max(min_align, win_align); size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align); add_align = max(min_align, add_align); + + if (bus->self && size0 && + !pbus_upstream_space_available(bus, mask | IORESOURCE_PREFETCH, type, + size0, add_align)) { + min_align = 1ULL << (max_order + __ffs(SZ_1M)); + min_align = max(min_align, win_align); + size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), win_align); + add_align = win_align; + pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n", + b_res, &bus->busn_res); + } + size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 : calculate_memsize(size, min_size, add_size, children_add_size, resource_size(b_res), add_align); |