diff options
Diffstat (limited to 'drivers/pci')
53 files changed, 1515 insertions, 492 deletions
diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig index b277c5f6e196..69efd7582369 100644 --- a/drivers/pci/controller/cadence/Kconfig +++ b/drivers/pci/controller/cadence/Kconfig @@ -29,11 +29,12 @@ config PCIE_CADENCE_EP config PCIE_CADENCE_PLAT tristate + select PCIE_CADENCE_HOST if PCIE_CADENCE_PLAT_HOST != n + select PCIE_CADENCE_EP if PCIE_CADENCE_PLAT_EP != n config PCIE_CADENCE_PLAT_HOST tristate "Cadence platform PCIe controller (host mode)" depends on OF - select PCIE_CADENCE_HOST select PCIE_CADENCE_PLAT help Say Y here if you want to support the Cadence PCIe platform controller in @@ -44,7 +45,6 @@ config PCIE_CADENCE_PLAT_EP tristate "Cadence platform PCIe controller (endpoint mode)" depends on OF depends on PCI_ENDPOINT - select PCIE_CADENCE_EP select PCIE_CADENCE_PLAT help Say Y here if you want to support the Cadence PCIe platform controller in diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.c b/drivers/pci/controller/cadence/pcie-cadence-host-common.c index 18e4b6c760b5..0ef4396151b4 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host-common.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c @@ -16,6 +16,8 @@ #include "pcie-cadence-host-common.h" #include "../pci-host-common.h" +#include "../../pci.h" + #define LINK_RETRAIN_TIMEOUT HZ u64 bar_max_size[] = { @@ -54,12 +56,12 @@ int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie, int retries; /* Check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) { if (pcie_link_up(pcie)) { dev_info(dev, "Link up\n"); return 0; } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + msleep(PCIE_LINK_WAIT_SLEEP_MS); } return -ETIMEDOUT; diff --git a/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h b/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h index 857b2140c5d2..15dc4fcaf45d 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h +++ b/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h @@ -10,11 +10,6 @@ #include <linux/bitfield.h> -/* 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 - /* Local Management Registers */ #define CDNS_PCIE_LM_BASE 0x00100000 diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 3260d916110b..dcfbe7e229fd 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -376,6 +376,7 @@ config PCIE_ROCKCHIP_DW_HOST depends on OF select PCIE_DW_HOST select PCIE_ROCKCHIP_DW + select PCI_HOST_COMMON help Enables support for the DesignWare PCIe controller in the Rockchip SoC (except RK3399) to work in host mode. diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 3fc889944f02..6ae5b27e27b3 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -373,12 +373,14 @@ static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = { .init = dra7xx_pcie_host_init, }; -static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) +static int dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); dra7xx_pcie_enable_wrapper_interrupts(dra7xx); + + return 0; } static void dra7xx_pcie_raise_intx_irq(struct dra7xx_pcie *dra7xx) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index e25f938eefe2..39790e66b98d 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -681,21 +681,12 @@ static int imx_pcie_attach_pd(struct device *dev) static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { - if (enable) { - /* power up core phy and enable ref clock */ - regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - /* - * The async reset input need ref clock to sync internally, - * when the ref clock comes after reset, internal synced - * reset time is too short, cannot meet the requirement. - * Add a ~10us delay here. - */ - usleep_range(10, 100); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); - } else { - regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - } + if (enable) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN); return 0; } @@ -826,8 +817,16 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) { + if (assert) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_SW_RST, assert ? IMX6Q_GPR1_PCIE_SW_RST : 0); + if (!assert) usleep_range(200, 500); @@ -836,11 +835,15 @@ static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) static int imx6q_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) { - if (!assert) - return 0; + if (assert) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); + if (!assert) + usleep_range(200, 500); return 0; } @@ -1451,6 +1454,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) return 0; err_phy_off: + imx_pcie_assert_core_reset(imx_pcie); phy_power_off(imx_pcie->phy); err_phy_exit: phy_exit(imx_pcie->phy); @@ -1470,6 +1474,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct imx_pcie *imx_pcie = to_imx_pcie(pci); + imx_pcie_assert_core_reset(imx_pcie); if (imx_pcie->phy) { if (phy_power_off(imx_pcie->phy)) dev_err(pci->dev, "unable to power off PHY\n"); diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index f1b27aed488d..602516239a57 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -876,7 +876,7 @@ static const struct dw_pcie_ops ks_pcie_dw_pcie_ops = { .write_dbi2 = ks_pcie_am654_write_dbi2, }; -static void ks_pcie_am654_ep_init(struct dw_pcie_ep *ep) +static int ks_pcie_am654_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); int flags; @@ -885,6 +885,8 @@ static void ks_pcie_am654_ep_init(struct dw_pcie_ep *ep) flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32; dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, APP_ADDR_SPACE_0 - 1); dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, flags); + + return 0; } static void ks_pcie_am654_raise_intx_irq(struct keystone_pcie *ks_pcie) diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index 8936975ff104..527c57114dcf 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -147,7 +147,7 @@ ls_pcie_ep_get_features(struct dw_pcie_ep *ep) return pcie->ls_epc; } -static void ls_pcie_ep_init(struct dw_pcie_ep *ep) +static int ls_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci); @@ -155,10 +155,12 @@ static void ls_pcie_ep_init(struct dw_pcie_ep *ep) ep_func = dw_pcie_ep_get_func_from_ep(ep, 0); if (!ep_func) - return; + return -ENODEV; pcie->ls_epc->msi_capable = ep_func->msi_cap ? true : false; pcie->ls_epc->msix_capable = ep_func->msix_cap ? true : false; + + return 0; } static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 71b0ebc0e472..8559d132dcde 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -397,7 +397,7 @@ static int meson_pcie_probe(struct platform_device *pdev) mp->phy = devm_phy_get(dev, "pcie"); if (IS_ERR(mp->phy)) { - dev_err(dev, "get phy failed, %ld\n", PTR_ERR(mp->phy)); + dev_err(dev, "get phy failed, %pe\n", mp->phy); return PTR_ERR(mp->phy); } diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 5cd227dda9a1..7f0ba506dd1c 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -335,7 +335,7 @@ static const struct dw_pcie_host_ops artpec6_pcie_host_ops = { .init = artpec6_pcie_host_init, }; -static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) +static int artpec6_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); @@ -344,6 +344,8 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) artpec6_pcie_init_phy(artpec6_pcie); artpec6_pcie_deassert_core_reset(artpec6_pcie); artpec6_pcie_wait_for_phy(artpec6_pcie); + + return 0; } static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 7d2794945704..de8ee3db4360 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -1032,6 +1032,8 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, * there is no unified way to check if we have operations in * flight, thus we don't know if we should WARN() or not. */ + /* flush posted write before unmap */ + readl(ep->msi_mem + ep->msi_iatu_mapped_offset); dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys); ep->msi_iatu_mapped = false; } @@ -1044,6 +1046,7 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, return ret; ep->msi_iatu_mapped = true; + ep->msi_iatu_mapped_offset = offset; ep->msi_msg_addr = msg_addr; ep->msi_map_size = map_size; } @@ -1124,6 +1127,17 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, return -EPERM; } + /* + * ep->msi_iatu_mapped means that an MSI target address is cached, + * unmap it first so that we can reuse ep->msi_mem_phys for MSI-X. + */ + if (ep->msi_iatu_mapped) { + /* flush posted write before unmap */ + readl(ep->msi_mem + ep->msi_iatu_mapped_offset); + dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys); + ep->msi_iatu_mapped = false; + } + msg_addr = dw_pcie_ep_align_addr(epc, msg_addr, &map_size, &offset); ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, map_size); @@ -1153,6 +1167,11 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + if (ep->msi_iatu_mapped) { + dw_pcie_ep_unmap_addr(ep->epc, 0, 0, ep->msi_mem_phys); + ep->msi_iatu_mapped = false; + } + dwc_pcie_debugfs_deinit(pci); dw_pcie_edma_remove(pci); } @@ -1367,8 +1386,11 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) list_add_tail(&ep_func->list, &ep->func_list); } - if (ep->ops->init) - ep->ops->init(ep); + if (ep->ops->init) { + ret = ep->ops->init(ep); + if (ret) + goto err_remove_edma; + } dw_pcie_ep_disable_bars(ep); @@ -1521,8 +1543,11 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) if (ret) return ret; - if (ep->ops->pre_init) - ep->ops->pre_init(ep); + if (ep->ops->pre_init) { + ret = ep->ops->pre_init(ep); + if (ret) + return ret; + } ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size, ep->page_size); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index de4b245b1758..0735ae940924 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -474,8 +474,8 @@ struct dw_pcie_rp { }; struct dw_pcie_ep_ops { - void (*pre_init)(struct dw_pcie_ep *ep); - void (*init)(struct dw_pcie_ep *ep); + int (*pre_init)(struct dw_pcie_ep *ep); + int (*init)(struct dw_pcie_ep *ep); int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, unsigned int type, u16 interrupt_num); const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); @@ -518,6 +518,7 @@ struct dw_pcie_ep { /* MSI outbound iATU state */ bool msi_iatu_mapped; + size_t msi_iatu_mapped_offset; u64 msi_msg_addr; size_t msi_map_size; }; diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 731d93663cca..af26a07c0c9e 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -26,6 +26,7 @@ #include <trace/events/pci_controller.h> #include "../../pci.h" +#include "../pci-host-common.h" #include "pcie-designware.h" /* @@ -122,6 +123,9 @@ struct rockchip_pcie_of_data { const struct pci_epc_features *epc_features; }; +static int rockchip_pcie_rc_reset_root_port(struct pci_host_bridge *bridge, + struct pci_dev *pdev); + static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg) { return readl_relaxed(rockchip->apb_base + reg); @@ -436,6 +440,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) rockchip_pcie_configure_l1ss(pci); rockchip_pcie_enable_l0s(pci); + pp->bridge->reset_root_port = rockchip_pcie_rc_reset_root_port; /* Disable Root Ports BAR0 and BAR1 as they report bogus size */ dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0); @@ -469,12 +474,14 @@ static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep) dw_pcie_remove_ext_capability(pci, PCI_EXT_CAP_ID_ATS); } -static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep) +static int rockchip_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); rockchip_pcie_enable_l0s(pci); rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep); + + return 0; }; static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, @@ -634,6 +641,32 @@ static const struct dw_pcie_ops dw_pcie_ops = { .get_ltssm = rockchip_pcie_get_ltssm, }; +static irqreturn_t rockchip_pcie_rc_sys_irq_thread(int irq, void *arg) +{ + struct rockchip_pcie *rockchip = arg; + struct dw_pcie *pci = &rockchip->pci; + struct dw_pcie_rp *pp = &pci->pp; + struct device *dev = pci->dev; + struct pci_dev *port; + u32 reg; + + reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC); + rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC); + + dev_dbg(dev, "PCIE_CLIENT_INTR_STATUS_MISC: %#x\n", reg); + dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm_reg(rockchip)); + + if (reg & PCIE_LINK_REQ_RST_NOT_INT) { + dev_dbg(dev, "hot reset or link-down reset\n"); + for_each_pci_bridge(port, pp->bridge->bus) { + if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT) + pci_host_handle_link_down(port); + } + } + + return IRQ_HANDLED; +} + static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) { struct rockchip_pcie *rockchip = arg; @@ -666,14 +699,29 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) return IRQ_HANDLED; } -static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip) +static int rockchip_pcie_configure_rc(struct platform_device *pdev, + struct rockchip_pcie *rockchip) { + struct device *dev = &pdev->dev; struct dw_pcie_rp *pp; + int irq, ret; u32 val; if (!IS_ENABLED(CONFIG_PCIE_ROCKCHIP_DW_HOST)) return -ENODEV; + irq = platform_get_irq_byname(pdev, "sys"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + rockchip_pcie_rc_sys_irq_thread, + IRQF_ONESHOT, "pcie-sys-rc", rockchip); + if (ret) { + dev_err(dev, "failed to request PCIe sys IRQ\n"); + return ret; + } + /* LTSSM enable control mode */ val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1); rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL); @@ -685,7 +733,17 @@ static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip) pp = &rockchip->pci.pp; pp->ops = &rockchip_pcie_host_ops; - return dw_pcie_host_init(pp); + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + return ret; + } + + /* unmask hot reset/link-down reset */ + val = FIELD_PREP_WM16(PCIE_LINK_REQ_RST_NOT_INT, 0); + rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC); + + return ret; } static int rockchip_pcie_configure_ep(struct platform_device *pdev, @@ -804,7 +862,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) switch (data->mode) { case DW_PCIE_RC_TYPE: - ret = rockchip_pcie_configure_rc(rockchip); + ret = rockchip_pcie_configure_rc(pdev, rockchip); if (ret) goto deinit_clk; break; @@ -829,6 +887,78 @@ deinit_phy: return ret; } +static int rockchip_pcie_rc_reset_root_port(struct pci_host_bridge *bridge, + struct pci_dev *pdev) +{ + struct pci_bus *bus = bridge->bus; + struct dw_pcie_rp *pp = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); + struct device *dev = rockchip->pci.dev; + u32 val; + int ret; + + dw_pcie_stop_link(pci); + clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks); + rockchip_pcie_phy_deinit(rockchip); + + ret = reset_control_assert(rockchip->rst); + if (ret) + return ret; + + ret = rockchip_pcie_phy_init(rockchip); + if (ret) + return ret; + + ret = reset_control_deassert(rockchip->rst); + if (ret) + goto deinit_phy; + + ret = rockchip_pcie_clk_init(rockchip); + if (ret) + goto deinit_phy; + + ret = pp->ops->init(pp); + if (ret) { + dev_err(dev, "Host init failed: %d\n", ret); + goto deinit_clk; + } + + /* LTSSM enable control mode */ + val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1); + rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL); + + rockchip_pcie_writel_apb(rockchip, + PCIE_CLIENT_SET_MODE(PCIE_CLIENT_MODE_RC), + PCIE_CLIENT_GENERAL_CON); + + ret = dw_pcie_setup_rc(pp); + if (ret) { + dev_err(dev, "Failed to setup RC: %d\n", ret); + goto deinit_clk; + } + + /* unmask hot reset/link-down reset */ + val = FIELD_PREP_WM16(PCIE_LINK_REQ_RST_NOT_INT, 0); + rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC); + + ret = dw_pcie_start_link(pci); + if (ret) + goto deinit_clk; + + /* Ignore errors, the link may come up later */ + dw_pcie_wait_for_link(pci); + dev_dbg(dev, "Root Port reset completed\n"); + return ret; + +deinit_clk: + clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks); +deinit_phy: + rockchip_pcie_phy_deinit(rockchip); + + return ret; +} + static const struct rockchip_pcie_of_data rockchip_pcie_rc_of_data_rk3568 = { .mode = DW_PCIE_RC_TYPE, }; diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 42fb5f24a223..b11e66c10fff 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -278,12 +278,14 @@ static int keembay_pcie_setup_msi_irq(struct keembay_pcie *pcie) return 0; } -static void keembay_pcie_ep_init(struct dw_pcie_ep *ep) +static int keembay_pcie_ep_init(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct keembay_pcie *pcie = dev_get_drvdata(pci->dev); writel(EDMA_INT_EN, pcie->apb_base + PCIE_REGS_INTERRUPT_ENABLE); + + return 0; } static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index b193c989b2b8..b58a607b713f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -56,6 +56,10 @@ #define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8 #define PARF_Q2A_FLUSH 0x1ac #define PARF_LTSSM 0x1b0 +#define PARF_INT_ALL_STATUS 0x224 +#define PARF_INT_ALL_CLEAR 0x228 +#define PARF_INT_ALL_MASK 0x22c +#define PARF_STATUS 0x230 #define PARF_SID_OFFSET 0x234 #define PARF_BDF_TRANSLATE_CFG 0x24c #define PARF_DBI_BASE_ADDR_V2 0x350 @@ -133,6 +137,13 @@ /* PARF_LTSSM register fields */ #define LTSSM_EN BIT(8) #define PARF_LTSSM_STATE_MASK GENMASK(5, 0) +#define SW_CLEAR_FLUSH_MODE BIT(10) +#define FLUSH_MODE BIT(11) + +/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */ +#define INT_ALL_LINK_DOWN 1 +#define PARF_INT_ALL_LINK_DOWN BIT(INT_ALL_LINK_DOWN) +#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23) /* PARF_NO_SNOOP_OVERRIDE register fields */ #define WR_NO_SNOOP_OVERRIDE_EN BIT(1) @@ -144,6 +155,9 @@ /* PARF_BDF_TO_SID_CFG fields */ #define BDF_TO_SID_BYPASS BIT(0) +/* PARF_STATUS fields */ +#define FLUSH_COMPLETED BIT(8) + /* ELBI_SYS_CTRL register fields */ #define ELBI_SYS_CTRL_LT_ENABLE BIT(0) #define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4) @@ -172,6 +186,7 @@ PCIE_CAP_SLOT_POWER_LIMIT_SCALE) #define PERST_DELAY_US 1000 +#define FLUSH_TIMEOUT_US 100 #define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0)) @@ -291,10 +306,13 @@ struct qcom_pcie { struct dentry *debugfs; struct list_head ports; struct gpio_desc *reset; + int global_irq; bool use_pm_opp; }; #define to_qcom_pcie(x) dev_get_drvdata((x)->dev) +static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge, + struct pci_dev *pdev); static void __qcom_pcie_perst_assert(struct qcom_pcie *pcie, bool assert) { @@ -358,7 +376,7 @@ static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci) dw_pcie_dbi_ro_wr_dis(pci); } -static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci) +static void qcom_pcie_set_slot_cap(struct dw_pcie *pci) { u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); u32 val; @@ -372,6 +390,12 @@ static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci) */ val = readl(pci->dbi_base + offset + PCI_EXP_SLTCAP); val |= PCI_EXP_SLTCAP_NCCS; + + /* + * Qcom PCIe Root Ports do not support Attention Button, so clear + * Attention Button Present in Slot Capabilities. + */ + val &= ~PCI_EXP_SLTCAP_ABP; writel(val, pci->dbi_base + offset + PCI_EXP_SLTCAP); dw_pcie_dbi_ro_wr_dis(pci); @@ -580,7 +604,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) writel(CFG_BRIDGE_SB_INIT, pci->dbi_base + AXI_MSTR_RESP_COMP_CTRL1); - qcom_pcie_set_slot_nccs(pcie->pci); + qcom_pcie_set_slot_cap(pcie->pci); return 0; } @@ -660,7 +684,7 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie) writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT); } - qcom_pcie_set_slot_nccs(pcie->pci); + qcom_pcie_set_slot_cap(pcie->pci); return 0; } @@ -759,7 +783,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) val |= EN; writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); - qcom_pcie_set_slot_nccs(pcie->pci); + qcom_pcie_set_slot_cap(pcie->pci); return 0; } @@ -1078,7 +1102,7 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) writel(WR_NO_SNOOP_OVERRIDE_EN | RD_NO_SNOOP_OVERRIDE_EN, pcie->parf + PARF_NO_SNOOP_OVERRIDE); - qcom_pcie_set_slot_nccs(pcie->pci); + qcom_pcie_set_slot_cap(pcie->pci); return 0; } @@ -1406,6 +1430,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) goto err_assert_reset; } + pp->bridge->reset_root_port = qcom_pcie_reset_root_port; + return 0; err_assert_reset: @@ -1734,6 +1760,75 @@ static int qcom_pcie_set_max_opp(struct device *dev) return ret; } +/* + * Qcom PCIe controllers only support one Root Port per controller instance. So + * this function ignores the 'pci_dev' associated with the Root Port and just + * resets the host bridge, which in turn resets the Root Port also. + */ +static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge, + struct pci_dev *pdev) +{ + struct device *dev = bridge->dev.parent; + struct qcom_pcie *pcie = dev_get_drvdata(dev); + struct dw_pcie *pci = pcie->pci; + struct dw_pcie_rp *pp = &pci->pp; + u32 val; + int ret; + + /* Wait for the pending transactions to be completed */ + ret = readl_relaxed_poll_timeout(pcie->parf + PARF_STATUS, val, + val & FLUSH_COMPLETED, 10, + FLUSH_TIMEOUT_US); + if (ret) { + dev_err(dev, "Flush completion failed: %d\n", ret); + return ret; + } + + /* Clear the FLUSH_MODE to allow the core to be reset */ + val = readl(pcie->parf + PARF_LTSSM); + val |= SW_CLEAR_FLUSH_MODE; + writel(val, pcie->parf + PARF_LTSSM); + + /* Wait for the FLUSH_MODE to clear */ + ret = readl_relaxed_poll_timeout(pcie->parf + PARF_LTSSM, val, + !(val & FLUSH_MODE), 10, + FLUSH_TIMEOUT_US); + if (ret) { + dev_err(dev, "Flush mode clear failed: %d\n", ret); + return ret; + } + + qcom_pcie_host_deinit(pp); + + ret = qcom_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Host init failed\n"); + return ret; + } + + ret = dw_pcie_setup_rc(pp); + if (ret) + return ret; + + /* + * Re-enable global IRQ events as the PARF_INT_ALL_MASK register is + * non-sticky. + */ + if (pcie->global_irq) + writel_relaxed(PARF_INT_ALL_LINK_DOWN | PARF_INT_MSI_DEV_0_7, + pcie->parf + PARF_INT_ALL_MASK); + + qcom_pcie_start_link(pci); + + ret = dw_pcie_wait_for_link(pci); + if (ret) + return ret; + + dev_dbg(dev, "Root Port reset completed\n"); + + return 0; +} + static int qcom_pcie_link_transition_count(struct seq_file *s, void *data) { struct qcom_pcie *pcie = (struct qcom_pcie *)dev_get_drvdata(s->private); @@ -1771,6 +1866,27 @@ static void qcom_pcie_init_debugfs(struct qcom_pcie *pcie) qcom_pcie_link_transition_count); } +static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data) +{ + struct qcom_pcie *pcie = data; + struct dw_pcie_rp *pp = &pcie->pci->pp; + struct device *dev = pcie->pci->dev; + struct pci_dev *port; + unsigned long status = readl_relaxed(pcie->parf + PARF_INT_ALL_STATUS); + + writel_relaxed(status, pcie->parf + PARF_INT_ALL_CLEAR); + + if (test_and_clear_bit(INT_ALL_LINK_DOWN, &status)) { + dev_dbg(dev, "Received Link down event\n"); + for_each_pci_bridge(port, pp->bridge->bus) { + if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT) + pci_host_handle_link_down(port); + } + } + + return IRQ_HANDLED; +} + static void qcom_pci_free_msi(void *ptr) { struct dw_pcie_rp *pp = (struct dw_pcie_rp *)ptr; @@ -2027,7 +2143,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) struct dw_pcie_rp *pp; struct resource *res; struct dw_pcie *pci; - int ret; + int ret, irq; pcie_cfg = of_device_get_match_data(dev); if (!pcie_cfg) { @@ -2172,6 +2288,32 @@ static int qcom_pcie_probe(struct platform_device *pdev) goto err_phy_exit; } + irq = platform_get_irq_byname_optional(pdev, "global"); + if (irq > 0) { + const char *name; + + name = devm_kasprintf(dev, GFP_KERNEL, "qcom_pcie_global_irq%d", + pci_domain_nr(pp->bridge->bus)); + if (!name) { + ret = -ENOMEM; + goto err_host_deinit; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + qcom_pcie_global_irq_thread, + IRQF_ONESHOT, name, pcie); + if (ret) { + dev_err_probe(&pdev->dev, ret, + "Failed to request Global IRQ\n"); + goto err_host_deinit; + } + + writel_relaxed(PARF_INT_ALL_LINK_DOWN | PARF_INT_MSI_DEV_0_7, + pcie->parf + PARF_INT_ALL_MASK); + + pcie->global_irq = irq; + } + qcom_pcie_icc_opp_update(pcie); if (pcie->mhi) @@ -2179,6 +2321,8 @@ static int qcom_pcie_probe(struct platform_device *pdev) return 0; +err_host_deinit: + dw_pcie_host_deinit(pp); err_phy_exit: list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) { list_for_each_entry_safe(perst, tmp_perst, &port->perst, list) diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 5f7211b91ee5..fbe465a29068 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -481,7 +481,7 @@ static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar) } /* Endpoint mode */ -static void rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep) +static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep) { struct dw_pcie *dw = to_dw_pcie_from_ep(ep); struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); @@ -489,9 +489,11 @@ static void rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep) ret = rcar_gen4_pcie_common_init(rcar); if (ret) - return; + return ret; writel(PCIEDMAINTSTSEN_INIT, rcar->base + PCIEDMAINTSTSEN); + + return 0; } static void rcar_gen4_pcie_ep_deinit(struct rcar_gen4_pcie *rcar) diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c index 0b187efe8551..0564c46e2f48 100644 --- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c +++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c @@ -20,9 +20,6 @@ #include "pcie-designware.h" -#define PCI_VENDOR_ID_SPACEMIT 0x201f -#define PCI_DEVICE_ID_SPACEMIT_K1 0x0001 - /* Offsets and field definitions for link management registers */ #define K1_PHY_AHB_IRQ_EN 0x0000 #define PCIE_INTERRUPT_EN BIT(0) diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 795cef5a915d..63a0f56cad8e 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -2196,15 +2196,15 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) if (IS_ERR(pcie->pex_ctl_supply)) { ret = PTR_ERR(pcie->pex_ctl_supply); if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get regulator: %ld\n", - PTR_ERR(pcie->pex_ctl_supply)); + dev_err(dev, "Failed to get regulator: %pe\n", + pcie->pex_ctl_supply); return ret; } pcie->core_clk = devm_clk_get(dev, "core"); if (IS_ERR(pcie->core_clk)) { - dev_err(dev, "Failed to get core clock: %ld\n", - PTR_ERR(pcie->core_clk)); + dev_err(dev, "Failed to get core clock: %pe\n", + pcie->core_clk); return PTR_ERR(pcie->core_clk); } @@ -2226,8 +2226,8 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) pcie->core_apb_rst = devm_reset_control_get(dev, "apb"); if (IS_ERR(pcie->core_apb_rst)) { - dev_err(dev, "Failed to get APB reset: %ld\n", - PTR_ERR(pcie->core_apb_rst)); + dev_err(dev, "Failed to get APB reset: %pe\n", + pcie->core_apb_rst); return PTR_ERR(pcie->core_apb_rst); } @@ -2268,8 +2268,8 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev) pcie->core_rst = devm_reset_control_get(dev, "core"); if (IS_ERR(pcie->core_rst)) { - dev_err(dev, "Failed to get core reset: %ld\n", - PTR_ERR(pcie->core_rst)); + dev_err(dev, "Failed to get core reset: %pe\n", + pcie->core_rst); return PTR_ERR(pcie->core_rst); } diff --git a/drivers/pci/controller/dwc/pcie-ultrarisc.c b/drivers/pci/controller/dwc/pcie-ultrarisc.c index 6ee661ceff67..51e89180490d 100644 --- a/drivers/pci/controller/dwc/pcie-ultrarisc.c +++ b/drivers/pci/controller/dwc/pcie-ultrarisc.c @@ -5,6 +5,7 @@ * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. */ +#include <linux/clk.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_device.h> @@ -23,6 +24,12 @@ #define ULTRARISC_PCIE_COMP_TIMEOUT_65_210MS 0x6 +struct ultrarisc_pcie { + struct dw_pcie pci; + struct clk_bulk_data *clks; + int num_clks; +}; + static struct pci_ops ultrarisc_pci_ops = { .map_bus = dw_pcie_own_conf_map_bus, .read = pci_generic_config_read32, @@ -98,17 +105,47 @@ static const struct dw_pcie_ops dw_pcie_ops = { .start_link = ultrarisc_pcie_start_link, }; +static int ultrarisc_pcie_enable_clks(struct ultrarisc_pcie *ultra) +{ + return clk_bulk_prepare_enable(ultra->num_clks, ultra->clks); +} + +static void ultrarisc_pcie_disable_clks(void *data) +{ + struct ultrarisc_pcie *ultra = data; + + clk_bulk_disable_unprepare(ultra->num_clks, ultra->clks); +} + +static int ultrarisc_pcie_init_clks(struct ultrarisc_pcie *ultra) +{ + struct device *dev = ultra->pci.dev; + int ret; + + ultra->num_clks = devm_clk_bulk_get_all(dev, &ultra->clks); + if (ultra->num_clks < 0) + return dev_err_probe(dev, ultra->num_clks, "Failed to get clocks\n"); + + ret = ultrarisc_pcie_enable_clks(ultra); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable clocks\n"); + + return devm_add_action_or_reset(dev, ultrarisc_pcie_disable_clks, ultra); +} + static int ultrarisc_pcie_probe(struct platform_device *pdev) { + struct ultrarisc_pcie *ultra; struct device *dev = &pdev->dev; struct dw_pcie_rp *pp; struct dw_pcie *pci; int ret; - pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - if (!pci) + ultra = devm_kzalloc(dev, sizeof(*ultra), GFP_KERNEL); + if (!ultra) return -ENOMEM; + pci = &ultra->pci; pci->dev = dev; pci->ops = &dw_pcie_ops; @@ -117,7 +154,11 @@ static int ultrarisc_pcie_probe(struct platform_device *pdev) pp = &pci->pp; - platform_set_drvdata(pdev, pci); + platform_set_drvdata(pdev, ultra); + + ret = ultrarisc_pcie_init_clks(ultra); + if (ret) + return ret; pp->num_vectors = MAX_MSI_IRQS; /* No L2/L3 Ready indication is available on this platform */ @@ -135,16 +176,46 @@ static int ultrarisc_pcie_probe(struct platform_device *pdev) static int ultrarisc_pcie_suspend_noirq(struct device *dev) { - struct dw_pcie *pci = dev_get_drvdata(dev); + struct ultrarisc_pcie *ultra = dev_get_drvdata(dev); + struct dw_pcie *pci = &ultra->pci; + int ret; + + /* + * A failed resume leaves the DWC suspended and the clocks disabled. + * A later suspend must not access the controller or disable them again. + */ + if (pci->suspended) + return 0; + + ret = dw_pcie_suspend_noirq(pci); + if (ret) + return ret; + + if (pci->suspended) + ultrarisc_pcie_disable_clks(ultra); - return dw_pcie_suspend_noirq(pci); + return 0; } static int ultrarisc_pcie_resume_noirq(struct device *dev) { - struct dw_pcie *pci = dev_get_drvdata(dev); + struct ultrarisc_pcie *ultra = dev_get_drvdata(dev); + struct dw_pcie *pci = &ultra->pci; + int ret; - return dw_pcie_resume_noirq(pci); + if (pci->suspended) { + ret = ultrarisc_pcie_enable_clks(ultra); + if (ret) + return ret; + + ret = dw_pcie_resume_noirq(pci); + if (ret) { + ultrarisc_pcie_disable_clks(ultra); + return ret; + } + } + + return 0; } static const struct dev_pm_ops ultrarisc_pcie_pm_ops = { @@ -169,7 +240,7 @@ static struct platform_driver ultrarisc_pcie_driver = { }, .probe = ultrarisc_pcie_probe, }; -builtin_platform_driver(ultrarisc_pcie_driver); +module_platform_driver(ultrarisc_pcie_driver); MODULE_DESCRIPTION("UltraRISC DP1000 DWC PCIe host controller"); MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c b/drivers/pci/controller/mobiveil/pcie-mobiveil.c index 62ecbaeb0a60..e8346851c49b 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c @@ -218,11 +218,11 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie) int retries; /* check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) { if (mobiveil_pcie_link_up(pcie)) return 0; - usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); + msleep(PCIE_LINK_WAIT_SLEEP_MS); } dev_err(&pcie->pdev->dev, "link never came up\n"); diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h index 7246de6a7176..11010a99e27c 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h @@ -122,11 +122,6 @@ #define IB_WIN_SIZE ((u64)256 * 1024 * 1024 * 1024) #define MAX_PIO_WINDOWS 8 -/* Parameters for the waiting for link up routine */ -#define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_MIN 90000 -#define LINK_WAIT_MAX 100000 - #define PAGED_ADDR_BNDRY 0xc00 #define OFFSET_TO_PAGE_ADDR(off) \ ((off & PAGE_LO_MASK) | PAGED_ADDR_BNDRY) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index fd9c7d53e8a7..ecb81ac73019 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -256,9 +256,6 @@ enum { #define PIO_RETRY_CNT 750000 /* 1.5 s */ #define PIO_RETRY_DELAY 2 /* 2 us*/ -#define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_USLEEP_MIN 90000 -#define LINK_WAIT_USLEEP_MAX 100000 #define RETRAIN_WAIT_MAX_RETRIES 10 #define RETRAIN_WAIT_USLEEP_US 2000 @@ -350,13 +347,13 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie) int retries; /* check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) { if (advk_pcie_link_up(pcie)) { pci_host_common_link_train_delay(pcie->link_gen); return 0; } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + msleep(PCIE_LINK_WAIT_SLEEP_MS); } return -ETIMEDOUT; @@ -1722,7 +1719,7 @@ static int advk_pcie_setup_phy(struct advk_pcie *pcie) /* Old bindings miss the PHY handle */ if (IS_ERR(pcie->phy)) { - dev_warn(dev, "PHY unavailable (%ld)\n", PTR_ERR(pcie->phy)); + dev_warn(dev, "PHY unavailable (%pe)\n", pcie->phy); pcie->phy = NULL; return 0; } diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index da6636505c74..a23907a875e5 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -13,9 +13,11 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_pci.h> +#include <linux/pci.h> #include <linux/pci-ecam.h> #include <linux/platform_device.h> +#include "../pci.h" #include "pci-host-common.h" /** @@ -323,5 +325,38 @@ bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge, } EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible); +static pci_ers_result_t pci_host_reset_root_port(struct pci_dev *dev) +{ + int ret; + + pci_lock_rescan_remove(); + ret = pci_bus_error_reset(dev); + pci_unlock_rescan_remove(); + if (ret) { + pci_err(dev, "Failed to reset Root Port: %d\n", ret); + return PCI_ERS_RESULT_DISCONNECT; + } + + pci_info(dev, "Root Port has been reset\n"); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void pci_host_recover_root_port(struct pci_dev *port) +{ +#if IS_ENABLED(CONFIG_PCIEAER) + pcie_do_recovery(port, pci_channel_io_frozen, pci_host_reset_root_port); +#else + pci_host_reset_root_port(port); +#endif +} + +void pci_host_handle_link_down(struct pci_dev *port) +{ + pci_info(port, "Recovering Root Port due to Link Down\n"); + pci_host_recover_root_port(port); +} +EXPORT_SYMBOL_GPL(pci_host_handle_link_down); + MODULE_DESCRIPTION("Common library for PCI host controller drivers"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h index 9f0f36a32221..51afd65d3c4a 100644 --- a/drivers/pci/controller/pci-host-common.h +++ b/drivers/pci/controller/pci-host-common.h @@ -48,6 +48,7 @@ int pci_host_common_init(struct platform_device *pdev, struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops); void pci_host_common_remove(struct platform_device *pdev); +void pci_host_handle_link_down(struct pci_dev *port); struct pci_config_window *pci_host_common_ecam_create(struct device *dev, struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops); diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 229c69534476..59b56446c507 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -1356,8 +1356,7 @@ static int tegra_pcie_port_get_phys(struct tegra_pcie_port *port) for (i = 0; i < port->lanes; i++) { phy = devm_of_phy_optional_get_index(dev, port->np, "pcie", i); if (IS_ERR(phy)) { - dev_err(dev, "failed to get PHY#%u: %ld\n", i, - PTR_ERR(phy)); + dev_err(dev, "failed to get PHY#%u: %pe\n", i, phy); return PTR_ERR(phy); } diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index bcbd08c82a5c..4148e901bd30 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -33,6 +33,11 @@ #include "pci-host-common.h" #include "../pci.h" +/* AN7581 SCU register */ +#define SCU_PCIC 0x88 +#define SCU_PCIC_PCIE_CTRL GENMASK(7, 0) + +/* PCIe register */ #define PCIE_BASE_CFG_REG 0x14 #define PCIE_BASE_CFG_SPEED GENMASK(15, 8) @@ -138,6 +143,7 @@ #define PCIE_ATR_TLP_TYPE_IO PCIE_ATR_TLP_TYPE(2) #define MAX_NUM_PHY_RESETS 3 +#define MAX_NUM_PERSTOUT_RESETS 2 #define PCIE_MTK_RESET_TIME_US 10 @@ -189,9 +195,11 @@ struct mtk_msi_set { * struct mtk_gen3_pcie - PCIe port information * @dev: pointer to PCIe device * @base: IO mapped register base + * @sec_base: Secondary register base * @reg_base: physical register base * @mac_reset: MAC reset control * @phy_resets: PHY reset controllers + * @perstout_resets: PERSTOUT resets * @phy: PHY controller block * @clks: PCIe clocks * @num_clks: PCIe clocks count for this port @@ -210,9 +218,11 @@ struct mtk_msi_set { struct mtk_gen3_pcie { struct device *dev; void __iomem *base; + void __iomem *sec_base; phys_addr_t reg_base; struct reset_control *mac_reset; struct reset_control_bulk_data phy_resets[MAX_NUM_PHY_RESETS]; + struct reset_control_bulk_data perstout_resets[MAX_NUM_PERSTOUT_RESETS]; struct phy *phy; struct clk_bulk_data *clks; int num_clks; @@ -950,6 +960,15 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) if (ret) return dev_err_probe(dev, ret, "failed to get PHY bulk reset\n"); + pcie->perstout_resets[0].id = "perstout"; + pcie->perstout_resets[1].id = "sec-perstout"; + + ret = devm_reset_control_bulk_get_optional_exclusive(dev, + MAX_NUM_PERSTOUT_RESETS, + pcie->perstout_resets); + if (ret) + return dev_err_probe(dev, ret, "failed to get PERSTOUT bulk reset\n"); + pcie->mac_reset = devm_reset_control_get_optional_exclusive(dev, "mac"); if (IS_ERR(pcie->mac_reset)) return dev_err_probe(dev, PTR_ERR(pcie->mac_reset), "failed to get MAC reset\n"); @@ -971,18 +990,42 @@ static int mtk_pcie_parse_port(struct mtk_gen3_pcie *pcie) pcie->num_lanes = num_lanes; } + /* Map secondary PCIe for 2-lanes mode for EN7581 */ + if (pcie->num_lanes == 2 && + device_is_compatible(dev, "airoha,en7581-pcie")) { + regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "sec-pcie-mac"); + if (!regs) + return -EINVAL; + pcie->sec_base = devm_ioremap_resource(dev, regs); + if (IS_ERR(pcie->sec_base)) + return dev_err_probe(dev, PTR_ERR(pcie->sec_base), + "failed to map secondary register base\n"); + } + return 0; } static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + unsigned int num_lanes = max(1, pcie->num_lanes); + struct regmap *pbus_regmap, *scu; struct device *dev = pcie->dev; struct resource_entry *entry; - struct regmap *pbus_regmap; u32 val, args[2], size; resource_size_t addr; - int err; + int i, err; + + if (num_lanes > 2) + return dev_err_probe(dev, -EINVAL, "unsupported num-lanes, maximum 2 lanes supported\n"); + + if (num_lanes == 2) { + scu = syscon_regmap_lookup_by_phandle(dev->of_node, + "airoha,scu"); + if (IS_ERR(scu)) + return dev_err_probe(dev, PTR_ERR(scu), "failed to map SCU regmap\n"); + } /* * The controller may have been left out of reset by the bootloader @@ -1014,6 +1057,19 @@ static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) size = lower_32_bits(resource_size(entry->res)); regmap_write(pbus_regmap, args[1], GENMASK(31, __fls(size))); + /* Assert PERSTOUT for all relevant lanes */ + err = reset_control_bulk_assert(MAX_NUM_PERSTOUT_RESETS, + pcie->perstout_resets); + if (err) { + dev_err(dev, "failed to assert PERSTOUTs\n"); + return err; + } + + /* Configure SCU MUX to disable PCIE1 for x2 lanes mode */ + if (num_lanes == 2) + regmap_update_bits(scu, SCU_PCIC, SCU_PCIC_PCIE_CTRL, + FIELD_PREP(SCU_PCIC_PCIE_CTRL, BIT(1))); + /* * Unlike the other MediaTek Gen3 controllers, the Airoha EN7581 * requires PHY initialization and power-on before PHY reset deassert. @@ -1046,18 +1102,6 @@ static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) | - FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) | - FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) | - FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41); - writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG); - - val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT | - FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) | - FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) | - FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf); - writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG); - err = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); if (err) { dev_err(dev, "failed to prepare clock\n"); @@ -1065,14 +1109,47 @@ static int mtk_pcie_en7581_power_up(struct mtk_gen3_pcie *pcie) } /* - * Airoha EN7581 performs PCIe reset via clk callbacks since it has a - * hw issue with PCIE_PE_RSTB signal. Add wait for the time needed to - * complete the PCIe reset. + * Old Airoha EN7581 clock driver performed PCIe reset via + * clk callbacks since it has a hw issue with PCIE_PE_RSTB signal. + * This is now handled by dedicated PERSTOUT resets with clk + * driver only enabling the refclk. + * + * Wait is still needed for refclk to stabilize */ msleep(PCIE_T_PVPERL_MS); + /* Configure all the lanes to the same EQ config */ + for (i = 0; i < num_lanes; i++) { + void __iomem *base = pcie->base; + + if (i == 1) + base = pcie->sec_base; + + val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) | + FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) | + FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) | + FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41); + writel_relaxed(val, base + PCIE_EQ_PRESET_01_REG); + + val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT | + FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) | + FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) | + FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf); + writel_relaxed(val, base + PCIE_PIPE4_PIE8_REG); + } + + /* Deassert PERSTOUT for all relevant lanes */ + err = reset_control_bulk_deassert(MAX_NUM_PERSTOUT_RESETS, + pcie->perstout_resets); + if (err) { + dev_err(dev, "failed to deassert PERSTOUTs\n"); + goto err_perstout_deassert; + } + return 0; +err_perstout_deassert: + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); err_clk_prepare_enable: pm_runtime_put_sync(dev); pm_runtime_disable(dev); @@ -1158,6 +1235,8 @@ static void mtk_pcie_power_down(struct mtk_gen3_pcie *pcie) phy_power_off(pcie->phy); phy_exit(pcie->phy); + reset_control_bulk_assert(MAX_NUM_PERSTOUT_RESETS, + pcie->perstout_resets); reset_control_bulk_assert(pcie->soc->phy_resets.num_resets, pcie->phy_resets); } diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c index 0f88da378805..8d78ab1b31f0 100644 --- a/drivers/pci/controller/pcie-rockchip.c +++ b/drivers/pci/controller/pcie-rockchip.c @@ -232,8 +232,8 @@ int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip) if (IS_ERR(phy)) { if (PTR_ERR(phy) != -EPROBE_DEFER) - dev_err(dev, "missing phy for lane %d: %ld\n", - i, PTR_ERR(phy)); + dev_err(dev, "missing phy for lane %d: %pe\n", + i, phy); return PTR_ERR(phy); } diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 7db2c96c6cec..0dee19fa24ca 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -140,11 +140,6 @@ #define PCIE_PHY_LINKUP_BIT BIT(0) #define PHY_RDY_LINKUP_BIT BIT(1) -/* 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 - struct nwl_msi { /* MSI information */ DECLARE_BITMAP(bitmap, INT_PCI_MSI_NR); struct irq_domain *dev_domain; @@ -203,10 +198,10 @@ static int nwl_wait_for_link(struct nwl_pcie *pcie) int retries; /* check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) { if (nwl_phy_link_up(pcie)) return 0; - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + msleep(PCIE_LINK_WAIT_SLEEP_MS); } dev_err(dev, "PHY link never came up\n"); diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c index f9a34f323ad8..fd1a11b4c8eb 100644 --- a/drivers/pci/controller/plda/pcie-plda-host.c +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -419,6 +419,8 @@ static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) return plda_allocate_msi_domains(port); } +static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie); + int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port, const struct plda_event *event) @@ -440,14 +442,17 @@ int plda_init_interrupts(struct platform_device *pdev, } port->irq = platform_get_irq(pdev, 0); - if (port->irq < 0) - return -ENODEV; + if (port->irq < 0) { + ret = -ENODEV; + goto err_irq_domain_deinit; + } for_each_set_bit(i, &port->events_bitmap, port->num_events) { event_irq = irq_create_mapping(port->event_domain, i); if (!event_irq) { dev_err(dev, "failed to map hwirq %d\n", i); - return -ENXIO; + ret = -ENXIO; + goto err_irq_domain_deinit; } if (event->request_event_irq) @@ -459,7 +464,7 @@ int plda_init_interrupts(struct platform_device *pdev, if (ret) { dev_err(dev, "failed to request IRQ %d\n", event_irq); - return ret; + goto err_irq_domain_deinit; } } @@ -467,7 +472,8 @@ int plda_init_interrupts(struct platform_device *pdev, event->intx_event); if (!port->intx_irq) { dev_err(dev, "failed to map INTx interrupt\n"); - return -ENXIO; + ret = -ENXIO; + goto err_irq_domain_deinit; } /* Plug the INTx chained handler */ @@ -475,8 +481,11 @@ int plda_init_interrupts(struct platform_device *pdev, port->msi_irq = irq_create_mapping(port->event_domain, event->msi_event); - if (!port->msi_irq) - return -ENXIO; + if (!port->msi_irq) { + dev_err(dev, "failed to map MSI interrupt\n"); + ret = -ENXIO; + goto err_irq_domain_deinit; + } /* Plug the MSI chained handler */ irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port); @@ -485,6 +494,11 @@ int plda_init_interrupts(struct platform_device *pdev, irq_set_chained_handler_and_data(port->irq, plda_handle_event, port); return 0; + +err_irq_domain_deinit: + plda_pcie_irq_domain_deinit(port); + + return ret; } EXPORT_SYMBOL_GPL(plda_init_interrupts); @@ -559,9 +573,27 @@ EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems); static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie) { - irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); - irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL); - irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL); + u32 i, event_irq; + + if (pcie->irq > 0) + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); + if (pcie->msi_irq > 0) + irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL); + if (pcie->intx_irq > 0) + irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL); + + for_each_set_bit(i, &pcie->events_bitmap, pcie->num_events) { + event_irq = irq_find_mapping(pcie->event_domain, i); + if (event_irq) { + devm_free_irq(pcie->dev, event_irq, pcie); + irq_dispose_mapping(event_irq); + } + } + + if (pcie->intx_irq) + irq_dispose_mapping(pcie->intx_irq); + if (pcie->msi_irq) + irq_dispose_mapping(pcie->msi_irq); irq_domain_remove(pcie->msi.dev_domain); diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c index 628f8c8d6747..02d5aa6dc14b 100644 --- a/drivers/pci/controller/plda/pcie-starfive.c +++ b/drivers/pci/controller/plda/pcie-starfive.c @@ -45,11 +45,6 @@ #define STG_SYSCON_LNKSTA_OFFSET 0x170 #define DATA_LINK_ACTIVE BIT(5) -/* 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 - struct starfive_jh7110_pcie { struct plda_pcie_rp plda; struct reset_control *resets; @@ -217,12 +212,12 @@ static int starfive_pcie_host_wait_for_link(struct starfive_jh7110_pcie *pcie) int retries; /* Check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) { if (starfive_pcie_link_up(&pcie->plda)) { dev_info(pcie->plda.dev, "port link up\n"); return 0; } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + msleep(PCIE_LINK_WAIT_SLEEP_MS); } return -ETIMEDOUT; @@ -419,7 +414,11 @@ static int starfive_pcie_probe(struct platform_device *pdev) return ret; pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) { + pm_runtime_disable(&pdev->dev); + return dev_err_probe(dev, ret, "failed to resume device\n"); + } plda->host_ops = &sf_host_ops; plda->num_events = PLDA_MAX_EVENT_NUM; @@ -445,9 +444,9 @@ static void starfive_pcie_remove(struct platform_device *pdev) { struct starfive_jh7110_pcie *pcie = platform_get_drvdata(pdev); - pm_runtime_put(&pdev->dev); - pm_runtime_disable(&pdev->dev); plda_pcie_host_deinit(&pcie->plda); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); platform_set_drvdata(pdev, NULL); } diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 76a45b48d09f..9b283e151c1a 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -917,6 +917,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) resource_size_t busn_end; struct pci_bus *child; struct pci_dev *dev; + bool vmd_in_guest; int ret; ret = vmd_prepare_offsets_and_bus(vmd, features, &membar2_offset, @@ -978,14 +979,16 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) .parent = res, }; + /* Non-zero offset means guest/direct assign view. */ + vmd_in_guest = offset[0] || offset[1]; + /* * Currently MSI remapping must be enabled in guest passthrough mode * due to some missing interrupt remapping plumbing. This is probably * acceptable because the guest is usually CPU-limited and MSI * remapping doesn't become a performance bottleneck. */ - if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || - offset[0] || offset[1]) { + if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || vmd_in_guest) { ret = vmd_alloc_irqs(vmd); if (ret) return ret; @@ -1026,8 +1029,13 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) return -ENODEV; } - vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus), - to_pci_host_bridge(vmd->bus->bridge)); + /* + * Don't copy _OSC control flags from root bridge if running in a VM, as + * they don't reflect the physical root bridge capabilities. + */ + if (!vmd_in_guest) + vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus), + to_pci_host_bridge(vmd->bus->bridge)); vmd_attach_resources(vmd); if (vmd->irq_domain) diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 7b41da4ec11a..ac95b1d2d999 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -858,8 +858,8 @@ void pci_doe_init(struct pci_dev *pdev) PCI_EXT_CAP_ID_DOE))) { doe_mb = pci_doe_create_mb(pdev, offset); if (IS_ERR(doe_mb)) { - pci_err(pdev, "[%x] failed to create mailbox: %ld\n", - offset, PTR_ERR(doe_mb)); + pci_err(pdev, "[%x] failed to create mailbox: %pe\n", + offset, doe_mb); continue; } diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index c3e3b58fb86c..78f1fb1a6b0c 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -624,6 +624,15 @@ err_unlock: return ret; } +static void pci_epf_mhi_edma_flush_async(struct mhi_ep_cntrl *mhi_cntrl) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + + dmaengine_synchronize(epf_mhi->dma_chan_rx); + dmaengine_synchronize(epf_mhi->dma_chan_tx); + flush_workqueue(epf_mhi->dma_wq); +} + struct epf_dma_filter { struct device *dev; u32 dma_mask; @@ -792,6 +801,7 @@ static int pci_epf_mhi_link_up(struct pci_epf *epf) mhi_cntrl->write_sync = pci_epf_mhi_edma_write; mhi_cntrl->read_async = pci_epf_mhi_edma_read_async; mhi_cntrl->write_async = pci_epf_mhi_edma_write_async; + mhi_cntrl->flush_async = pci_epf_mhi_edma_flush_async; } /* Register the MHI EP controller */ diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index ff92a5c301b8..37299d59f906 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -808,7 +808,7 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) if (dn->child && PCI_DN(dn->child)) php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn); else - php_slot->slot_no = -1; /* Placeholder slot */ + php_slot->slot_no = PCI_SLOT_PLACEHOLDER; /* Placeholder slot */ kref_init(&php_slot->kref); php_slot->state = PNV_PHP_STATE_INITIALIZED; diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index 67362e5b9971..92eabf5f61b9 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c @@ -84,7 +84,7 @@ int rpaphp_register_slot(struct slot *slot) struct hotplug_slot *php_slot = &slot->hotplug_slot; u32 my_index; int retval; - int slotno = -1; + int slotno = PCI_SLOT_PLACEHOLDER; dbg("%s registering slot:path[%pOF] index[%x], name[%s] pdomain[%x] type[%d]\n", __func__, slot->dn, slot->index, slot->name, diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index 209373c92e9e..80a9db417dc8 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -870,6 +870,7 @@ void __pci_restore_msix_state(struct pci_dev *dev) { struct msi_desc *entry; bool write_msg; + u16 cmd; if (!dev->msix_enabled) return; @@ -879,6 +880,14 @@ void __pci_restore_msix_state(struct pci_dev *dev) pci_msix_clear_and_set_ctrl(dev, 0, PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL); + /* + * The restored device state may not have Memory Space enabled. + * Since the MSI-X Table and PBA are in Memory Space, enable it + * while restoring them. + */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY); + write_msg = arch_restore_msi_irqs(dev); scoped_guard (msi_descs_lock, &dev->dev) { @@ -889,6 +898,7 @@ void __pci_restore_msix_state(struct pci_dev *dev) } } + pci_write_config_word(dev, PCI_COMMAND, cmd); pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); } diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 8b18c4ba845c..a51dff91b196 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) "PCI: OF: " fmt #include <linux/cleanup.h> +#include <linux/gpio/consumer.h> #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/pci.h> @@ -15,6 +16,7 @@ #include <linux/of_address.h> #include <linux/of_pci.h> #include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> #include "pci.h" #ifdef CONFIG_PCI @@ -586,6 +588,83 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) return irq_create_of_mapping(&oirq); } EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci); + +static void pci_configure_wake_irq(struct pci_dev *pdev, struct gpio_desc *wake) +{ + int ret, wake_irq, irq_type; + + wake_irq = gpiod_to_irq(wake); + if (wake_irq < 0) { + pci_err(pdev, "Failed to get wake IRQ: %d\n", wake_irq); + return; + } + + /* + * dev_pm_set_dedicated_wake_irq() associates a wakeup IRQ with the + * device and requests it, but the PM core keeps it disabled by + * default. The IRQ is enabled only when the device is allowed to + * wake the system (during system suspend and after runtime + * suspend), and only if device wakeup is enabled. + * + * When the wake IRQ fires, the wakeirq handler invokes + * pm_runtime_resume() to bring the device back to an active power + * state (e.g. from D3cold to D0). Once the device is active and + * the link is usable, the endpoint may signal a PME, which is then + * handled by the PCI core (either via PME polling or the PCIe PME + * service driver) to wakeup the particular endpoint. + */ + ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, wake_irq); + if (ret < 0) { + pci_err(pdev, "Failed to set WAKE# IRQ: %d\n", ret); + return; + } + + irq_type = gpiod_is_active_low(wake) ? IRQ_TYPE_LEVEL_LOW : + IRQ_TYPE_LEVEL_HIGH; + ret = irq_set_irq_type(wake_irq, irq_type); + if (ret < 0) { + dev_pm_clear_wake_irq(&pdev->dev); + pci_err(pdev, "Failed to set irq_type: %d\n", ret); + return; + } + + device_init_wakeup(&pdev->dev, true); +} + +void pci_configure_of_wake_gpio(struct pci_dev *dev) +{ + struct device_node *dn = pci_device_to_OF_node(dev); + struct gpio_desc *gpio; + + if (!dn && !dev->wake) + return; + /* + * fwnode_gpiod_get() may fail with -EBUSY (e.g. shared WAKE#), but + * the actual WAKE# trigger from the device would still work and + * the host controller driver will enable power to the topology. + * + * -EPROBE_DEFER cannot be propagated here since pci_device_add() + * has no retry mechanism. + */ + gpio = fwnode_gpiod_get(of_fwnode_handle(dn), "wake", GPIOD_IN, NULL); + if (!IS_ERR(gpio)) { + dev->wake = gpio; + pci_configure_wake_irq(dev, gpio); + } +} + +void pci_remove_of_wake_gpio(struct pci_dev *dev) +{ + struct device_node *dn = pci_device_to_OF_node(dev); + + if (!dn) + return; + + device_init_wakeup(&dev->dev, false); + dev_pm_clear_wake_irq(&dev->dev); + gpiod_put(dev->wake); + dev->wake = NULL; +} #endif /* CONFIG_OF_IRQ */ static int pci_parse_request_of_pci_ranges(struct device *dev, diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 4d0f2cb6c695..42d545edd7fa 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1636,8 +1636,8 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); if (IS_ERR(cfg)) { - dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, - PTR_ERR(cfg)); + dev_err(dev, "%04x:%pR error %pe mapping ECAM\n", seg, bus_res, + cfg); return NULL; } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index f36778e62ac1..e16aa59dd7ac 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -30,6 +30,47 @@ struct pci_dynid { }; /** + * do_pci_add_dynid - Add a new PCI device ID to this driver and re-probe + * @drv: target PCI driver + * @id: ID to be added + * @check_dup: whether to check if matching ID is already present + * + * Add a new dynamic PCI device ID to this driver and causes the driver to + * probe for all devices again. @drv must have been registered prior to calling + * this function. + * + * Context: Does GFP_KERNEL allocation. + * + * Return: 0 on success, -errno on failure. + */ +static int do_pci_add_dynid(struct pci_driver *drv, + const struct pci_device_id *id, + bool check_dup) +{ + struct pci_dynid *dynid, *existing_dynid; + + dynid = kzalloc_obj(*dynid); + if (!dynid) + return -ENOMEM; + + dynid->id = *id; + + scoped_guard(spinlock, &drv->dynids.lock) { + if (check_dup) { + list_for_each_entry(existing_dynid, &drv->dynids.list, node) { + if (pci_match_one_id(&existing_dynid->id, id)) { + kfree(dynid); + return -EEXIST; + } + } + } + list_add_tail(&dynid->node, &drv->dynids.list); + } + + return driver_attach(&drv->driver); +} + +/** * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices * @drv: target pci driver * @vendor: PCI vendor ID @@ -56,25 +97,17 @@ int pci_add_dynid(struct pci_driver *drv, unsigned int class, unsigned int class_mask, unsigned long driver_data) { - struct pci_dynid *dynid; - - dynid = kzalloc_obj(*dynid); - if (!dynid) - return -ENOMEM; - - dynid->id.vendor = vendor; - dynid->id.device = device; - dynid->id.subvendor = subvendor; - dynid->id.subdevice = subdevice; - dynid->id.class = class; - dynid->id.class_mask = class_mask; - dynid->id.driver_data = driver_data; - - spin_lock(&drv->dynids.lock); - list_add_tail(&dynid->node, &drv->dynids.list); - spin_unlock(&drv->dynids.lock); - - return driver_attach(&drv->driver); + struct pci_device_id id = { + .vendor = vendor, + .device = device, + .subvendor = subvendor, + .subdevice = subdevice, + .class = class, + .class_mask = class_mask, + .driver_data = driver_data, + }; + + return do_pci_add_dynid(drv, &id, false); } EXPORT_SYMBOL_GPL(pci_add_dynid); @@ -91,6 +124,31 @@ static void pci_free_dynids(struct pci_driver *drv) } /** + * do_pci_match_id - See if a PCI ID matches a given pci_id table + * @ids: array of PCI device ID structures to search in + * @dev_id: the actual PCI device ID structure to match against. + * @include_override_only: also match against device ID entries marked as + * override only. + * + * Return: the matching pci_device_id structure or %NULL if there is no match. + */ +static const struct pci_device_id * +do_pci_match_id(const struct pci_device_id *ids, + const struct pci_device_id *dev_id, + bool include_override_only) +{ + if (ids) { + while (ids->vendor || ids->subvendor || ids->class_mask) { + if ((!ids->override_only || include_override_only) && + pci_match_one_id(ids, dev_id)) + return ids; + ids++; + } + } + return NULL; +} + +/** * pci_match_id - See if a PCI device matches a given pci_id table * @ids: array of PCI device ID structures to search in * @dev: the PCI device structure to match against. @@ -105,14 +163,9 @@ static void pci_free_dynids(struct pci_driver *drv) const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, struct pci_dev *dev) { - if (ids) { - while (ids->vendor || ids->subvendor || ids->class_mask) { - if (pci_match_one_device(ids, dev)) - return ids; - ids++; - } - } - return NULL; + struct pci_device_id dev_id = pci_id_from_device(dev); + + return do_pci_match_id(ids, &dev_id, true); } EXPORT_SYMBOL(pci_match_id); @@ -127,6 +180,7 @@ static const struct pci_device_id pci_device_id_any = { * pci_match_device - See if a device matches a driver's list of IDs * @drv: the PCI driver to match against * @dev: the PCI device structure to match against + * @id_copy: place to store copy of pci_device_id for dynamic ID * * Used by a driver to check whether a PCI device is in its list of * supported devices or in the dynids list, which may have been augmented @@ -134,10 +188,11 @@ static const struct pci_device_id pci_device_id_any = { * structure or %NULL if there is no match. */ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, - struct pci_dev *dev) + struct pci_dev *dev, + struct pci_device_id *id_copy) { - struct pci_dynid *dynid; - const struct pci_device_id *found_id = NULL, *ids; + const struct pci_device_id *found_id = NULL; + struct pci_device_id dev_id; int ret; /* When driver_override is set, only bind to the matching driver */ @@ -145,45 +200,29 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, if (ret == 0) return NULL; + dev_id = pci_id_from_device(dev); /* Look at the dynamic ids first, before the static ones */ - spin_lock(&drv->dynids.lock); - list_for_each_entry(dynid, &drv->dynids.list, node) { - if (pci_match_one_device(&dynid->id, dev)) { - found_id = &dynid->id; - break; + scoped_guard(spinlock, &drv->dynids.lock) { + struct pci_dynid *dynid; + + list_for_each_entry(dynid, &drv->dynids.list, node) { + if (pci_match_one_id(&dynid->id, &dev_id)) { + *id_copy = dynid->id; + return id_copy; + } } } - spin_unlock(&drv->dynids.lock); + found_id = do_pci_match_id(drv->id_table, &dev_id, ret > 0); if (found_id) return found_id; - for (ids = drv->id_table; (found_id = pci_match_id(ids, dev)); - ids = found_id + 1) { - /* - * The match table is split based on driver_override. - * In case override_only was set, enforce driver_override - * matching. - */ - if (found_id->override_only) { - if (ret > 0) - return found_id; - } else { - return found_id; - } - } - /* driver_override will always match, send a dummy id */ if (ret > 0) return &pci_device_id_any; return NULL; } -static void _pci_free_device(struct device *dev) -{ - kfree(to_pci_dev(dev)); -} - /** * new_id_store - sysfs frontend to pci_add_dynid() * @driver: target device driver @@ -197,38 +236,22 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, { struct pci_driver *pdrv = to_pci_driver(driver); const struct pci_device_id *ids = pdrv->id_table; - u32 vendor, device, subvendor = PCI_ANY_ID, - subdevice = PCI_ANY_ID, class = 0, class_mask = 0; - unsigned long driver_data = 0; + struct pci_device_id id = { + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID + }; int fields; int retval = 0; fields = sscanf(buf, "%x %x %x %x %x %x %lx", - &vendor, &device, &subvendor, &subdevice, - &class, &class_mask, &driver_data); + &id.vendor, &id.device, &id.subvendor, &id.subdevice, + &id.class, &id.class_mask, &id.driver_data); if (fields < 2) return -EINVAL; if (fields != 7) { - struct pci_dev *pdev = kzalloc_obj(*pdev); - if (!pdev) - return -ENOMEM; - - pdev->vendor = vendor; - pdev->device = device; - pdev->subsystem_vendor = subvendor; - pdev->subsystem_device = subdevice; - pdev->class = class; - pdev->dev.release = _pci_free_device; - - device_initialize(&pdev->dev); - if (pci_match_device(pdrv, pdev)) - retval = -EEXIST; - - put_device(&pdev->dev); - - if (retval) - return retval; + if (do_pci_match_id(pdrv->id_table, &id, false)) + return -EEXIST; } /* Only accept driver_data values that match an existing id_table @@ -236,7 +259,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, if (ids) { retval = -EINVAL; while (ids->vendor || ids->subvendor || ids->class_mask) { - if (driver_data == ids->driver_data) { + if (id.driver_data == ids->driver_data) { retval = 0; break; } @@ -246,8 +269,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, return retval; } - retval = pci_add_dynid(pdrv, vendor, device, subvendor, subdevice, - class, class_mask, driver_data); + retval = do_pci_add_dynid(pdrv, &id, fields != 7); if (retval) return retval; return count; @@ -445,12 +467,13 @@ void pci_probe_flush_workqueue(void) static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) { const struct pci_device_id *id; + struct pci_device_id id_copy; int error = 0; if (drv->probe) { error = -ENODEV; - id = pci_match_device(drv, pci_dev); + id = pci_match_device(drv, pci_dev, &id_copy); if (id) error = pci_call_probe(drv, pci_dev, id); } @@ -1538,12 +1561,13 @@ static int pci_bus_match(struct device *dev, const struct device_driver *drv) struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *pci_drv; const struct pci_device_id *found_id; + struct pci_device_id id_copy; if (pci_dev_binding_disallowed(pci_dev)) return 0; pci_drv = (struct pci_driver *)to_pci_driver(drv); - found_id = pci_match_device(pci_drv, pci_dev); + found_id = pci_match_device(pci_drv, pci_dev, &id_copy); if (found_id) return 1; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 4d5dc62a7c86..1f21856aac8a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -719,7 +719,7 @@ static ssize_t pci_read_config(struct file *filp, struct kobject *kobj, else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) size = 128; - if (off > size) + if (off >= size) return 0; if (off + count > size) { size -= off; @@ -800,7 +800,7 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj, add_taint(TAINT_USER, LOCKDEP_STILL_OK); } - if (off > dev->cfg_size) + if (off >= dev->cfg_size) return 0; if (off + count > dev->cfg_size) { size = dev->cfg_size - off; @@ -872,6 +872,27 @@ static const struct attribute_group pci_dev_config_attr_group = { }; #ifdef HAVE_PCI_LEGACY + +#define pci_legacy_resource_io_attr(_suffix, _size) \ +static const struct bin_attribute pci_legacy_io##_suffix##_attr = { \ + .attr = { .name = "legacy_io" __stringify(_suffix), .mode = 0600 }, \ + .size = (_size), \ + .read = pci_read_legacy_io, \ + .write = pci_write_legacy_io, \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource_legacy, \ + .mmap = pci_mmap_legacy_io, \ +} + +#define pci_legacy_resource_mem_attr(_suffix, _size) \ +static const struct bin_attribute pci_legacy_mem##_suffix##_attr = { \ + .attr = { .name = "legacy_mem" __stringify(_suffix), .mode = 0600 }, \ + .size = (_size), \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource_legacy, \ + .mmap = pci_mmap_legacy_mem, \ +} + /** * pci_read_legacy_io - read byte(s) from legacy I/O port space * @filp: open sysfs file @@ -968,6 +989,11 @@ static int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj, struct vm_area_struct *vma) { struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); + int ret; + + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem); } @@ -988,6 +1014,11 @@ static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj, struct vm_area_struct *vma) { struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); + int ret; + + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); } @@ -1004,6 +1035,11 @@ static inline umode_t __pci_legacy_is_visible(struct kobject *kobj, bool sparse) { struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); + int ret; + + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; if (pci_legacy_has_sparse(bus, type) != sparse) return 0; @@ -1045,41 +1081,11 @@ static loff_t pci_llseek_resource_legacy(struct file *filep, return fixed_size_llseek(filep, offset, whence, attr->size); } -static const struct bin_attribute pci_legacy_io_attr = { - .attr = { .name = "legacy_io", .mode = 0600 }, - .size = PCI_LEGACY_IO_SIZE, - .read = pci_read_legacy_io, - .write = pci_write_legacy_io, - .mmap = pci_mmap_legacy_io, - .llseek = pci_llseek_resource_legacy, - .f_mapping = iomem_get_mapping, -}; +pci_legacy_resource_io_attr(, PCI_LEGACY_IO_SIZE); +pci_legacy_resource_io_attr(_sparse, PCI_LEGACY_IO_SIZE << 5); -static const struct bin_attribute pci_legacy_io_sparse_attr = { - .attr = { .name = "legacy_io_sparse", .mode = 0600 }, - .size = PCI_LEGACY_IO_SIZE << 5, - .read = pci_read_legacy_io, - .write = pci_write_legacy_io, - .mmap = pci_mmap_legacy_io, - .llseek = pci_llseek_resource_legacy, - .f_mapping = iomem_get_mapping, -}; - -static const struct bin_attribute pci_legacy_mem_attr = { - .attr = { .name = "legacy_mem", .mode = 0600 }, - .size = PCI_LEGACY_MEM_SIZE, - .mmap = pci_mmap_legacy_mem, - .llseek = pci_llseek_resource_legacy, - .f_mapping = iomem_get_mapping, -}; - -static const struct bin_attribute pci_legacy_mem_sparse_attr = { - .attr = { .name = "legacy_mem_sparse", .mode = 0600 }, - .size = PCI_LEGACY_MEM_SIZE << 5, - .mmap = pci_mmap_legacy_mem, - .llseek = pci_llseek_resource_legacy, - .f_mapping = iomem_get_mapping, -}; +pci_legacy_resource_mem_attr(, PCI_LEGACY_MEM_SIZE); +pci_legacy_resource_mem_attr(_sparse, PCI_LEGACY_MEM_SIZE << 5); static const struct bin_attribute *const pci_legacy_io_attrs[] = { &pci_legacy_io_attr, @@ -1270,16 +1276,25 @@ static loff_t pci_llseek_resource(struct file *filep, * attribute, it's not going to work, so override it as well. */ #if arch_can_pci_mmap_io() -# define __PCI_RESOURCE_IO_MMAP_ATTRS \ - .f_mapping = iomem_get_mapping, \ - .llseek = pci_llseek_resource, \ +# define __PCI_RESOURCE_IO_MMAP_ATTRS \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource, \ .mmap = pci_mmap_resource_uc, #else -# define __PCI_RESOURCE_IO_MMAP_ATTRS +static int pci_mmap_resource_io_unsupported(struct file *filp, + struct kobject *kobj, + const struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + return -EINVAL; +} + +# define __PCI_RESOURCE_IO_MMAP_ATTRS \ + .mmap = pci_mmap_resource_io_unsupported, #endif #define pci_dev_resource_io_attr(_bar) \ -static const struct bin_attribute dev_resource##_bar##_io_attr = { \ +static const struct bin_attribute pci_dev_resource##_bar##_io_attr = { \ .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \ .private = (void *)(unsigned long)(_bar), \ .read = pci_read_resource, \ @@ -1288,7 +1303,7 @@ static const struct bin_attribute dev_resource##_bar##_io_attr = { \ } #define pci_dev_resource_uc_attr(_bar) \ -static const struct bin_attribute dev_resource##_bar##_uc_attr = { \ +static const struct bin_attribute pci_dev_resource##_bar##_uc_attr = { \ .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \ .private = (void *)(unsigned long)(_bar), \ .f_mapping = iomem_get_mapping, \ @@ -1296,8 +1311,8 @@ static const struct bin_attribute dev_resource##_bar##_uc_attr = { \ .mmap = pci_mmap_resource_uc, \ } -#define pci_dev_resource_wc_attr(_bar) \ -static const struct bin_attribute dev_resource##_bar##_wc_attr = { \ +#define pci_dev_resource_wc_attr(_bar) \ +static const struct bin_attribute pci_dev_resource##_bar##_wc_attr = { \ .attr = { .name = "resource" __stringify(_bar) "_wc", .mode = 0600 }, \ .private = (void *)(unsigned long)(_bar), \ .f_mapping = iomem_get_mapping, \ @@ -1383,32 +1398,32 @@ pci_dev_resource_wc_attr(4); pci_dev_resource_wc_attr(5); static const struct bin_attribute *const pci_dev_resource_io_attrs[] = { - &dev_resource0_io_attr, - &dev_resource1_io_attr, - &dev_resource2_io_attr, - &dev_resource3_io_attr, - &dev_resource4_io_attr, - &dev_resource5_io_attr, + &pci_dev_resource0_io_attr, + &pci_dev_resource1_io_attr, + &pci_dev_resource2_io_attr, + &pci_dev_resource3_io_attr, + &pci_dev_resource4_io_attr, + &pci_dev_resource5_io_attr, NULL, }; static const struct bin_attribute *const pci_dev_resource_uc_attrs[] = { - &dev_resource0_uc_attr, - &dev_resource1_uc_attr, - &dev_resource2_uc_attr, - &dev_resource3_uc_attr, - &dev_resource4_uc_attr, - &dev_resource5_uc_attr, + &pci_dev_resource0_uc_attr, + &pci_dev_resource1_uc_attr, + &pci_dev_resource2_uc_attr, + &pci_dev_resource3_uc_attr, + &pci_dev_resource4_uc_attr, + &pci_dev_resource5_uc_attr, NULL, }; static const struct bin_attribute *const pci_dev_resource_wc_attrs[] = { - &dev_resource0_wc_attr, - &dev_resource1_wc_attr, - &dev_resource2_wc_attr, - &dev_resource3_wc_attr, - &dev_resource4_wc_attr, - &dev_resource5_wc_attr, + &pci_dev_resource0_wc_attr, + &pci_dev_resource1_wc_attr, + &pci_dev_resource2_wc_attr, + &pci_dev_resource3_wc_attr, + &pci_dev_resource4_wc_attr, + &pci_dev_resource5_wc_attr, NULL, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 77b17b13ee61..b2879a6be5f8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -17,6 +17,7 @@ #include <linux/lockdep.h> #include <linux/msi.h> #include <linux/of.h> +#include <linux/of_pci.h> #include <linux/pci.h> #include <linux/pm.h> #include <linux/slab.h> @@ -713,6 +714,28 @@ u16 pci_find_dvsec_capability(struct pci_dev *dev, u16 vendor, u16 dvsec) } EXPORT_SYMBOL_GPL(pci_find_dvsec_capability); +static bool pci_dev_config_accessible(struct pci_dev *dev, char *msg) +{ + u32 val; + + /* + * If a device's config space is inaccessible, reads typically + * return ~0. Since Device and Vendor ID are always ~0 for VFs, + * check the Command and Status registers instead. + * + * N.B. This is racy because the device may become inaccessible + * before the next access. + */ + pci_read_config_dword(dev, PCI_COMMAND, &val); + if (PCI_POSSIBLE_ERROR(val)) { + pci_warn(dev, "Device config space inaccessible; unable to %s\n", + msg); + return false; + } + + return true; +} + /** * pci_find_parent_resource - return resource region of parent bus of given * region @@ -1114,6 +1137,16 @@ static inline bool platform_pci_bridge_d3(struct pci_dev *dev) return acpi_pci_bridge_d3(dev); } +void platform_pci_configure_wake(struct pci_dev *dev) +{ + pci_configure_of_wake_gpio(dev); +} + +void platform_pci_remove_wake(struct pci_dev *dev) +{ + pci_remove_of_wake_gpio(dev); +} + /** * pci_update_current_state - Read power state of given device and cache it * @dev: PCI device to handle. @@ -3020,11 +3053,11 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) return true; /* - * Hotplug ports handled natively by the OS were not validated - * by vendors for runtime D3 at least until 2018 because there - * was no OS support. + * Hotplug ports handled natively by the OS on x86 platforms + * were not validated by vendors for runtime D3 at least until + * 2018 because there was no OS support. */ - if (bridge->is_pciehp) + if (IS_ENABLED(CONFIG_X86) && bridge->is_pciehp) return false; if (dmi_check_system(bridge_d3_blacklist)) @@ -4363,6 +4396,9 @@ int pcie_flr(struct pci_dev *dev) { int ret; + if (!pci_dev_config_accessible(dev, "FLR")) + return -ENOTTY; + if (!pci_wait_for_pending_transaction(dev)) pci_err(dev, "timed out waiting for pending transaction; performing function level reset anyway\n"); @@ -4841,6 +4877,19 @@ void pci_reset_secondary_bus(struct pci_dev *dev) void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) { + struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); + int ret; + + if (pci_is_root_bus(dev->bus) && host->reset_root_port) { + ret = host->reset_root_port(host, dev); + if (ret) + pci_err(dev, "Failed to reset Root Port: %d\n", ret); + else + pci_restore_state(dev); + + return; + } + pci_reset_secondary_bus(dev); } @@ -4897,8 +4946,9 @@ static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, bool probe) static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe) { - if (dev->multifunction || dev->subordinate || !dev->slot || - dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET) + if (dev->subordinate || !dev->slot || + dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET || + (dev->multifunction && !dev->slot->per_func_slot)) return -ENOTTY; return pci_reset_hotplug_slot(dev->slot->hotplug, probe); @@ -5058,6 +5108,9 @@ static void pci_dev_save_and_disable(struct pci_dev *dev) */ pci_set_power_state(dev, PCI_D0); + if (!pci_dev_config_accessible(dev, "save state")) + return; + pci_save_state(dev); /* * Disable the device by clearing the Command register, except for @@ -5687,6 +5740,7 @@ int pci_bus_error_reset(struct pci_dev *bridge) { return pci_reset_bridge(bridge, PCI_RESET_NO_RESTORE); } +EXPORT_SYMBOL_GPL(pci_bus_error_reset); int pci_try_reset_bridge(struct pci_dev *bridge) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 93731242d314..ba3c3fddddc2 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -286,6 +286,8 @@ void pci_msix_init(struct pci_dev *dev); bool pci_bridge_d3_possible(struct pci_dev *dev); void pci_bridge_d3_update(struct pci_dev *dev); int pci_bridge_wait_for_secondary_bus(struct pci_dev *dev, char *reset_type); +void platform_pci_configure_wake(struct pci_dev *dev); +void platform_pci_remove_wake(struct pci_dev *dev); static inline bool pci_bus_rrs_vendor_id(u32 l) { @@ -443,21 +445,40 @@ static inline int pci_setup_cardbus(char *str) { return -ENOENT; } #endif /* CONFIG_CARDBUS */ /** - * pci_match_one_device - Tell if a PCI device structure has a matching - * PCI device id structure - * @id: single PCI device id structure to match - * @dev: the PCI device structure to match against + * pci_id_from_device - Obtain a pci_device_id from a PCI device + * @dev: the PCI device * - * Returns the matching pci_device_id structure or %NULL if there is no match. + * Return: a pci_device_id filled. + */ +static inline struct pci_device_id pci_id_from_device(const struct pci_dev *dev) +{ + return (struct pci_device_id) { + .vendor = dev->vendor, + .device = dev->device, + .subvendor = dev->subsystem_vendor, + .subdevice = dev->subsystem_device, + .class = dev->class, + }; +} + +/** + * pci_match_one_id - Tell if a PCI device ID matches a needle PCI device ID + * @id: single PCI device id structure to match against (needle) + * @dev_id: the actual ID from the PCI device + * + * ID can be retrieved from device using pci_id_from_device(). + * + * Return: the matching pci_device_id structure or %NULL if there is no match. */ static inline const struct pci_device_id * -pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) -{ - if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) && - (id->device == PCI_ANY_ID || id->device == dev->device) && - (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) && - (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) && - !((id->class ^ dev->class) & id->class_mask)) +pci_match_one_id(const struct pci_device_id *id, + const struct pci_device_id *dev_id) +{ + if ((id->vendor == PCI_ANY_ID || id->vendor == dev_id->vendor) && + (id->device == PCI_ANY_ID || id->device == dev_id->device) && + (id->subvendor == PCI_ANY_ID || id->subvendor == dev_id->subvendor) && + (id->subdevice == PCI_ANY_ID || id->subdevice == dev_id->subdevice) && + !((id->class ^ dev_id->class) & id->class_mask)) return id; return NULL; } @@ -851,6 +872,9 @@ static inline bool pci_dev_binding_disallowed(struct pci_dev *dev) * @tlp_header_valid: Indicates if TLP field contains error information * @status: COR/UNCOR error status * @mask: COR/UNCOR mask + * @anfe_status: Advisory Non-Fatal Errors, i.e. Uncorrectable Errors signaled + * as Correctable Errors (PCIe r7.0 sec 6.2.4.3). Only used if @severity + * is AER_CORRECTABLE and @status has Advisory Non-Fatal Error Status set. * @tlp: Transaction packet information */ struct aer_err_info { @@ -873,6 +897,7 @@ struct aer_err_info { unsigned int status; unsigned int mask; + u32 anfe_status; struct pcie_tlp_log tlp; }; diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index c4fd9c0b2a54..2a380bb9bfcb 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -96,6 +96,20 @@ struct aer_info { struct ratelimit_state nonfatal_ratelimit; }; +#define AER_ANFE (PCI_ERR_UNC_UNX_COMP | \ + PCI_ERR_UNC_ATOMEG | \ + PCI_ERR_UNC_DMWR_BLK) + +#define AER_POSSIBLE_ANFE (PCI_ERR_UNC_POISON_TLP | \ + PCI_ERR_UNC_POISON_BLK | \ + PCI_ERR_UNC_ECRC | \ + PCI_ERR_UNC_UNSUP | \ + PCI_ERR_UNC_COMP_TIME | \ + PCI_ERR_UNC_COMP_ABORT | \ + PCI_ERR_UNC_ACSV | \ + PCI_ERR_UNC_TLPPRE | \ + PCI_ERR_UNC_PCRC_CHECK) + #define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ PCI_ERR_UNC_POISON_BLK | \ PCI_ERR_UNC_ECRC| \ @@ -410,6 +424,15 @@ void pci_aer_init(struct pci_dev *dev) n = pcie_cap_has_rtctl(dev) ? 5 : 4; pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ERR, sizeof(u32) * n); + /* + * Advisory Non-Fatal Errors are masked by default (PCIe r7.0, sec + * 7.8.4.6). + */ + if (pcie_aer_is_native(dev) && dev->devcap & PCI_EXP_DEVCAP_RBER) + pci_clear_and_set_config_dword(dev, + dev->aer_cap + PCI_ERR_COR_MASK, + PCI_ERR_COR_ADV_NFAT, 0); + pci_aer_clear_status(dev); if (pci_aer_available()) @@ -428,23 +451,38 @@ void pci_aer_exit(struct pci_dev *dev) #define AER_AGENT_REQUESTER 1 #define AER_AGENT_COMPLETER 2 #define AER_AGENT_TRANSMITTER 3 +#define AER_AGENT_COMPONENT 4 +#define AER_AGENT_UNDEF 5 #define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) + 0 : PCI_ERR_UNC_COMP_TIME) #define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ 0 : PCI_ERR_UNC_COMP_ABORT) #define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) + (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : \ + (PCI_ERR_UNC_POISON_BLK|PCI_ERR_UNC_ATOMEG| \ + PCI_ERR_UNC_DMWR_BLK|PCI_ERR_UNC_XLAT_BLK| \ + PCI_ERR_UNC_TLPPRE)) +#define AER_AGENT_COMPONENT_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : \ + (PCI_ERR_UNC_INTN|PCI_ERR_UNC_SURPDN)) +#define AER_AGENT_UNDEF_MASK(t) ((t == AER_CORRECTABLE) ? \ + PCI_ERR_COR_ADV_NFAT : 0) + #define AER_GET_AGENT(t, e) \ ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ + (e & AER_AGENT_COMPONENT_MASK(t)) ? AER_AGENT_COMPONENT : \ + (e & AER_AGENT_UNDEF_MASK(t)) ? AER_AGENT_UNDEF : \ AER_AGENT_RECEIVER) #define AER_PHYSICAL_LAYER_ERROR 0 #define AER_DATA_LINK_LAYER_ERROR 1 #define AER_TRANSACTION_LAYER_ERROR 2 +#define AER_GENERAL_ERROR 3 +#define AER_UNDEF_ERROR 4 #define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ PCI_ERR_COR_RCVR : 0) @@ -452,11 +490,17 @@ void pci_aer_exit(struct pci_dev *dev) (PCI_ERR_COR_BAD_TLP| \ PCI_ERR_COR_BAD_DLLP| \ PCI_ERR_COR_REP_ROLL| \ - PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) + PCI_ERR_COR_REP_TIMER) : (PCI_ERR_UNC_DLP|PCI_ERR_UNC_SURPDN)) +#define AER_GENERAL_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : PCI_ERR_UNC_INTN) +#define AER_UNDEF_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + PCI_ERR_COR_ADV_NFAT : 0) #define AER_GET_LAYER_ERROR(t, e) \ ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ + (e & AER_GENERAL_ERROR_MASK(t)) ? AER_GENERAL_ERROR : \ + (e & AER_UNDEF_ERROR_MASK(t)) ? AER_UNDEF_ERROR : \ AER_TRANSACTION_LAYER_ERROR) /* @@ -471,7 +515,9 @@ static const char * const aer_error_severity_string[] = { static const char *aer_error_layer[] = { "Physical Layer", "Data Link Layer", - "Transaction Layer" + "Transaction Layer", + "General", + "", }; static const char *aer_correctable_error_string[] = { @@ -545,10 +591,12 @@ static const char *aer_uncorrectable_error_string[] = { }; static const char *aer_agent_string[] = { - "Receiver ID", - "Requester ID", - "Completer ID", - "Transmitter ID" + "Receiver", + "Requester", + "Completer", + "Transmitter", + "Component", + "", }; #define aer_stats_dev_attr(name, stats_array, strings_array, \ @@ -812,6 +860,46 @@ static int aer_ratelimit(struct pci_dev *dev, unsigned int severity) } } +static u32 aer_compute_anfe_status(u16 devsta, u32 uncor_status, + u32 uncor_mask, u32 uncor_severity) +{ + u32 anfe_status; + + /* + * Uncorrectable Errors must be unmasked and have Non-Fatal severity + * to qualify as Advisory Non-Fatal Errors (PCIe r7.0 sec 6.2.4.3). + */ + uncor_status &= ~uncor_mask & ~uncor_severity; + + /* Some Non-Fatal Errors are always Advisory (PCIe r7.0 sec 6.2.7). */ + anfe_status = uncor_status & AER_ANFE; + + /* + * Others may be Advisory at the discretion of the detecting agent. + * That's impossible to discern if the agent signaled ERR_NONFATAL + * in addition to ERR_COR. Assume none are Advisory in that case + * to ensure that the Uncorrectable Error code path is taken. + */ + if (!(devsta & PCI_EXP_DEVSTA_NFED)) + anfe_status |= uncor_status & AER_POSSIBLE_ANFE; + + return anfe_status; +} + +static u32 aer_get_anfe_status(struct pci_dev *dev) +{ + u32 uncor_status, uncor_mask, uncor_severity; + u16 devsta, aer = dev->aer_cap; + + pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &uncor_status); + pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &uncor_mask); + pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_SEVER, &uncor_severity); + pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &devsta); + + return aer_compute_anfe_status(devsta, uncor_status, uncor_mask, + uncor_severity); +} + static bool tlp_header_logged(u32 status, u32 capctl) { /* Errors for which a header is always logged (PCIe r7.0 sec 6.2.7) */ @@ -830,8 +918,8 @@ static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { const char **strings; unsigned long status = info->status & ~info->mask; + const char *errmsg, *agent, *layer; const char *level = info->level; - const char *errmsg; int i; if (info->severity == AER_CORRECTABLE) @@ -841,10 +929,18 @@ static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) for_each_set_bit(i, &status, 32) { errmsg = strings[i]; - if (!errmsg) + agent = aer_agent_string[AER_GET_AGENT(info->severity, BIT(i))]; + layer = aer_error_layer[AER_GET_LAYER_ERROR(info->severity, + BIT(i))]; + if (!errmsg) { errmsg = "Unknown Error Bit"; + agent = ""; + layer = ""; + } - aer_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg, + aer_printk(level, dev, " [%2d] %-17s | %-11s | %-17s%s\n", + i, errmsg, agent, layer, + info->severity != AER_CORRECTABLE && info->first_error == i ? " (First)" : ""); } } @@ -854,18 +950,18 @@ static void aer_print_source(struct pci_dev *dev, struct aer_err_info *info, { u16 source = info->id; - pci_info(dev, "%s%s error message received from %04x:%02x:%02x.%d%s\n", + pci_info(dev, "%s%s Error message%s from %04x:%02x:%02x.%d%s\n", info->multi_error_valid ? "Multiple " : "", aer_error_severity_string[info->severity], + info->multi_error_valid ? "s received, first one" : " received", pci_domain_nr(dev->bus), PCI_BUS_NUM(source), PCI_SLOT(source), PCI_FUNC(source), - found ? "" : " (no details found"); + found ? "" : " (no details found)"); } void aer_print_error(struct aer_err_info *info, int i) { struct pci_dev *dev; - int layer, agent, id; const char *level = info->level; const char *bus_type = aer_err_bus(info); @@ -873,39 +969,48 @@ void aer_print_error(struct aer_err_info *info, int i) return; dev = info->dev[i]; - id = pci_dev_id(dev); pci_dev_aer_stats_incr(dev, info); trace_aer_event(pci_name(dev), (info->status & ~info->mask), - info->severity, info->tlp_header_valid, &info->tlp, bus_type); + info->severity, info->tlp_header_valid && + info->severity != AER_CORRECTABLE, &info->tlp, + bus_type); + /* + * For Advisory Non-Fatal Errors, record statistics and tracing + * even if ratelimited + */ if (!info->ratelimit_print[i]) - return; + goto anfe; if (!info->status) { - pci_err(dev, "%s Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n", + pci_err(dev, "%s Bus Error: severity=%s (Inaccessible)\n", bus_type, aer_error_severity_string[info->severity]); - goto out; + return; } - layer = AER_GET_LAYER_ERROR(info->severity, info->status); - agent = AER_GET_AGENT(info->severity, info->status); - - aer_printk(level, dev, "%s Bus Error: severity=%s, type=%s, (%s)\n", - bus_type, aer_error_severity_string[info->severity], - aer_error_layer[layer], aer_agent_string[agent]); + aer_printk(level, dev, "%s Bus Error: severity=%s\n", + bus_type, aer_error_severity_string[info->severity]); aer_printk(level, dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", dev->vendor, dev->device, info->status, info->mask); __aer_print_error(dev, info); - if (info->tlp_header_valid) + if (info->tlp_header_valid && info->severity != AER_CORRECTABLE) pcie_print_tlp_log(dev, &info->tlp, level, dev_fmt(" ")); -out: - if (info->id && info->error_dev_num > 1 && info->id == id) - pci_err(dev, " Error of this Agent is reported first\n"); +anfe: + /* Recursive invocation for Advisory Non-Fatal Errors */ + if (info->anfe_status && info->severity == AER_CORRECTABLE) { + info->severity = AER_NONFATAL; + info->status = info->anfe_status; + info->mask = 0; + + aer_print_error(info, i); + + info->severity = AER_CORRECTABLE; + } } #ifdef CONFIG_ACPI_APEI_PCIEAER @@ -926,8 +1031,8 @@ EXPORT_SYMBOL_GPL(cper_severity_to_aer); void pci_print_aer(struct pci_dev *dev, int aer_severity, struct aer_capability_regs *aer) { - const char *bus_type; - int layer, agent, tlp_header_valid = 0; + const char *bus_type, *sev; + int tlp_header_valid = 0; u32 status, mask; struct aer_err_info info = { .severity = aer_severity, @@ -937,12 +1042,15 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, if (aer_severity == AER_CORRECTABLE) { status = aer->cor_status; mask = aer->cor_mask; + sev = "cor"; info.level = KERN_WARNING; } else { status = aer->uncor_status; mask = aer->uncor_mask; + sev = "uncor"; info.level = KERN_ERR; - tlp_header_valid = tlp_header_logged(status, aer->cap_control); + tlp_header_valid = tlp_header_logged(status & ~mask, + aer->cap_control); } info.status = status; @@ -955,17 +1063,17 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, trace_aer_event(pci_name(dev), (status & ~mask), aer_severity, tlp_header_valid, &aer->header_log, bus_type); + /* + * For Advisory Non-Fatal Errors, record statistics and tracing + * even if ratelimited + */ if (!aer_ratelimit(dev, info.severity)) - return; - - layer = AER_GET_LAYER_ERROR(aer_severity, status); - agent = AER_GET_AGENT(aer_severity, status); + goto anfe; - aer_printk(info.level, dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", - status, mask); + aer_printk(info.level, dev, + "aer_%s_status: 0x%08x, aer_%s_mask: 0x%08x\n", + sev, status, sev, mask); __aer_print_error(dev, &info); - aer_printk(info.level, dev, "aer_layer=%s, aer_agent=%s\n", - aer_error_layer[layer], aer_agent_string[agent]); if (aer_severity != AER_CORRECTABLE) aer_printk(info.level, dev, "aer_uncor_severity: 0x%08x\n", @@ -974,6 +1082,21 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, if (tlp_header_valid) pcie_print_tlp_log(dev, &aer->header_log, info.level, dev_fmt(" ")); + +anfe: + /* Recursive invocation for Advisory Non-Fatal Errors */ + if (aer_severity == AER_CORRECTABLE && + info.status & ~info.mask & PCI_ERR_COR_ADV_NFAT) { + u32 anfe_status = aer_compute_anfe_status(PCI_EXP_DEVSTA_CED, + aer->uncor_status, + aer->uncor_mask, + aer->uncor_severity); + if (anfe_status) { + aer->uncor_status = anfe_status; + aer->uncor_mask = 0; + pci_print_aer(dev, AER_NONFATAL, aer); + } + } } EXPORT_SYMBOL_GPL(pci_print_aer); @@ -1176,9 +1299,14 @@ static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info) * Correctable error does not need software intervention. * No need to go through error recovery process. */ - if (aer) + if (aer) { pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS, info->status); + if (info->anfe_status) + pci_write_config_dword(dev, + aer + PCI_ERR_UNCOR_STATUS, + info->anfe_status); + } if (pcie_aer_is_native(dev)) { struct pci_driver *pdrv = dev->driver; @@ -1281,6 +1409,27 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, EXPORT_SYMBOL_GPL(aer_recover_queue); #endif +static void aer_get_uncor_info(struct pci_dev *dev, struct aer_err_info *info, + u32 status) +{ + u16 aer = dev->aer_cap; + u32 aercc; + + /* Get First Error Pointer */ + pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc); + info->first_error = PCI_ERR_CAP_FEP(aercc); + + /* Get TLP Prefix/Header Log */ + if (tlp_header_logged(status, aercc)) { + info->tlp_header_valid = 1; + pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, + aer + PCI_ERR_PREFIX_LOG, + aer_tlp_log_len(dev, aercc), + aercc & PCI_ERR_CAP_TLP_LOG_FLIT, + &info->tlp); + } +} + /** * aer_get_device_error_info - read error status from dev and store it to info * @info: pointer to structure to store the error record @@ -1294,7 +1443,6 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) { struct pci_dev *dev; int type, aer; - u32 aercc; if (i >= AER_MAX_MULTI_ERR_DEVICES) return 0; @@ -1305,6 +1453,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) /* Must reset in this function */ info->status = 0; + info->anfe_status = 0; info->tlp_header_valid = 0; info->is_cxl = pcie_is_cxl(dev); @@ -1319,6 +1468,10 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) &info->mask); if (!(info->status & ~info->mask)) return 0; + if (info->status & ~info->mask & PCI_ERR_COR_ADV_NFAT) { + info->anfe_status = aer_get_anfe_status(dev); + aer_get_uncor_info(dev, info, info->anfe_status); + } } else if (type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_RC_EC || type == PCI_EXP_TYPE_DOWNSTREAM || @@ -1332,18 +1485,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) if (!(info->status & ~info->mask)) return 0; - /* Get First Error Pointer */ - pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc); - info->first_error = PCI_ERR_CAP_FEP(aercc); - - if (tlp_header_logged(info->status, aercc)) { - info->tlp_header_valid = 1; - pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, - aer + PCI_ERR_PREFIX_LOG, - aer_tlp_log_len(dev, aercc), - aercc & PCI_ERR_CAP_TLP_LOG_FLIT, - &info->tlp); - } + aer_get_uncor_info(dev, info, info->status & ~info->mask); } return 1; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 172783e7f519..95ac34a34bd5 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -839,6 +839,49 @@ static void aspm_l1ss_init(struct pcie_link_state *link) #define FLAG(x, y, d) (((x) & (PCIE_LINK_STATE_##y)) ? d : "") +/* Configure the ASPM L1 substates. Caller must disable L1 first. */ +static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) +{ + u32 val = 0; + struct pci_dev *child = link->downstream, *parent = link->pdev; + + if (state & PCIE_LINK_STATE_L1_1) + val |= PCI_L1SS_CTL1_ASPM_L1_1; + if (state & PCIE_LINK_STATE_L1_2) + val |= PCI_L1SS_CTL1_ASPM_L1_2; + if (state & PCIE_LINK_STATE_L1_1_PCIPM) + val |= PCI_L1SS_CTL1_PCIPM_L1_1; + if (state & PCIE_LINK_STATE_L1_2_PCIPM) + val |= PCI_L1SS_CTL1_PCIPM_L1_2; + + /* + * PCIe r6.2, sec 5.5.4, rules for enabling L1 PM Substates: + * - Clear L1.x enable bits at child first, then at parent + * - Set L1.x enable bits at parent first, then at child + * - ASPM/PCIPM L1.2 must be disabled while programming timing + * parameters + */ + + /* Disable all L1 substates */ + pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, 0); + pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, 0); + + /* Enable what we need to enable */ + pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, val); + pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1, + PCI_L1SS_CTL1_L1SS_MASK, val); +} + +static bool pcie_link_has_aspm_override(const struct pcie_link_state *link, + const char *aspm) +{ + return (device_property_present(&link->pdev->dev, aspm) || + device_property_present(&link->downstream->dev, aspm)); +} + static void pcie_aspm_override_default_link_state(struct pcie_link_state *link) { struct pci_dev *pdev = link->downstream; @@ -846,6 +889,36 @@ static void pcie_aspm_override_default_link_state(struct pcie_link_state *link) /* For devicetree platforms, enable L0s and L1 by default */ if (of_have_populated_dt()) { + bool no_l0s = pcie_link_has_aspm_override(link, "aspm-no-l0s"); + bool no_l1 = pcie_link_has_aspm_override(link, "aspm-no-l1"); + bool no_l1ss = pcie_link_has_aspm_override(link, "aspm-no-l1ss"); + + if (no_l0s) { + link->aspm_support &= ~PCIE_LINK_STATE_L0S; + link->aspm_default &= ~PCIE_LINK_STATE_L0S; + link->aspm_enabled &= ~PCIE_LINK_STATE_L0S; + } + + /* + * Clear L1SS in hardware before updating aspm_support. Once + * aspm_capable is derived from aspm_support, pcie_config_aspm_link() + * skips pcie_config_aspm_l1ss() entirely via the aspm_capable guard, + * leaving firmware-enabled L1SS substates active in hardware. + * This applies equally when disabling L1 (which implies L1SS). + */ + if ((no_l1 || no_l1ss) && (link->aspm_enabled & PCIE_LINK_STATE_L1SS)) + pcie_config_aspm_l1ss(link, 0); + + if (no_l1) { + link->aspm_support &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS); + link->aspm_default &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS); + link->aspm_enabled &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS); + } else if (no_l1ss) { + link->aspm_support &= ~PCIE_LINK_STATE_L1SS; + link->aspm_default &= ~PCIE_LINK_STATE_L1SS; + link->aspm_enabled &= ~PCIE_LINK_STATE_L1SS; + } + if (link->aspm_support & PCIE_LINK_STATE_L0S) link->aspm_default |= PCIE_LINK_STATE_L0S; if (link->aspm_support & PCIE_LINK_STATE_L1) @@ -861,6 +934,7 @@ static void pcie_aspm_override_default_link_state(struct pcie_link_state *link) static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { struct pci_dev *child = link->downstream, *parent = link->pdev; + struct pci_dev *fn; u16 parent_lnkctl, child_lnkctl; struct pci_bus *linkbus = parent->subordinate; @@ -894,10 +968,11 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) /* Disable L0s/L1 before updating L1SS config */ if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { - pcie_capability_write_word(child, PCI_EXP_LNKCTL, - child_lnkctl & ~PCI_EXP_LNKCTL_ASPMC); - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, - parent_lnkctl & ~PCI_EXP_LNKCTL_ASPMC); + list_for_each_entry(fn, &linkbus->devices, bus_list) + pcie_capability_clear_and_set_word(fn, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, 0); + pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, 0); } /* @@ -924,18 +999,34 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) aspm_l1ss_init(link); - /* Restore L0s/L1 if they were enabled */ - if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || - FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_lnkctl); - pcie_capability_write_word(child, PCI_EXP_LNKCTL, child_lnkctl); - } - /* Save default state */ link->aspm_default = link->aspm_enabled; pcie_aspm_override_default_link_state(link); + /* + * Restore L0s/L1 if they were enabled, but don't restore any + * state a Devicetree override just disabled in aspm_support above. + */ + if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) || + FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) { + if (!(link->aspm_support & PCIE_LINK_STATE_L0S)) { + child_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L0S; + parent_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L0S; + } + if (!(link->aspm_support & PCIE_LINK_STATE_L1)) { + child_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L1; + parent_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L1; + } + pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, + parent_lnkctl & PCI_EXP_LNKCTL_ASPMC); + list_for_each_entry(fn, &linkbus->devices, bus_list) + pcie_capability_clear_and_set_word(fn, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, + child_lnkctl & PCI_EXP_LNKCTL_ASPMC); + } + /* Setup initial capable state. Will be updated later */ link->aspm_capable = link->aspm_support; @@ -949,42 +1040,6 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) } } -/* Configure the ASPM L1 substates. Caller must disable L1 first. */ -static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) -{ - u32 val = 0; - struct pci_dev *child = link->downstream, *parent = link->pdev; - - if (state & PCIE_LINK_STATE_L1_1) - val |= PCI_L1SS_CTL1_ASPM_L1_1; - if (state & PCIE_LINK_STATE_L1_2) - val |= PCI_L1SS_CTL1_ASPM_L1_2; - if (state & PCIE_LINK_STATE_L1_1_PCIPM) - val |= PCI_L1SS_CTL1_PCIPM_L1_1; - if (state & PCIE_LINK_STATE_L1_2_PCIPM) - val |= PCI_L1SS_CTL1_PCIPM_L1_2; - - /* - * PCIe r6.2, sec 5.5.4, rules for enabling L1 PM Substates: - * - Clear L1.x enable bits at child first, then at parent - * - Set L1.x enable bits at parent first, then at child - * - ASPM/PCIPM L1.2 must be disabled while programming timing - * parameters - */ - - /* Disable all L1 substates */ - pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1SS_MASK, 0); - pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1SS_MASK, 0); - - /* Enable what we need to enable */ - pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1SS_MASK, val); - pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1, - PCI_L1SS_CTL1_L1SS_MASK, val); -} - static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) { pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index bebe4bc111d7..d77403d8855b 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -256,11 +256,6 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, } if (status == PCI_ERS_RESULT_NEED_RESET) { - /* - * TODO: Should call platform-specific - * functions to reset slot before calling - * drivers' slot_reset callbacks? - */ status = PCI_ERS_RESULT_RECOVERED; pci_dbg(bridge, "broadcast slot_reset message\n"); pci_walk_bridge(bridge, report_slot_reset, &status); @@ -297,3 +292,4 @@ failed: return status; } +EXPORT_SYMBOL_GPL(pcie_do_recovery); diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index 65f502602dee..a9cbfc1d2bc7 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -330,8 +330,8 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq) */ static int pcie_port_device_register(struct pci_dev *dev) { - int status, capabilities, i, nr_service; int irqs[PCIE_PORT_DEVICE_MAXSERVICES]; + int status, capabilities, i; /* Enable PCI Express port device */ status = pci_enable_device(dev); @@ -355,29 +355,29 @@ static int pcie_port_device_register(struct pci_dev *dev) if (status) { capabilities &= PCIE_PORT_SERVICE_HP; if (!capabilities) - goto error_disable; + goto out; } /* Allocate child services if any */ - status = -ENODEV; - nr_service = 0; for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { int service = 1 << i; if (!(capabilities & service)) continue; - if (!pcie_device_init(dev, service, irqs[i])) - nr_service++; + if (pcie_device_init(dev, service, irqs[i])) + capabilities &= ~service; } - if (!nr_service) - goto error_cleanup_irqs; - return 0; +out: + /* + * With no child services, we shouldn't need bus mastering or any IRQ + * vectors we allocated. + */ + if (!capabilities) { + pci_free_irq_vectors(dev); + pci_clear_master(dev); + } -error_cleanup_irqs: - pci_free_irq_vectors(dev); -error_disable: - pci_disable_device(dev); - return status; + return 0; } typedef int (*pcie_callback_t)(struct pcie_device *); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dd0abbc63e18..27008e2ea5af 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2749,6 +2749,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) pci_init_capabilities(dev); + platform_pci_configure_wake(dev); + /* * Add the device to our list of discovered devices * and the bus list for fixup functions, etc. diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 71ad289fcb8e..f5fd860a0540 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -14,6 +14,8 @@ #include <linux/capability.h> #include <linux/uaccess.h> #include <linux/security.h> +#include <linux/panic.h> +#include <linux/sched.h> #include <asm/byteorder.h> #include "pci.h" @@ -39,13 +41,16 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, * undefined locations (think of Intel PIIX4 as a typical example). */ - if (capable(CAP_SYS_ADMIN)) + if (file_ns_capable(file, &init_user_ns, CAP_SYS_ADMIN)) size = dev->cfg_size; else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) size = 128; else size = 64; + if (!nbytes) + return 0; + if (pos >= size) return 0; if (nbytes >= size) @@ -122,6 +127,15 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, if (ret) return ret; + if (!nbytes) + return 0; + + if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) { + pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x", + current->comm, pos); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } + if (pos >= size) return 0; if (nbytes >= size) diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c index e6a8abd9da8e..fd53a7db9875 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c @@ -79,8 +79,7 @@ enum tc9563_pwrctrl_ports { TC9563_USP, TC9563_DSP1, TC9563_DSP2, - TC9563_DSP3, - TC9563_ETHERNET, + TC9563_VDSP, TC9563_MAX }; @@ -108,6 +107,7 @@ struct tc9563_pwrctrl { struct pci_pwrctrl pwrctrl; struct regulator_bulk_data supplies[TC9563_PWRCTL_MAX_SUPPLY]; struct tc9563_pwrctrl_cfg cfg[TC9563_MAX]; + struct tc9563_pwrctrl_cfg ep_cfg; struct gpio_desc *reset_gpio; struct i2c_adapter *adapter; struct i2c_client *client; @@ -240,12 +240,18 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563, if (!cfg->disable_port) return 0; - if (port == TC9563_DSP1) { + switch (port) { + case TC9563_DSP1: seq = dsp1_pwroff_seq; len = ARRAY_SIZE(dsp1_pwroff_seq); - } else { + break; + case TC9563_DSP2: seq = dsp2_pwroff_seq; len = ARRAY_SIZE(dsp2_pwroff_seq); + break; + default: + /* Only external downstream ports DSP1/DSP2 can be powered off */ + return 0; } ret = tc9563_pwrctrl_i2c_bulk_write(tc9563->client, seq, len); @@ -256,11 +262,11 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563, ARRAY_SIZE(common_pwroff_seq)); } -static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563, - enum tc9563_pwrctrl_ports port, - bool is_l1, u32 ns) +static int tc9563_pwrctrl_set_port_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563, + enum tc9563_pwrctrl_ports port, + bool is_l1, u32 ns) { - u32 rd_val, units; + u32 units; int ret; if (ns < TC9563_L0S_L1_DELAY_UNIT_NS) @@ -269,25 +275,6 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563, /* convert to units of 256ns */ units = ns / TC9563_L0S_L1_DELAY_UNIT_NS; - if (port == TC9563_ETHERNET) { - ret = tc9563_pwrctrl_i2c_read(tc9563->client, - TC9563_EMBEDDED_ETH_DELAY, - &rd_val); - if (ret) - return ret; - - if (is_l1) - rd_val = u32_replace_bits(rd_val, units, - TC9563_ETH_L1_DELAY_MASK); - else - rd_val = u32_replace_bits(rd_val, units, - TC9563_ETH_L0S_DELAY_MASK); - - return tc9563_pwrctrl_i2c_write(tc9563->client, - TC9563_EMBEDDED_ETH_DELAY, - rd_val); - } - ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT, BIT(port)); if (ret) @@ -298,9 +285,38 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563, units); } +static int tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563, + bool is_l1, u32 ns) +{ + u32 rd_val, units; + int ret; + + if (ns < TC9563_L0S_L1_DELAY_UNIT_NS) + return 0; + + /* convert to units of 256ns */ + units = ns / TC9563_L0S_L1_DELAY_UNIT_NS; + + ret = tc9563_pwrctrl_i2c_read(tc9563->client, TC9563_EMBEDDED_ETH_DELAY, + &rd_val); + if (ret) + return ret; + + if (is_l1) + rd_val = u32_replace_bits(rd_val, units, + TC9563_ETH_L1_DELAY_MASK); + else + rd_val = u32_replace_bits(rd_val, units, + TC9563_ETH_L0S_DELAY_MASK); + + return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_EMBEDDED_ETH_DELAY, + rd_val); +} + static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563, enum tc9563_pwrctrl_ports port) { + struct device *dev = tc9563->pwrctrl.dev; u32 amp = tc9563->cfg[port].tx_amp; int port_access; @@ -320,6 +336,9 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563, case TC9563_DSP2: port_access = 0x8; break; + case TC9563_VDSP: + dev_dbg(dev, "Tx amplitude tuning not supported for VDSP\n"); + return 0; default: return -EINVAL; } @@ -338,6 +357,7 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563, enum tc9563_pwrctrl_ports port) { struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port]; + struct device *dev = tc9563->pwrctrl.dev; int port_access, lane_access = 0x3; u32 phy_rate = 0x21; @@ -356,6 +376,9 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563, port_access = 0x8; lane_access = 0x1; break; + case TC9563_VDSP: + dev_dbg(dev, "DFE tuning not supported for VDSP\n"); + return 0; default: return -EINVAL; } @@ -386,11 +409,17 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563, {TC9563_NFTS_2_5_GT, nfts[0]}, {TC9563_NFTS_5_GT, nfts[1]}, }; + struct device *dev = tc9563->pwrctrl.dev; int ret; if (!nfts[0]) return 0; + if (port == TC9563_VDSP) { + dev_dbg(dev, "N_FTS tuning not supported for VDSP\n"); + return 0; + } + ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT, BIT(port)); if (ret) @@ -415,11 +444,9 @@ static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl *tc9563, return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val); } -static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl *tc9563, - struct device_node *node, - enum tc9563_pwrctrl_ports port) +static int tc9563_pwrctrl_parse_device_dt(struct device_node *node, + struct tc9563_pwrctrl_cfg *cfg) { - struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port]; int ret; /* Disable port if the status of the port is disabled. */ @@ -490,13 +517,13 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl) goto power_off; } - ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, false, cfg->l0s_delay); + ret = tc9563_pwrctrl_set_port_l0s_l1_entry_delay(tc9563, i, false, cfg->l0s_delay); if (ret) { dev_err(dev, "Setting L0s entry delay failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, true, cfg->l1_delay); + ret = tc9563_pwrctrl_set_port_l0s_l1_entry_delay(tc9563, i, true, cfg->l1_delay); if (ret) { dev_err(dev, "Setting L1 entry delay failed\n"); goto power_off; @@ -521,6 +548,21 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl) } } + /* Configure the integrated Ethernet MAC endpoint */ + ret = tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(tc9563, false, + tc9563->ep_cfg.l0s_delay); + if (ret) { + dev_err(dev, "Setting Ethernet L0s entry delay failed\n"); + goto power_off; + } + + ret = tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(tc9563, true, + tc9563->ep_cfg.l1_delay); + if (ret) { + dev_err(dev, "Setting Ethernet L1 entry delay failed\n"); + goto power_off; + } + ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, true); if (!ret) return 0; @@ -578,8 +620,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) pci_pwrctrl_init(&tc9563->pwrctrl, dev); - port = TC9563_USP; - ret = tc9563_pwrctrl_parse_device_dt(tc9563, node, port); + ret = tc9563_pwrctrl_parse_device_dt(node, &tc9563->cfg[TC9563_USP]); if (ret) { dev_err(dev, "failed to parse device tree properties: %d\n", ret); goto remove_i2c; @@ -590,17 +631,26 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) * The first node represents DSP1, the second node represents DSP2, * and so on. */ + port = TC9563_USP; for_each_child_of_node_scoped(node, child) { - port++; - ret = tc9563_pwrctrl_parse_device_dt(tc9563, child, port); + if (++port >= TC9563_MAX) + break; + + ret = tc9563_pwrctrl_parse_device_dt(child, &tc9563->cfg[port]); if (ret) break; - /* Embedded ethernet device are under DSP3 */ - if (port == TC9563_DSP3) { - for_each_child_of_node_scoped(child, child1) { - port++; - ret = tc9563_pwrctrl_parse_device_dt(tc9563, - child1, port); + + /* + * The integrated Ethernet MAC Endpoint under VDSP is a single + * device whose functions share the same config registers. + */ + if (port == TC9563_VDSP) { + struct device_node *eth __free(device_node) = + of_get_next_available_child(child, NULL); + + if (eth) { + ret = tc9563_pwrctrl_parse_device_dt(eth, + &tc9563->ep_cfg); if (ret) break; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 6501c949c5b7..de9bbccda21f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2507,6 +2507,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f1, quirk_disable_aspm_l0s); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s); +/* Realtek RTS525A generates a Replay Timer Timeout storm when L0s is enabled. */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x525a, quirk_disable_aspm_l0s); + static void quirk_disable_aspm_l0s_l1(struct pci_dev *dev) { pcie_aspm_remove_cap(dev, @@ -5361,7 +5364,7 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev) if (!(mpc & INTEL_MPC_REG_IRBNCE)) { pci_info(dev, "Enabling MPC IRBNCE\n"); mpc |= INTEL_MPC_REG_IRBNCE; - pci_write_config_word(dev, INTEL_MPC_REG, mpc); + pci_write_config_dword(dev, INTEL_MPC_REG, mpc); } } diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index d8bffa21498a..e711ac1d4e38 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -34,6 +34,7 @@ static void pci_destroy_dev(struct pci_dev *dev) if (pci_dev_test_and_set_removed(dev)) return; + platform_pci_remove_wake(dev); pci_doe_sysfs_teardown(dev); pci_npem_remove(dev); diff --git a/drivers/pci/search.c b/drivers/pci/search.c index e3d3177fce54..34f8de551d58 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -245,8 +245,10 @@ static int match_pci_dev_by_id(struct device *dev, const void *data) { struct pci_dev *pdev = to_pci_dev(dev); const struct pci_device_id *id = data; + struct pci_device_id dev_id; - if (pci_match_one_device(id, pdev)) + dev_id = pci_id_from_device(pdev); + if (pci_match_one_id(id, &dev_id)) return 1; return 0; } @@ -416,9 +418,9 @@ EXPORT_SYMBOL(pci_get_class); * @class: search for a PCI device with this base class code * @from: Previous PCI device found in search, or %NULL for new search. * - * Iterates through the list of known PCI devices. If a PCI device is found + * Iterate through the list of known PCI devices. If a PCI device is found * with a matching base class code, the reference count to the device is - * incremented. See pci_match_one_device() to figure out how does this works. + * incremented. See pci_match_one_id() to figure out how this works. * A new search is initiated by passing %NULL as the @from argument. * Otherwise if @from is not %NULL, searches continue from next device on the * global list. The reference count for @from is always decremented if it is diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 6d5cd37bfb1e..897223f01f6a 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -37,7 +37,7 @@ static const struct sysfs_ops pci_slot_sysfs_ops = { static ssize_t address_read_file(struct pci_slot *slot, char *buf) { - if (slot->number == 0xff) + if (slot->number == PCI_SLOT_PLACEHOLDER) return sysfs_emit(buf, "%04x:%02x\n", pci_domain_nr(slot->bus), slot->bus->number); @@ -72,6 +72,23 @@ static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf) return bus_speed_read(slot->bus->cur_bus_speed, buf); } +static bool pci_dev_matches_slot(struct pci_dev *dev, struct pci_slot *slot) +{ + if (slot->per_func_slot) + return dev->devfn == slot->number; + + return slot->number == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot->number; +} + +static bool pci_slot_enabled_per_func(void) +{ + if (IS_ENABLED(CONFIG_S390)) + return true; + + return false; +} + static void pci_slot_release(struct kobject *kobj) { struct pci_dev *dev; @@ -82,8 +99,7 @@ static void pci_slot_release(struct kobject *kobj) down_read(&pci_bus_sem); list_for_each_entry(dev, &slot->bus->devices, bus_list) - if (slot->number == PCI_SLOT_ALL_DEVICES || - PCI_SLOT(dev->devfn) == slot->number) + if (pci_dev_matches_slot(dev, slot)) dev->slot = NULL; up_read(&pci_bus_sem); @@ -187,8 +203,7 @@ void pci_dev_assign_slot(struct pci_dev *dev) mutex_lock(&pci_slot_mutex); list_for_each_entry(slot, &dev->bus->slots, list) - if (slot->number == PCI_SLOT_ALL_DEVICES || - PCI_SLOT(dev->devfn) == slot->number) + if (pci_dev_matches_slot(dev, slot)) dev->slot = slot; mutex_unlock(&pci_slot_mutex); } @@ -210,7 +225,7 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) /** * pci_create_slot - create or increment refcount for physical PCI slot * @parent: struct pci_bus of parent bridge - * @slot_nr: PCI_SLOT(pci_dev->devfn), -1 for placeholder, or + * @slot_nr: PCI_SLOT(pci_dev->devfn), PCI_SLOT_PLACEHOLDER for placeholder, or * PCI_SLOT_ALL_DEVICES * @name: user visible string presented in /sys/bus/pci/slots/<name> * @hotplug: set if caller is hotplug driver, NULL otherwise @@ -236,15 +251,16 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify * a slot. There is one notable exception - pSeries (rpaphp), where the * @slot_nr cannot be determined until a device is actually inserted into - * the slot. In this scenario, the caller may pass -1 for @slot_nr. + * the slot. In this scenario, the caller may pass PCI_SLOT_PLACEHOLDER for @slot_nr. * * The following semantics are imposed when the caller passes @slot_nr == - * -1. First, we no longer check for an existing %struct pci_slot, as there - * may be many slots with @slot_nr of -1. The other change in semantics is - * user-visible, which is the 'address' parameter presented in sysfs will - * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the - * %struct pci_bus and bb is the bus number. In other words, the devfn of - * the 'placeholder' slot will not be displayed. + * PCI_SLOT_PLACEHOLDER. First, we no longer check for an existing %struct + * pci_slot, as there may be many slots with @slot_nr of + * PCI_SLOT_PLACEHOLDER. The other change in semantics is user-visible, + * which is the 'address' parameter presented in sysfs will consist solely + * of a dddd:bb tuple, where dddd is the PCI domain of the %struct pci_bus + * and bb is the bus number. In other words, the devfn of the 'placeholder' + * slot will not be displayed. * * Bus-wide slots: * For PCIe hotplug, the physical slot encompasses the entire secondary @@ -267,7 +283,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, mutex_lock(&pci_slot_mutex); - if (slot_nr == -1) + if (slot_nr == PCI_SLOT_PLACEHOLDER) goto placeholder; /* @@ -298,6 +314,9 @@ placeholder: slot->bus = pci_bus_get(parent); slot->number = slot_nr; + if (pci_slot_enabled_per_func()) + slot->per_func_slot = 1; + slot->kobj.kset = pci_slots_kset; slot_name = make_slot_name(name); @@ -318,8 +337,7 @@ placeholder: down_read(&pci_bus_sem); list_for_each_entry(dev, &parent->devices, bus_list) - if (slot_nr == PCI_SLOT_ALL_DEVICES || - PCI_SLOT(dev->devfn) == slot_nr) + if (pci_dev_matches_slot(dev, slot)) dev->slot = slot; up_read(&pci_bus_sem); |
