diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2024-07-15 14:03:44 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2024-07-15 14:03:44 -0700 |
commit | a23e1966932464e1c5226cb9ac4ce1d5fc10ba22 (patch) | |
tree | bf5f1b57faa01ca31656bfc48c7d6b6f0bc39189 /drivers/pci/controller/dwc | |
parent | 7c7b1be19b228b450c2945ec379d7fc6bfef9852 (diff) | |
parent | f3efefb6fdcce604413135bd8d4c5568e53a1f13 (diff) |
Merge branch 'next' into for-linus
Prepare input updates for 6.11 merge window.
Diffstat (limited to 'drivers/pci/controller/dwc')
32 files changed, 1987 insertions, 743 deletions
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index ab96da43e0c2..8afacc90c63b 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -286,6 +286,31 @@ config PCIE_QCOM_EP to work in endpoint mode. The PCIe controller uses the DesignWare core plus Qualcomm-specific hardware wrappers. +config PCIE_RCAR_GEN4 + tristate + +config PCIE_RCAR_GEN4_HOST + tristate "Renesas R-Car Gen4 PCIe controller (host mode)" + depends on ARCH_RENESAS || COMPILE_TEST + depends on PCI_MSI + select PCIE_DW_HOST + select PCIE_RCAR_GEN4 + help + Say Y here if you want PCIe controller (host mode) on R-Car Gen4 SoCs. + To compile this driver as a module, choose M here: the module will be + called pcie-rcar-gen4.ko. This uses the DesignWare core. + +config PCIE_RCAR_GEN4_EP + tristate "Renesas R-Car Gen4 PCIe controller (endpoint mode)" + depends on ARCH_RENESAS || COMPILE_TEST + depends on PCI_ENDPOINT + select PCIE_DW_EP + select PCIE_RCAR_GEN4 + help + Say Y here if you want PCIe controller (endpoint mode) on R-Car Gen4 + SoCs. To compile this driver as a module, choose M here: the module + will be called pcie-rcar-gen4.ko. This uses the DesignWare core. + config PCIE_ROCKCHIP_DW_HOST bool "Rockchip DesignWare PCIe controller" select PCIE_DW @@ -311,7 +336,7 @@ config PCI_EXYNOS config PCIE_FU740 bool "SiFive FU740 PCIe controller" depends on PCI_MSI - depends on SOC_SIFIVE || COMPILE_TEST + depends on ARCH_SIFIVE || COMPILE_TEST select PCIE_DW_HOST help Say Y here if you want PCIe controller support for the SiFive diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index bf5c311875a1..bac103faa523 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o +obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o # The following drivers are for devices that use the generic ACPI # pci_root.c driver but don't support standard ECAM config access. diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 4ae807e7cf79..0e406677060d 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -16,7 +16,7 @@ #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_pci.h> #include <linux/pci.h> @@ -371,7 +371,7 @@ static int dra7xx_pcie_init_irq_domain(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = { - .host_init = dra7xx_pcie_host_init, + .init = dra7xx_pcie_host_init, }; static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) @@ -386,7 +386,7 @@ static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) dra7xx_pcie_enable_wrapper_interrupts(dra7xx); } -static void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx) +static void dra7xx_pcie_raise_intx_irq(struct dra7xx_pcie *dra7xx) { dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1); mdelay(1); @@ -404,16 +404,16 @@ static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx, } static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); switch (type) { - case PCI_EPC_IRQ_LEGACY: - dra7xx_pcie_raise_legacy_irq(dra7xx); + case PCI_IRQ_INTX: + dra7xx_pcie_raise_intx_irq(dra7xx); break; - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num); break; default: @@ -436,7 +436,7 @@ dra7xx_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = dra7xx_pcie_ep_init, + .init = dra7xx_pcie_ep_init, .raise_irq = dra7xx_pcie_raise_irq, .get_features = dra7xx_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index ec5611005566..a33fa98a252e 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -14,11 +14,11 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/of_device.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include "pcie-designware.h" @@ -268,7 +268,7 @@ static int exynos_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops exynos_pcie_host_ops = { - .host_init = exynos_pcie_host_init, + .init = exynos_pcie_host_init, }; static int exynos_add_pcie_port(struct exynos_pcie *ep, @@ -375,7 +375,7 @@ fail_probe: return ret; } -static int __exit exynos_pcie_remove(struct platform_device *pdev) +static void exynos_pcie_remove(struct platform_device *pdev) { struct exynos_pcie *ep = platform_get_drvdata(pdev); @@ -385,8 +385,6 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev) phy_exit(ep->phy); exynos_pcie_deinit_clk_resources(ep); regulator_bulk_disable(ARRAY_SIZE(ep->supplies), ep->supplies); - - return 0; } static int exynos_pcie_suspend_noirq(struct device *dev) @@ -431,7 +429,7 @@ static const struct of_device_id exynos_pcie_of_match[] = { static struct platform_driver exynos_pcie_driver = { .probe = exynos_pcie_probe, - .remove = __exit_p(exynos_pcie_remove), + .remove_new = exynos_pcie_remove, .driver = { .name = "exynos-pcie", .of_match_table = exynos_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 27aaa2a6bf39..99a60270b26c 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -17,8 +17,8 @@ #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> #include <linux/mfd/syscon/imx7-iomuxc-gpr.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/of_gpio.h> -#include <linux/of_device.h> #include <linux/of_address.h> #include <linux/pci.h> #include <linux/platform_device.h> @@ -42,6 +42,19 @@ #define IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE GENMASK(11, 8) #define IMX8MQ_PCIE2_BASE_ADDR 0x33c00000 +#define IMX95_PCIE_PHY_GEN_CTRL 0x0 +#define IMX95_PCIE_REF_USE_PAD BIT(17) + +#define IMX95_PCIE_SS_RW_REG_0 0xf0 +#define IMX95_PCIE_REF_CLKEN BIT(23) +#define IMX95_PCIE_PHY_CR_PARA_SEL BIT(9) + +#define IMX95_PE0_GEN_CTRL_1 0x1050 +#define IMX95_PCIE_DEVICE_TYPE GENMASK(3, 0) + +#define IMX95_PE0_GEN_CTRL_3 0x1058 +#define IMX95_PCIE_LTSSM_EN BIT(0) + #define to_imx6_pcie(x) dev_get_drvdata((x)->dev) enum imx6_pcie_variants { @@ -52,14 +65,29 @@ enum imx6_pcie_variants { IMX8MQ, IMX8MM, IMX8MP, + IMX95, IMX8MQ_EP, IMX8MM_EP, IMX8MP_EP, + IMX95_EP, }; #define IMX6_PCIE_FLAG_IMX6_PHY BIT(0) #define IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE BIT(1) #define IMX6_PCIE_FLAG_SUPPORTS_SUSPEND BIT(2) +#define IMX6_PCIE_FLAG_HAS_PHYDRV BIT(3) +#define IMX6_PCIE_FLAG_HAS_APP_RESET BIT(4) +#define IMX6_PCIE_FLAG_HAS_PHY_RESET BIT(5) +#define IMX6_PCIE_FLAG_HAS_SERDES BIT(6) +#define IMX6_PCIE_FLAG_SUPPORT_64BIT BIT(7) + +#define imx6_check_flag(pci, val) (pci->drvdata->flags & val) + +#define IMX6_PCIE_MAX_CLKS 6 + +#define IMX6_PCIE_MAX_INSTANCES 2 + +struct imx6_pcie; struct imx6_pcie_drvdata { enum imx6_pcie_variants variant; @@ -67,6 +95,14 @@ struct imx6_pcie_drvdata { u32 flags; int dbi_length; const char *gpr; + const char * const *clk_names; + const u32 clks_cnt; + const u32 ltssm_off; + const u32 ltssm_mask; + const u32 mode_off[IMX6_PCIE_MAX_INSTANCES]; + const u32 mode_mask[IMX6_PCIE_MAX_INSTANCES]; + const struct pci_epc_features *epc_features; + int (*init_phy)(struct imx6_pcie *pcie); }; struct imx6_pcie { @@ -74,11 +110,7 @@ struct imx6_pcie { int reset_gpio; bool gpio_active_high; bool link_is_up; - struct clk *pcie_bus; - struct clk *pcie_phy; - struct clk *pcie_inbound_axi; - struct clk *pcie; - struct clk *pcie_aux; + struct clk_bulk_data clks[IMX6_PCIE_MAX_CLKS]; struct regmap *iomuxc_gpr; u16 msi_ctrl; u32 controller_id; @@ -165,34 +197,44 @@ static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie) return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; } +static int imx95_pcie_init_phy(struct imx6_pcie *imx6_pcie) +{ + regmap_update_bits(imx6_pcie->iomuxc_gpr, + IMX95_PCIE_SS_RW_REG_0, + IMX95_PCIE_PHY_CR_PARA_SEL, + IMX95_PCIE_PHY_CR_PARA_SEL); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, + IMX95_PCIE_PHY_GEN_CTRL, + IMX95_PCIE_REF_USE_PAD, 0); + regmap_update_bits(imx6_pcie->iomuxc_gpr, + IMX95_PCIE_SS_RW_REG_0, + IMX95_PCIE_REF_CLKEN, + IMX95_PCIE_REF_CLKEN); + + return 0; +} + static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie) { - unsigned int mask, val, mode; + const struct imx6_pcie_drvdata *drvdata = imx6_pcie->drvdata; + unsigned int mask, val, mode, id; - if (imx6_pcie->drvdata->mode == DW_PCIE_EP_TYPE) + if (drvdata->mode == DW_PCIE_EP_TYPE) mode = PCI_EXP_TYPE_ENDPOINT; else mode = PCI_EXP_TYPE_ROOT_PORT; - switch (imx6_pcie->drvdata->variant) { - case IMX8MQ: - case IMX8MQ_EP: - if (imx6_pcie->controller_id == 1) { - mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE; - val = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, - mode); - } else { - mask = IMX6Q_GPR12_DEVICE_TYPE; - val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, mode); - } - break; - default: - mask = IMX6Q_GPR12_DEVICE_TYPE; - val = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, mode); - break; - } + id = imx6_pcie->controller_id; + + /* If mode_mask[id] is zero, means each controller have its individual gpr */ + if (!drvdata->mode_mask[id]) + id = 0; + + mask = drvdata->mode_mask[id]; + val = mode << (ffs(mask) - 1); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val); + regmap_update_bits(imx6_pcie->iomuxc_gpr, drvdata->mode_off[id], mask, val); } static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, bool exp_val) @@ -320,76 +362,66 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, u16 data) return 0; } -static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) +static int imx8mq_pcie_init_phy(struct imx6_pcie *imx6_pcie) { - switch (imx6_pcie->drvdata->variant) { - case IMX8MM: - case IMX8MM_EP: - case IMX8MP: - case IMX8MP_EP: - /* - * The PHY initialization had been done in the PHY - * driver, break here directly. - */ - break; - case IMX8MQ: - case IMX8MQ_EP: - /* - * TODO: Currently this code assumes external - * oscillator is being used - */ + /* TODO: Currently this code assumes external oscillator is being used */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, + imx6_pcie_grp_offset(imx6_pcie), + IMX8MQ_GPR_PCIE_REF_USE_PAD, + IMX8MQ_GPR_PCIE_REF_USE_PAD); + /* + * Regarding the datasheet, the PCIE_VPH is suggested to be 1.8V. If the PCIE_VPH is + * supplied by 3.3V, the VREG_BYPASS should be cleared to zero. + */ + if (imx6_pcie->vph && regulator_get_voltage(imx6_pcie->vph) > 3000000) regmap_update_bits(imx6_pcie->iomuxc_gpr, imx6_pcie_grp_offset(imx6_pcie), - IMX8MQ_GPR_PCIE_REF_USE_PAD, - IMX8MQ_GPR_PCIE_REF_USE_PAD); - /* - * Regarding the datasheet, the PCIE_VPH is suggested - * to be 1.8V. If the PCIE_VPH is supplied by 3.3V, the - * VREG_BYPASS should be cleared to zero. - */ - if (imx6_pcie->vph && - regulator_get_voltage(imx6_pcie->vph) > 3000000) - regmap_update_bits(imx6_pcie->iomuxc_gpr, - imx6_pcie_grp_offset(imx6_pcie), - IMX8MQ_GPR_PCIE_VREG_BYPASS, - 0); - break; - case IMX7D: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); - break; - case IMX6SX: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6SX_GPR12_PCIE_RX_EQ_MASK, - IMX6SX_GPR12_PCIE_RX_EQ_2); - fallthrough; - default: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX8MQ_GPR_PCIE_VREG_BYPASS, + 0); + + return 0; +} + +static int imx7d_pcie_init_phy(struct imx6_pcie *imx6_pcie) +{ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); + + return 0; +} + +static int imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) +{ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); - /* configure constant input signal to the pcie ctrl and phy */ - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_LOS_LEVEL, 9 << 4); - - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN1, - imx6_pcie->tx_deemph_gen1 << 0); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, - imx6_pcie->tx_deemph_gen2_3p5db << 6); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, - imx6_pcie->tx_deemph_gen2_6db << 12); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_SWING_FULL, - imx6_pcie->tx_swing_full << 18); - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, - IMX6Q_GPR8_TX_SWING_LOW, - imx6_pcie->tx_swing_low << 25); - break; - } + /* configure constant input signal to the pcie ctrl and phy */ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6Q_GPR12_LOS_LEVEL, 9 << 4); + + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN1, + imx6_pcie->tx_deemph_gen1 << 0); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, + imx6_pcie->tx_deemph_gen2_3p5db << 6); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, + imx6_pcie->tx_deemph_gen2_6db << 12); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_SWING_FULL, + imx6_pcie->tx_swing_full << 18); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, + IMX6Q_GPR8_TX_SWING_LOW, + imx6_pcie->tx_swing_low << 25); + return 0; +} - imx6_pcie_configure_type(imx6_pcie); +static int imx6sx_pcie_init_phy(struct imx6_pcie *imx6_pcie) +{ + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_RX_EQ_MASK, IMX6SX_GPR12_PCIE_RX_EQ_2); + + return imx6_pcie_init_phy(imx6_pcie); } static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) @@ -407,13 +439,18 @@ static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie) { - unsigned long phy_rate = clk_get_rate(imx6_pcie->pcie_phy); + unsigned long phy_rate = 0; int mult, div; u16 val; + int i; if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_IMX6_PHY)) return 0; + for (i = 0; i < imx6_pcie->drvdata->clks_cnt; i++) + if (strncmp(imx6_pcie->clks[i].id, "pcie_phy", 8) == 0) + phy_rate = clk_get_rate(imx6_pcie->clks[i].clk); + switch (phy_rate) { case 125000000: /* @@ -550,19 +587,11 @@ static int imx6_pcie_attach_pd(struct device *dev) static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) { - struct dw_pcie *pci = imx6_pcie->pci; - struct device *dev = pci->dev; unsigned int offset; int ret = 0; switch (imx6_pcie->drvdata->variant) { case IMX6SX: - ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi); - if (ret) { - dev_err(dev, "unable to enable pcie_axi clock\n"); - break; - } - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0); break; @@ -582,6 +611,8 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16); break; case IMX7D: + case IMX95: + case IMX95_EP: break; case IMX8MM: case IMX8MM_EP: @@ -589,12 +620,6 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) case IMX8MQ_EP: case IMX8MP: case IMX8MP_EP: - ret = clk_prepare_enable(imx6_pcie->pcie_aux); - if (ret) { - dev_err(dev, "unable to enable pcie_aux clock\n"); - break; - } - offset = imx6_pcie_grp_offset(imx6_pcie); /* * Set the over ride low and enabled @@ -615,9 +640,6 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) static void imx6_pcie_disable_ref_clk(struct imx6_pcie *imx6_pcie) { switch (imx6_pcie->drvdata->variant) { - case IMX6SX: - clk_disable_unprepare(imx6_pcie->pcie_inbound_axi); - break; case IMX6QP: case IMX6Q: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, @@ -631,14 +653,6 @@ static void imx6_pcie_disable_ref_clk(struct imx6_pcie *imx6_pcie) IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, IMX7D_GPR12_PCIE_PHY_REFCLK_SEL); break; - case IMX8MM: - case IMX8MM_EP: - case IMX8MQ: - case IMX8MQ_EP: - case IMX8MP: - case IMX8MP_EP: - clk_disable_unprepare(imx6_pcie->pcie_aux); - break; default: break; } @@ -650,23 +664,9 @@ static int imx6_pcie_clk_enable(struct imx6_pcie *imx6_pcie) struct device *dev = pci->dev; int ret; - ret = clk_prepare_enable(imx6_pcie->pcie_phy); - if (ret) { - dev_err(dev, "unable to enable pcie_phy clock\n"); + ret = clk_bulk_prepare_enable(imx6_pcie->drvdata->clks_cnt, imx6_pcie->clks); + if (ret) return ret; - } - - ret = clk_prepare_enable(imx6_pcie->pcie_bus); - if (ret) { - dev_err(dev, "unable to enable pcie_bus clock\n"); - goto err_pcie_bus; - } - - ret = clk_prepare_enable(imx6_pcie->pcie); - if (ret) { - dev_err(dev, "unable to enable pcie clock\n"); - goto err_pcie; - } ret = imx6_pcie_enable_ref_clk(imx6_pcie); if (ret) { @@ -679,11 +679,7 @@ static int imx6_pcie_clk_enable(struct imx6_pcie *imx6_pcie) return 0; err_ref_clk: - clk_disable_unprepare(imx6_pcie->pcie); -err_pcie: - clk_disable_unprepare(imx6_pcie->pcie_bus); -err_pcie_bus: - clk_disable_unprepare(imx6_pcie->pcie_phy); + clk_bulk_disable_unprepare(imx6_pcie->drvdata->clks_cnt, imx6_pcie->clks); return ret; } @@ -691,25 +687,15 @@ err_pcie_bus: static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie) { imx6_pcie_disable_ref_clk(imx6_pcie); - clk_disable_unprepare(imx6_pcie->pcie); - clk_disable_unprepare(imx6_pcie->pcie_bus); - clk_disable_unprepare(imx6_pcie->pcie_phy); + clk_bulk_disable_unprepare(imx6_pcie->drvdata->clks_cnt, imx6_pcie->clks); } static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { + reset_control_assert(imx6_pcie->pciephy_reset); + reset_control_assert(imx6_pcie->apps_reset); + switch (imx6_pcie->drvdata->variant) { - case IMX7D: - case IMX8MQ: - case IMX8MQ_EP: - reset_control_assert(imx6_pcie->pciephy_reset); - fallthrough; - case IMX8MM: - case IMX8MM_EP: - case IMX8MP: - case IMX8MP_EP: - reset_control_assert(imx6_pcie->apps_reset); - break; case IMX6SX: regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_TEST_POWERDOWN, @@ -730,6 +716,8 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); break; + default: + break; } /* Some boards don't have PCIe reset GPIO. */ @@ -743,14 +731,10 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) struct dw_pcie *pci = imx6_pcie->pci; struct device *dev = pci->dev; + reset_control_deassert(imx6_pcie->pciephy_reset); + switch (imx6_pcie->drvdata->variant) { - case IMX8MQ: - case IMX8MQ_EP: - reset_control_deassert(imx6_pcie->pciephy_reset); - break; case IMX7D: - reset_control_deassert(imx6_pcie->pciephy_reset); - /* Workaround for ERR010728, failure of PCI-e PLL VCO to * oscillate, especially when cold. This turns off "Duty-cycle * Corrector" and other mysterious undocumented things. @@ -782,11 +766,7 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) usleep_range(200, 500); break; - case IMX6Q: /* Nothing to do */ - case IMX8MM: - case IMX8MM_EP: - case IMX8MP: - case IMX8MP_EP: + default: break; } @@ -824,48 +804,25 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) static void imx6_pcie_ltssm_enable(struct device *dev) { struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); + const struct imx6_pcie_drvdata *drvdata = imx6_pcie->drvdata; - switch (imx6_pcie->drvdata->variant) { - case IMX6Q: - case IMX6SX: - case IMX6QP: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, - IMX6Q_GPR12_PCIE_CTL_2); - break; - case IMX7D: - case IMX8MQ: - case IMX8MQ_EP: - case IMX8MM: - case IMX8MM_EP: - case IMX8MP: - case IMX8MP_EP: - reset_control_deassert(imx6_pcie->apps_reset); - break; - } + if (drvdata->ltssm_mask) + regmap_update_bits(imx6_pcie->iomuxc_gpr, drvdata->ltssm_off, drvdata->ltssm_mask, + drvdata->ltssm_mask); + + reset_control_deassert(imx6_pcie->apps_reset); } static void imx6_pcie_ltssm_disable(struct device *dev) { struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); + const struct imx6_pcie_drvdata *drvdata = imx6_pcie->drvdata; - switch (imx6_pcie->drvdata->variant) { - case IMX6Q: - case IMX6SX: - case IMX6QP: - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6Q_GPR12_PCIE_CTL_2, 0); - break; - case IMX7D: - case IMX8MQ: - case IMX8MQ_EP: - case IMX8MM: - case IMX8MM_EP: - case IMX8MP: - case IMX8MP_EP: - reset_control_assert(imx6_pcie->apps_reset); - break; - } + if (drvdata->ltssm_mask) + regmap_update_bits(imx6_pcie->iomuxc_gpr, drvdata->ltssm_off, + drvdata->ltssm_mask, 0); + + reset_control_assert(imx6_pcie->apps_reset); } static int imx6_pcie_start_link(struct dw_pcie *pci) @@ -977,7 +934,11 @@ static int imx6_pcie_host_init(struct dw_pcie_rp *pp) } imx6_pcie_assert_core_reset(imx6_pcie); - imx6_pcie_init_phy(imx6_pcie); + + if (imx6_pcie->drvdata->init_phy) + imx6_pcie->drvdata->init_phy(imx6_pcie); + + imx6_pcie_configure_type(imx6_pcie); ret = imx6_pcie_clk_enable(imx6_pcie); if (ret) { @@ -1039,7 +1000,8 @@ static void imx6_pcie_host_exit(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops imx6_pcie_host_ops = { - .host_init = imx6_pcie_host_init, + .init = imx6_pcie_host_init, + .deinit = imx6_pcie_host_exit, }; static const struct dw_pcie_ops dw_pcie_ops = { @@ -1057,17 +1019,16 @@ static void imx6_pcie_ep_init(struct dw_pcie_ep *ep) } static int imx6_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "UNKNOWN IRQ type\n"); @@ -1081,18 +1042,39 @@ static const struct pci_epc_features imx8m_pcie_epc_features = { .linkup_notifier = false, .msi_capable = true, .msix_capable = false, - .reserved_bar = 1 << BAR_1 | 1 << BAR_3, + .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_3] = { .type = BAR_RESERVED, }, .align = SZ_64K, }; +/* + * BAR# | Default BAR enable | Default BAR Type | Default BAR Size | BAR Sizing Scheme + * ================================================================================================ + * BAR0 | Enable | 64-bit | 1 MB | Programmable Size + * BAR1 | Disable | 32-bit | 64 KB | Fixed Size + * BAR1 should be disabled if BAR0 is 64bit. + * BAR2 | Enable | 32-bit | 1 MB | Programmable Size + * BAR3 | Enable | 32-bit | 64 KB | Programmable Size + * BAR4 | Enable | 32-bit | 1M | Programmable Size + * BAR5 | Enable | 32-bit | 64 KB | Programmable Size + */ +static const struct pci_epc_features imx95_pcie_epc_features = { + .msi_capable = true, + .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, }, + .align = SZ_4K, +}; + static const struct pci_epc_features* imx6_pcie_ep_get_features(struct dw_pcie_ep *ep) { - return &imx8m_pcie_epc_features; + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); + + return imx6_pcie->drvdata->epc_features; } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = imx6_pcie_ep_init, + .init = imx6_pcie_ep_init, .raise_irq = imx6_pcie_ep_raise_irq, .get_features = imx6_pcie_ep_get_features, }; @@ -1103,7 +1085,6 @@ static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie, int ret; unsigned int pcie_dbi2_offset; struct dw_pcie_ep *ep; - struct resource *res; struct dw_pcie *pci = imx6_pcie->pci; struct dw_pcie_rp *pp = &pci->pp; struct device *dev = pci->dev; @@ -1122,14 +1103,20 @@ static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie, pcie_dbi2_offset = SZ_4K; break; } + pci->dbi_base2 = pci->dbi_base + pcie_dbi2_offset; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); - if (!res) - return -EINVAL; - ep->phys_base = res->start; - ep->addr_size = resource_size(res); - ep->page_size = SZ_64K; + /* + * FIXME: Ideally, dbi2 base address should come from DT. But since only IMX95 is defining + * "dbi2" in DT, "dbi_base2" is set to NULL here for that platform alone so that the DWC + * core code can fetch that from DT. But once all platform DTs were fixed, this and the + * above "dbi_base2" setting should be removed. + */ + if (device_property_match_string(dev, "reg-names", "dbi2") >= 0) + pci->dbi_base2 = NULL; + + if (imx6_check_flag(imx6_pcie, IMX6_PCIE_FLAG_SUPPORT_64BIT)) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ret = dw_pcie_ep_init(ep); if (ret) { @@ -1251,6 +1238,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; int ret; u16 val; + int i; imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL); if (!imx6_pcie) @@ -1282,8 +1270,7 @@ static int imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx6_pcie->phy_base); } - dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pci->dbi_base = devm_ioremap_resource(dev, dbi_base); + pci->dbi_base = devm_platform_get_and_ioremap_resource(pdev, 0, &dbi_base); if (IS_ERR(pci->dbi_base)) return PTR_ERR(pci->dbi_base); @@ -1305,81 +1292,48 @@ static int imx6_pcie_probe(struct platform_device *pdev) return imx6_pcie->reset_gpio; } - /* Fetch clocks */ - imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus"); - if (IS_ERR(imx6_pcie->pcie_bus)) - return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_bus), - "pcie_bus clock source missing or invalid\n"); + if (imx6_pcie->drvdata->clks_cnt >= IMX6_PCIE_MAX_CLKS) + return dev_err_probe(dev, -ENOMEM, "clks_cnt is too big\n"); - imx6_pcie->pcie = devm_clk_get(dev, "pcie"); - if (IS_ERR(imx6_pcie->pcie)) - return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie), - "pcie clock source missing or invalid\n"); + for (i = 0; i < imx6_pcie->drvdata->clks_cnt; i++) + imx6_pcie->clks[i].id = imx6_pcie->drvdata->clk_names[i]; - switch (imx6_pcie->drvdata->variant) { - case IMX6SX: - imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, - "pcie_inbound_axi"); - if (IS_ERR(imx6_pcie->pcie_inbound_axi)) - return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_inbound_axi), - "pcie_inbound_axi clock missing or invalid\n"); - break; - case IMX8MQ: - case IMX8MQ_EP: - imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux"); - if (IS_ERR(imx6_pcie->pcie_aux)) - return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux), - "pcie_aux clock source missing or invalid\n"); - fallthrough; - case IMX7D: - if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR) - imx6_pcie->controller_id = 1; + /* Fetch clocks */ + ret = devm_clk_bulk_get(dev, imx6_pcie->drvdata->clks_cnt, imx6_pcie->clks); + if (ret) + return ret; - imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev, - "pciephy"); - if (IS_ERR(imx6_pcie->pciephy_reset)) { - dev_err(dev, "Failed to get PCIEPHY reset control\n"); - return PTR_ERR(imx6_pcie->pciephy_reset); - } + if (imx6_check_flag(imx6_pcie, IMX6_PCIE_FLAG_HAS_PHYDRV)) { + imx6_pcie->phy = devm_phy_get(dev, "pcie-phy"); + if (IS_ERR(imx6_pcie->phy)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->phy), + "failed to get pcie phy\n"); + } - imx6_pcie->apps_reset = devm_reset_control_get_exclusive(dev, - "apps"); - if (IS_ERR(imx6_pcie->apps_reset)) { - dev_err(dev, "Failed to get PCIE APPS reset control\n"); - return PTR_ERR(imx6_pcie->apps_reset); - } - break; - case IMX8MM: - case IMX8MM_EP: - case IMX8MP: - case IMX8MP_EP: - imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux"); - if (IS_ERR(imx6_pcie->pcie_aux)) - return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux), - "pcie_aux clock source missing or invalid\n"); - imx6_pcie->apps_reset = devm_reset_control_get_exclusive(dev, - "apps"); + if (imx6_check_flag(imx6_pcie, IMX6_PCIE_FLAG_HAS_APP_RESET)) { + imx6_pcie->apps_reset = devm_reset_control_get_exclusive(dev, "apps"); if (IS_ERR(imx6_pcie->apps_reset)) return dev_err_probe(dev, PTR_ERR(imx6_pcie->apps_reset), "failed to get pcie apps reset control\n"); + } - imx6_pcie->phy = devm_phy_get(dev, "pcie-phy"); - if (IS_ERR(imx6_pcie->phy)) - return dev_err_probe(dev, PTR_ERR(imx6_pcie->phy), - "failed to get pcie phy\n"); + if (imx6_check_flag(imx6_pcie, IMX6_PCIE_FLAG_HAS_PHY_RESET)) { + imx6_pcie->pciephy_reset = devm_reset_control_get_exclusive(dev, "pciephy"); + if (IS_ERR(imx6_pcie->pciephy_reset)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->pciephy_reset), + "Failed to get PCIEPHY reset control\n"); + } + switch (imx6_pcie->drvdata->variant) { + case IMX8MQ: + case IMX8MQ_EP: + case IMX7D: + if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR) + imx6_pcie->controller_id = 1; break; default: break; } - /* Don't fetch the pcie_phy clock, if it has abstract PHY driver */ - if (imx6_pcie->phy == NULL) { - imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy"); - if (IS_ERR(imx6_pcie->pcie_phy)) - return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_phy), - "pcie_phy clock source missing or invalid\n"); - } - /* Grab turnoff reset */ imx6_pcie->turnoff_reset = devm_reset_control_get_optional_exclusive(dev, "turnoff"); @@ -1388,12 +1342,32 @@ static int imx6_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx6_pcie->turnoff_reset); } + if (imx6_pcie->drvdata->gpr) { /* Grab GPR config register range */ - imx6_pcie->iomuxc_gpr = - syscon_regmap_lookup_by_compatible(imx6_pcie->drvdata->gpr); - if (IS_ERR(imx6_pcie->iomuxc_gpr)) { - dev_err(dev, "unable to find iomuxc registers\n"); - return PTR_ERR(imx6_pcie->iomuxc_gpr); + imx6_pcie->iomuxc_gpr = + syscon_regmap_lookup_by_compatible(imx6_pcie->drvdata->gpr); + if (IS_ERR(imx6_pcie->iomuxc_gpr)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->iomuxc_gpr), + "unable to find iomuxc registers\n"); + } + + if (imx6_check_flag(imx6_pcie, IMX6_PCIE_FLAG_HAS_SERDES)) { + void __iomem *off = devm_platform_ioremap_resource_byname(pdev, "app"); + + if (IS_ERR(off)) + return dev_err_probe(dev, PTR_ERR(off), + "unable to find serdes registers\n"); + + static const struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + + imx6_pcie->iomuxc_gpr = devm_regmap_init_mmio(dev, off, ®map_config); + if (IS_ERR(imx6_pcie->iomuxc_gpr)) + return dev_err_probe(dev, PTR_ERR(imx6_pcie->iomuxc_gpr), + "unable to find iomuxc registers\n"); } /* Grab PCIe PHY Tx Settings */ @@ -1470,6 +1444,11 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) imx6_pcie_assert_core_reset(imx6_pcie); } +static const char * const imx6q_clks[] = {"pcie_bus", "pcie", "pcie_phy"}; +static const char * const imx8mm_clks[] = {"pcie_bus", "pcie", "pcie_aux"}; +static const char * const imx8mq_clks[] = {"pcie_bus", "pcie", "pcie_phy", "pcie_aux"}; +static const char * const imx6sx_clks[] = {"pcie_bus", "pcie", "pcie_phy", "pcie_inbound_axi"}; + static const struct imx6_pcie_drvdata drvdata[] = { [IMX6Q] = { .variant = IMX6Q, @@ -1477,6 +1456,13 @@ static const struct imx6_pcie_drvdata drvdata[] = { IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", + .clk_names = imx6q_clks, + .clks_cnt = ARRAY_SIZE(imx6q_clks), + .ltssm_off = IOMUXC_GPR12, + .ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2, + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + .init_phy = imx6_pcie_init_phy, }, [IMX6SX] = { .variant = IMX6SX, @@ -1484,6 +1470,13 @@ static const struct imx6_pcie_drvdata drvdata[] = { IMX6_PCIE_FLAG_IMX6_SPEED_CHANGE | IMX6_PCIE_FLAG_SUPPORTS_SUSPEND, .gpr = "fsl,imx6q-iomuxc-gpr", + .clk_names = imx6sx_clks, + .clks_cnt = ARRAY_SIZE(imx6sx_clks), + .ltssm_off = IOMUXC_GPR12, + .ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2, + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + .init_phy = imx6sx_pcie_init_phy, }, [IMX6QP] = { .variant = IMX6QP, @@ -1492,40 +1485,122 @@ static const struct imx6_pcie_drvdata drvdata[] = { IMX6_PCIE_FLAG_SUPPORTS_SUSPEND, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", + .clk_names = imx6q_clks, + .clks_cnt = ARRAY_SIZE(imx6q_clks), + .ltssm_off = IOMUXC_GPR12, + .ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2, + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + .init_phy = imx6_pcie_init_phy, }, [IMX7D] = { .variant = IMX7D, - .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND, + .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX6_PCIE_FLAG_HAS_APP_RESET | + IMX6_PCIE_FLAG_HAS_PHY_RESET, .gpr = "fsl,imx7d-iomuxc-gpr", + .clk_names = imx6q_clks, + .clks_cnt = ARRAY_SIZE(imx6q_clks), + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + .init_phy = imx7d_pcie_init_phy, }, [IMX8MQ] = { .variant = IMX8MQ, + .flags = IMX6_PCIE_FLAG_HAS_APP_RESET | + IMX6_PCIE_FLAG_HAS_PHY_RESET, .gpr = "fsl,imx8mq-iomuxc-gpr", + .clk_names = imx8mq_clks, + .clks_cnt = ARRAY_SIZE(imx8mq_clks), + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + .mode_off[1] = IOMUXC_GPR12, + .mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, + .init_phy = imx8mq_pcie_init_phy, }, [IMX8MM] = { .variant = IMX8MM, - .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND, + .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX6_PCIE_FLAG_HAS_PHYDRV | + IMX6_PCIE_FLAG_HAS_APP_RESET, .gpr = "fsl,imx8mm-iomuxc-gpr", + .clk_names = imx8mm_clks, + .clks_cnt = ARRAY_SIZE(imx8mm_clks), + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, }, [IMX8MP] = { .variant = IMX8MP, - .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND, + .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX6_PCIE_FLAG_HAS_PHYDRV | + IMX6_PCIE_FLAG_HAS_APP_RESET, .gpr = "fsl,imx8mp-iomuxc-gpr", + .clk_names = imx8mm_clks, + .clks_cnt = ARRAY_SIZE(imx8mm_clks), + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + }, + [IMX95] = { + .variant = IMX95, + .flags = IMX6_PCIE_FLAG_HAS_SERDES, + .clk_names = imx8mq_clks, + .clks_cnt = ARRAY_SIZE(imx8mq_clks), + .ltssm_off = IMX95_PE0_GEN_CTRL_3, + .ltssm_mask = IMX95_PCIE_LTSSM_EN, + .mode_off[0] = IMX95_PE0_GEN_CTRL_1, + .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, + .init_phy = imx95_pcie_init_phy, }, [IMX8MQ_EP] = { .variant = IMX8MQ_EP, + .flags = IMX6_PCIE_FLAG_HAS_APP_RESET | + IMX6_PCIE_FLAG_HAS_PHY_RESET, .mode = DW_PCIE_EP_TYPE, .gpr = "fsl,imx8mq-iomuxc-gpr", + .clk_names = imx8mq_clks, + .clks_cnt = ARRAY_SIZE(imx8mq_clks), + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + .mode_off[1] = IOMUXC_GPR12, + .mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, + .epc_features = &imx8m_pcie_epc_features, + .init_phy = imx8mq_pcie_init_phy, }, [IMX8MM_EP] = { .variant = IMX8MM_EP, + .flags = IMX6_PCIE_FLAG_HAS_PHYDRV, .mode = DW_PCIE_EP_TYPE, .gpr = "fsl,imx8mm-iomuxc-gpr", + .clk_names = imx8mm_clks, + .clks_cnt = ARRAY_SIZE(imx8mm_clks), + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + .epc_features = &imx8m_pcie_epc_features, }, [IMX8MP_EP] = { .variant = IMX8MP_EP, + .flags = IMX6_PCIE_FLAG_HAS_PHYDRV, .mode = DW_PCIE_EP_TYPE, .gpr = "fsl,imx8mp-iomuxc-gpr", + .clk_names = imx8mm_clks, + .clks_cnt = ARRAY_SIZE(imx8mm_clks), + .mode_off[0] = IOMUXC_GPR12, + .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, + .epc_features = &imx8m_pcie_epc_features, + }, + [IMX95_EP] = { + .variant = IMX95_EP, + .flags = IMX6_PCIE_FLAG_HAS_SERDES | + IMX6_PCIE_FLAG_SUPPORT_64BIT, + .clk_names = imx8mq_clks, + .clks_cnt = ARRAY_SIZE(imx8mq_clks), + .ltssm_off = IMX95_PE0_GEN_CTRL_3, + .ltssm_mask = IMX95_PCIE_LTSSM_EN, + .mode_off[0] = IMX95_PE0_GEN_CTRL_1, + .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, + .init_phy = imx95_pcie_init_phy, + .epc_features = &imx95_pcie_epc_features, + .mode = DW_PCIE_EP_TYPE, }, }; @@ -1537,9 +1612,11 @@ static const struct of_device_id imx6_pcie_of_match[] = { { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], }, { .compatible = "fsl,imx8mm-pcie", .data = &drvdata[IMX8MM], }, { .compatible = "fsl,imx8mp-pcie", .data = &drvdata[IMX8MP], }, + { .compatible = "fsl,imx95-pcie", .data = &drvdata[IMX95], }, { .compatible = "fsl,imx8mq-pcie-ep", .data = &drvdata[IMX8MQ_EP], }, { .compatible = "fsl,imx8mm-pcie-ep", .data = &drvdata[IMX8MM_EP], }, { .compatible = "fsl,imx8mp-pcie-ep", .data = &drvdata[IMX8MP_EP], }, + { .compatible = "fsl,imx95-pcie-ep", .data = &drvdata[IMX95_EP], }, {}, }; diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 78818853af9e..844de4418724 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -19,7 +19,6 @@ #include <linux/mfd/syscon.h> #include <linux/msi.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_pci.h> #include <linux/phy/phy.h> @@ -116,8 +115,7 @@ struct keystone_pcie { struct dw_pcie *pci; /* PCI Device ID */ u32 device_id; - int legacy_host_irqs[PCI_NUM_INTX]; - struct device_node *legacy_intc_np; + int intx_host_irqs[PCI_NUM_INTX]; int msi_host_irq; int num_lanes; @@ -125,7 +123,7 @@ struct keystone_pcie { struct phy **phy; struct device_link **link; struct device_node *msi_intc_np; - struct irq_domain *legacy_irq_domain; + struct irq_domain *intx_irq_domain; struct device_node *np; /* Application register space */ @@ -253,8 +251,8 @@ static int ks_pcie_msi_host_init(struct dw_pcie_rp *pp) return dw_pcie_allocate_domains(pp); } -static void ks_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, - int offset) +static void ks_pcie_handle_intx_irq(struct keystone_pcie *ks_pcie, + int offset) { struct dw_pcie *pci = ks_pcie->pci; struct device *dev = pci->dev; @@ -264,7 +262,7 @@ static void ks_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, if (BIT(0) & pending) { dev_dbg(dev, ": irq: irq_offset %d", offset); - generic_handle_domain_irq(ks_pcie->legacy_irq_domain, offset); + generic_handle_domain_irq(ks_pcie->intx_irq_domain, offset); } /* EOI the INTx interrupt */ @@ -308,38 +306,37 @@ static irqreturn_t ks_pcie_handle_error_irq(struct keystone_pcie *ks_pcie) return IRQ_HANDLED; } -static void ks_pcie_ack_legacy_irq(struct irq_data *d) +static void ks_pcie_ack_intx_irq(struct irq_data *d) { } -static void ks_pcie_mask_legacy_irq(struct irq_data *d) +static void ks_pcie_mask_intx_irq(struct irq_data *d) { } -static void ks_pcie_unmask_legacy_irq(struct irq_data *d) +static void ks_pcie_unmask_intx_irq(struct irq_data *d) { } -static struct irq_chip ks_pcie_legacy_irq_chip = { - .name = "Keystone-PCI-Legacy-IRQ", - .irq_ack = ks_pcie_ack_legacy_irq, - .irq_mask = ks_pcie_mask_legacy_irq, - .irq_unmask = ks_pcie_unmask_legacy_irq, +static struct irq_chip ks_pcie_intx_irq_chip = { + .name = "Keystone-PCI-INTX-IRQ", + .irq_ack = ks_pcie_ack_intx_irq, + .irq_mask = ks_pcie_mask_intx_irq, + .irq_unmask = ks_pcie_unmask_intx_irq, }; -static int ks_pcie_init_legacy_irq_map(struct irq_domain *d, - unsigned int irq, - irq_hw_number_t hw_irq) +static int ks_pcie_init_intx_irq_map(struct irq_domain *d, + unsigned int irq, irq_hw_number_t hw_irq) { - irq_set_chip_and_handler(irq, &ks_pcie_legacy_irq_chip, + irq_set_chip_and_handler(irq, &ks_pcie_intx_irq_chip, handle_level_irq); irq_set_chip_data(irq, d->host_data); return 0; } -static const struct irq_domain_ops ks_pcie_legacy_irq_domain_ops = { - .map = ks_pcie_init_legacy_irq_map, +static const struct irq_domain_ops ks_pcie_intx_irq_domain_ops = { + .map = ks_pcie_init_intx_irq_map, .xlate = irq_domain_xlate_onetwocell, }; @@ -606,22 +603,22 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc) } /** - * ks_pcie_legacy_irq_handler() - Handle legacy interrupt + * ks_pcie_intx_irq_handler() - Handle INTX interrupt * @desc: Pointer to irq descriptor * - * Traverse through pending legacy interrupts and invoke handler for each. Also + * Traverse through pending INTX interrupts and invoke handler for each. Also * takes care of interrupt controller level mask/ack operation. */ -static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) +static void ks_pcie_intx_irq_handler(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); struct dw_pcie *pci = ks_pcie->pci; struct device *dev = pci->dev; - u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0]; + u32 irq_offset = irq - ks_pcie->intx_host_irqs[0]; struct irq_chip *chip = irq_desc_get_chip(desc); - dev_dbg(dev, ": Handling legacy irq %d\n", irq); + dev_dbg(dev, ": Handling INTX irq %d\n", irq); /* * The chained irq handler installation would have replaced normal @@ -629,7 +626,7 @@ static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) * ack operation. */ chained_irq_enter(chip, desc); - ks_pcie_handle_legacy_irq(ks_pcie, irq_offset); + ks_pcie_handle_intx_irq(ks_pcie, irq_offset); chained_irq_exit(chip, desc); } @@ -687,10 +684,10 @@ err: return ret; } -static int ks_pcie_config_legacy_irq(struct keystone_pcie *ks_pcie) +static int ks_pcie_config_intx_irq(struct keystone_pcie *ks_pcie) { struct device *dev = ks_pcie->pci->dev; - struct irq_domain *legacy_irq_domain; + struct irq_domain *intx_irq_domain; struct device_node *np = ks_pcie->np; struct device_node *intc_np; int irq_count, irq, ret = 0, i; @@ -698,7 +695,7 @@ static int ks_pcie_config_legacy_irq(struct keystone_pcie *ks_pcie) intc_np = of_get_child_by_name(np, "legacy-interrupt-controller"); if (!intc_np) { /* - * Since legacy interrupts are modeled as edge-interrupts in + * Since INTX interrupts are modeled as edge-interrupts in * AM6, keep it disabled for now. */ if (ks_pcie->is_am6) @@ -720,22 +717,21 @@ static int ks_pcie_config_legacy_irq(struct keystone_pcie *ks_pcie) ret = -EINVAL; goto err; } - ks_pcie->legacy_host_irqs[i] = irq; + ks_pcie->intx_host_irqs[i] = irq; irq_set_chained_handler_and_data(irq, - ks_pcie_legacy_irq_handler, + ks_pcie_intx_irq_handler, ks_pcie); } - legacy_irq_domain = - irq_domain_add_linear(intc_np, PCI_NUM_INTX, - &ks_pcie_legacy_irq_domain_ops, NULL); - if (!legacy_irq_domain) { - dev_err(dev, "Failed to add irq domain for legacy irqs\n"); + intx_irq_domain = irq_domain_add_linear(intc_np, PCI_NUM_INTX, + &ks_pcie_intx_irq_domain_ops, NULL); + if (!intx_irq_domain) { + dev_err(dev, "Failed to add irq domain for INTX irqs\n"); ret = -EINVAL; goto err; } - ks_pcie->legacy_irq_domain = legacy_irq_domain; + ks_pcie->intx_irq_domain = intx_irq_domain; for (i = 0; i < PCI_NUM_INTX; i++) ks_pcie_app_writel(ks_pcie, IRQ_ENABLE_SET(i), INTx_EN); @@ -809,7 +805,7 @@ static int __init ks_pcie_host_init(struct dw_pcie_rp *pp) if (!ks_pcie->is_am6) pp->bridge->child_ops = &ks_child_pcie_ops; - ret = ks_pcie_config_legacy_irq(ks_pcie); + ret = ks_pcie_config_intx_irq(ks_pcie); if (ret) return ret; @@ -839,12 +835,12 @@ static int __init ks_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops ks_pcie_host_ops = { - .host_init = ks_pcie_host_init, - .msi_host_init = ks_pcie_msi_host_init, + .init = ks_pcie_host_init, + .msi_init = ks_pcie_msi_host_init, }; static const struct dw_pcie_host_ops ks_pcie_am654_host_ops = { - .host_init = ks_pcie_host_init, + .init = ks_pcie_host_init, }; static irqreturn_t ks_pcie_err_irq_handler(int irq, void *priv) @@ -882,7 +878,7 @@ static void ks_pcie_am654_ep_init(struct dw_pcie_ep *ep) dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, flags); } -static void ks_pcie_am654_raise_legacy_irq(struct keystone_pcie *ks_pcie) +static void ks_pcie_am654_raise_intx_irq(struct keystone_pcie *ks_pcie) { struct dw_pcie *pci = ks_pcie->pci; u8 int_pin; @@ -901,20 +897,19 @@ static void ks_pcie_am654_raise_legacy_irq(struct keystone_pcie *ks_pcie) } static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); switch (type) { - case PCI_EPC_IRQ_LEGACY: - ks_pcie_am654_raise_legacy_irq(ks_pcie); + case PCI_IRQ_INTX: + ks_pcie_am654_raise_intx_irq(ks_pcie); break; - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); break; - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); break; default: @@ -929,12 +924,12 @@ static const struct pci_epc_features ks_pcie_am654_epc_features = { .linkup_notifier = false, .msi_capable = true, .msix_capable = true, - .reserved_bar = 1 << BAR_0 | 1 << BAR_1, - .bar_fixed_64bit = 1 << BAR_0, - .bar_fixed_size[2] = SZ_1M, - .bar_fixed_size[3] = SZ_64K, - .bar_fixed_size[4] = 256, - .bar_fixed_size[5] = SZ_1M, + .bar[BAR_0] = { .type = BAR_RESERVED, }, + .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_2] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, + .bar[BAR_3] = { .type = BAR_FIXED, .fixed_size = SZ_64K, }, + .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256, }, + .bar[BAR_5] = { .type = BAR_FIXED, .fixed_size = SZ_1M, }, .align = SZ_1M, }; @@ -945,7 +940,7 @@ ks_pcie_am654_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops ks_pcie_am654_ep_ops = { - .ep_init = ks_pcie_am654_ep_init, + .init = ks_pcie_am654_ep_init, .raise_irq = ks_pcie_am654_raise_irq, .get_features = &ks_pcie_am654_get_features, }; @@ -1101,7 +1096,7 @@ static const struct of_device_id ks_pcie_of_match[] = { { }, }; -static int __init ks_pcie_probe(struct platform_device *pdev) +static int ks_pcie_probe(struct platform_device *pdev) { const struct dw_pcie_host_ops *host_ops; const struct dw_pcie_ep_ops *ep_ops; @@ -1219,7 +1214,16 @@ static int __init ks_pcie_probe(struct platform_device *pdev) goto err_link; } + /* Obtain references to the PHYs */ + for (i = 0; i < num_lanes; i++) + phy_pm_runtime_get_sync(ks_pcie->phy[i]); + ret = ks_pcie_enable_phy(ks_pcie); + + /* Release references to the PHYs */ + for (i = 0; i < num_lanes; i++) + phy_pm_runtime_put_sync(ks_pcie->phy[i]); + if (ret) { dev_err(dev, "failed to enable phy\n"); goto err_link; @@ -1303,7 +1307,7 @@ err_link: return ret; } -static int __exit ks_pcie_remove(struct platform_device *pdev) +static void ks_pcie_remove(struct platform_device *pdev) { struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev); struct device_link **link = ks_pcie->link; @@ -1315,13 +1319,11 @@ static int __exit ks_pcie_remove(struct platform_device *pdev) ks_pcie_disable_phy(ks_pcie); while (num_lanes--) device_link_del(link[num_lanes]); - - return 0; } -static struct platform_driver ks_pcie_driver __refdata = { +static struct platform_driver ks_pcie_driver = { .probe = ks_pcie_probe, - .remove = __exit_p(ks_pcie_remove), + .remove_new = ks_pcie_remove, .driver = { .name = "keystone-pcie", .of_match_table = ks_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index de4c1758a6c3..1f6ee1460ec2 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -45,10 +45,11 @@ struct ls_pcie_ep { struct pci_epc_features *ls_epc; const struct ls_pcie_ep_drvdata *drvdata; int irq; + u32 lnkcap; bool big_endian; }; -static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset) +static u32 ls_pcie_pf_lut_readl(struct ls_pcie_ep *pcie, u32 offset) { struct dw_pcie *pci = pcie->pci; @@ -58,7 +59,7 @@ static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset) return ioread32(pci->dbi_base + offset); } -static void ls_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value) +static void ls_pcie_pf_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value) { struct dw_pcie *pci = pcie->pci; @@ -73,22 +74,37 @@ static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id) struct ls_pcie_ep *pcie = dev_id; struct dw_pcie *pci = pcie->pci; u32 val, cfg; + u8 offset; - val = ls_lut_readl(pcie, PEX_PF0_PME_MES_DR); - ls_lut_writel(pcie, PEX_PF0_PME_MES_DR, val); + val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_DR); + ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_DR, val); if (!val) return IRQ_NONE; if (val & PEX_PF0_PME_MES_DR_LUD) { - cfg = ls_lut_readl(pcie, PEX_PF0_CONFIG); + + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + + /* + * The values of the Maximum Link Width and Supported Link + * Speed from the Link Capabilities Register will be lost + * during link down or hot reset. Restore initial value + * that configured by the Reset Configuration Word (RCW). + */ + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, pcie->lnkcap); + dw_pcie_dbi_ro_wr_dis(pci); + + cfg = ls_pcie_pf_lut_readl(pcie, PEX_PF0_CONFIG); cfg |= PEX_PF0_CFG_READY; - ls_lut_writel(pcie, PEX_PF0_CONFIG, cfg); + ls_pcie_pf_lut_writel(pcie, PEX_PF0_CONFIG, cfg); dw_pcie_ep_linkup(&pci->ep); dev_dbg(pci->dev, "Link up\n"); } else if (val & PEX_PF0_PME_MES_DR_LDD) { dev_dbg(pci->dev, "Link down\n"); + pci_epc_linkdown(pci->ep.epc); } else if (val & PEX_PF0_PME_MES_DR_HRD) { dev_dbg(pci->dev, "Hot reset\n"); } @@ -114,10 +130,10 @@ static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie, } /* Enable interrupts */ - val = ls_lut_readl(pcie, PEX_PF0_PME_MES_IER); + val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_IER); val |= PEX_PF0_PME_MES_IER_LDDIE | PEX_PF0_PME_MES_IER_HRDIE | PEX_PF0_PME_MES_IER_LUDIE; - ls_lut_writel(pcie, PEX_PF0_PME_MES_IER, val); + ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_IER, val); return 0; } @@ -150,16 +166,16 @@ static void ls_pcie_ep_init(struct dw_pcie_ep *ep) } static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return dw_pcie_ep_raise_msix_irq_doorbell(ep, func_no, interrupt_num); default: @@ -168,8 +184,7 @@ static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } } -static unsigned int ls_pcie_ep_func_conf_select(struct dw_pcie_ep *ep, - u8 func_no) +static unsigned int ls_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep, u8 func_no) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci); @@ -179,10 +194,10 @@ static unsigned int ls_pcie_ep_func_conf_select(struct dw_pcie_ep *ep, } static const struct dw_pcie_ep_ops ls_pcie_ep_ops = { - .ep_init = ls_pcie_ep_init, + .init = ls_pcie_ep_init, .raise_irq = ls_pcie_ep_raise_irq, .get_features = ls_pcie_ep_get_features, - .func_conf_select = ls_pcie_ep_func_conf_select, + .get_dbi_offset = ls_pcie_ep_get_dbi_offset, }; static const struct ls_pcie_ep_drvdata ls1_ep_drvdata = { @@ -215,6 +230,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) struct ls_pcie_ep *pcie; struct pci_epc_features *ls_epc; struct resource *dbi_base; + u8 offset; int ret; pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); @@ -234,7 +250,10 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) pci->dev = dev; pci->ops = pcie->drvdata->dw_pcie_ops; - ls_epc->bar_fixed_64bit = (1 << BAR_2) | (1 << BAR_4); + ls_epc->bar[BAR_2].only_64bit = true; + ls_epc->bar[BAR_3].type = BAR_RESERVED; + ls_epc->bar[BAR_4].only_64bit = true; + ls_epc->bar[BAR_5].type = BAR_RESERVED; ls_epc->linkup_notifier = true; pcie->pci = pci; @@ -249,8 +268,13 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian"); + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + platform_set_drvdata(pdev, pcie); + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + pcie->lnkcap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP); + ret = dw_pcie_ep_init(&pci->ep); if (ret) return ret; diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index ed5fb492fe08..ee6f52568133 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -8,9 +8,11 @@ * Author: Minghuan Lian <Minghuan.Lian@freescale.com> */ +#include <linux/delay.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/init.h> +#include <linux/iopoll.h> #include <linux/of_pci.h> #include <linux/of_platform.h> #include <linux/of_address.h> @@ -20,6 +22,7 @@ #include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include "../../pci.h" #include "pcie-designware.h" /* PEX Internal Configuration Registers */ @@ -27,12 +30,46 @@ #define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */ #define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */ +/* PF Message Command Register */ +#define LS_PCIE_PF_MCR 0x2c +#define PF_MCR_PTOMR BIT(0) +#define PF_MCR_EXL2S BIT(1) + +/* LS1021A PEXn PM Write Control Register */ +#define SCFG_PEXPMWRCR(idx) (0x5c + (idx) * 0x64) +#define PMXMTTURNOFF BIT(31) +#define SCFG_PEXSFTRSTCR 0x190 +#define PEXSR(idx) BIT(idx) + +/* LS1043A PEX PME control register */ +#define SCFG_PEXPMECR 0x144 +#define PEXPME(idx) BIT(31 - (idx) * 4) + +/* LS1043A PEX LUT debug register */ +#define LS_PCIE_LDBG 0x7fc +#define LDBG_SR BIT(30) +#define LDBG_WE BIT(31) + #define PCIE_IATU_NUM 6 +struct ls_pcie_drvdata { + const u32 pf_lut_off; + const struct dw_pcie_host_ops *ops; + int (*exit_from_l2)(struct dw_pcie_rp *pp); + bool scfg_support; + bool pm_support; +}; + struct ls_pcie { struct dw_pcie *pci; + const struct ls_pcie_drvdata *drvdata; + void __iomem *pf_lut_base; + struct regmap *scfg; + int index; + bool big_endian; }; +#define ls_pcie_pf_lut_readl_addr(addr) ls_pcie_pf_lut_readl(pcie, addr) #define to_ls_pcie(x) dev_get_drvdata((x)->dev) static bool ls_pcie_is_bridge(struct ls_pcie *pcie) @@ -41,7 +78,7 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie) u32 header_type; header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE); - header_type &= 0x7f; + header_type &= PCI_HEADER_TYPE_MASK; return header_type == PCI_HEADER_TYPE_BRIDGE; } @@ -73,6 +110,70 @@ static void ls_pcie_fix_error_response(struct ls_pcie *pcie) iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR); } +static u32 ls_pcie_pf_lut_readl(struct ls_pcie *pcie, u32 off) +{ + if (pcie->big_endian) + return ioread32be(pcie->pf_lut_base + off); + + return ioread32(pcie->pf_lut_base + off); +} + +static void ls_pcie_pf_lut_writel(struct ls_pcie *pcie, u32 off, u32 val) +{ + if (pcie->big_endian) + iowrite32be(val, pcie->pf_lut_base + off); + else + iowrite32(val, pcie->pf_lut_base + off); +} + +static void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + u32 val; + int ret; + + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_PF_MCR); + val |= PF_MCR_PTOMR; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_PF_MCR, val); + + ret = readx_poll_timeout(ls_pcie_pf_lut_readl_addr, LS_PCIE_PF_MCR, + val, !(val & PF_MCR_PTOMR), + PCIE_PME_TO_L2_TIMEOUT_US/10, + PCIE_PME_TO_L2_TIMEOUT_US); + if (ret) + dev_err(pcie->pci->dev, "PME_Turn_off timeout\n"); +} + +static int ls_pcie_exit_from_l2(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + u32 val; + int ret; + + /* + * Set PF_MCR_EXL2S bit in LS_PCIE_PF_MCR register for the link + * to exit L2 state. + */ + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_PF_MCR); + val |= PF_MCR_EXL2S; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_PF_MCR, val); + + /* + * L2 exit timeout of 10ms is not defined in the specifications, + * it was chosen based on empirical observations. + */ + ret = readx_poll_timeout(ls_pcie_pf_lut_readl_addr, LS_PCIE_PF_MCR, + val, !(val & PF_MCR_EXL2S), + 1000, + 10000); + if (ret) + dev_err(pcie->pci->dev, "L2 exit timeout\n"); + + return ret; +} + static int ls_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -89,20 +190,135 @@ static int ls_pcie_host_init(struct dw_pcie_rp *pp) return 0; } +static void scfg_pcie_send_turnoff_msg(struct regmap *scfg, u32 reg, u32 mask) +{ + /* Send PME_Turn_Off message */ + regmap_write_bits(scfg, reg, mask, mask); + + /* + * There is no specific register to check for PME_To_Ack from endpoint. + * So on the safe side, wait for PCIE_PME_TO_L2_TIMEOUT_US. + */ + mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000); + + /* + * Layerscape hardware reference manual recommends clearing the PMXMTTURNOFF bit + * to complete the PME_Turn_Off handshake. + */ + regmap_write_bits(scfg, reg, mask, 0); +} + +static void ls1021a_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + + scfg_pcie_send_turnoff_msg(pcie->scfg, SCFG_PEXPMWRCR(pcie->index), PMXMTTURNOFF); +} + +static int scfg_pcie_exit_from_l2(struct regmap *scfg, u32 reg, u32 mask) +{ + /* Reset the PEX wrapper to bring the link out of L2 */ + regmap_write_bits(scfg, reg, mask, mask); + regmap_write_bits(scfg, reg, mask, 0); + + return 0; +} + +static int ls1021a_pcie_exit_from_l2(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + + return scfg_pcie_exit_from_l2(pcie->scfg, SCFG_PEXSFTRSTCR, PEXSR(pcie->index)); +} + +static void ls1043a_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + + scfg_pcie_send_turnoff_msg(pcie->scfg, SCFG_PEXPMECR, PEXPME(pcie->index)); +} + +static int ls1043a_pcie_exit_from_l2(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct ls_pcie *pcie = to_ls_pcie(pci); + u32 val; + + /* + * Reset the PEX wrapper to bring the link out of L2. + * LDBG_WE: allows the user to have write access to the PEXDBG[SR] for both setting and + * clearing the soft reset on the PEX module. + * LDBG_SR: When SR is set to 1, the PEX module enters soft reset. + */ + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG); + val |= LDBG_WE; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG); + val |= LDBG_SR; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG); + val &= ~LDBG_SR; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val); + + val = ls_pcie_pf_lut_readl(pcie, LS_PCIE_LDBG); + val &= ~LDBG_WE; + ls_pcie_pf_lut_writel(pcie, LS_PCIE_LDBG, val); + + return 0; +} + static const struct dw_pcie_host_ops ls_pcie_host_ops = { - .host_init = ls_pcie_host_init, + .init = ls_pcie_host_init, + .pme_turn_off = ls_pcie_send_turnoff_msg, +}; + +static const struct dw_pcie_host_ops ls1021a_pcie_host_ops = { + .init = ls_pcie_host_init, + .pme_turn_off = ls1021a_pcie_send_turnoff_msg, +}; + +static const struct ls_pcie_drvdata ls1021a_drvdata = { + .pm_support = true, + .scfg_support = true, + .ops = &ls1021a_pcie_host_ops, + .exit_from_l2 = ls1021a_pcie_exit_from_l2, +}; + +static const struct dw_pcie_host_ops ls1043a_pcie_host_ops = { + .init = ls_pcie_host_init, + .pme_turn_off = ls1043a_pcie_send_turnoff_msg, +}; + +static const struct ls_pcie_drvdata ls1043a_drvdata = { + .pf_lut_off = 0x10000, + .pm_support = true, + .scfg_support = true, + .ops = &ls1043a_pcie_host_ops, + .exit_from_l2 = ls1043a_pcie_exit_from_l2, +}; + +static const struct ls_pcie_drvdata layerscape_drvdata = { + .pf_lut_off = 0xc0000, + .pm_support = true, + .ops = &ls_pcie_host_ops, + .exit_from_l2 = ls_pcie_exit_from_l2, }; static const struct of_device_id ls_pcie_of_match[] = { - { .compatible = "fsl,ls1012a-pcie", }, - { .compatible = "fsl,ls1021a-pcie", }, - { .compatible = "fsl,ls1028a-pcie", }, - { .compatible = "fsl,ls1043a-pcie", }, - { .compatible = "fsl,ls1046a-pcie", }, - { .compatible = "fsl,ls2080a-pcie", }, - { .compatible = "fsl,ls2085a-pcie", }, - { .compatible = "fsl,ls2088a-pcie", }, - { .compatible = "fsl,ls1088a-pcie", }, + { .compatible = "fsl,ls1012a-pcie", .data = &layerscape_drvdata }, + { .compatible = "fsl,ls1021a-pcie", .data = &ls1021a_drvdata }, + { .compatible = "fsl,ls1028a-pcie", .data = &layerscape_drvdata }, + { .compatible = "fsl,ls1043a-pcie", .data = &ls1043a_drvdata }, + { .compatible = "fsl,ls1046a-pcie", .data = &layerscape_drvdata }, + { .compatible = "fsl,ls2080a-pcie", .data = &layerscape_drvdata }, + { .compatible = "fsl,ls2085a-pcie", .data = &layerscape_drvdata }, + { .compatible = "fsl,ls2088a-pcie", .data = &layerscape_drvdata }, + { .compatible = "fsl,ls1088a-pcie", .data = &layerscape_drvdata }, { }, }; @@ -112,6 +328,8 @@ static int ls_pcie_probe(struct platform_device *pdev) struct dw_pcie *pci; struct ls_pcie *pcie; struct resource *dbi_base; + u32 index[2]; + int ret; pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) @@ -121,16 +339,35 @@ static int ls_pcie_probe(struct platform_device *pdev) if (!pci) return -ENOMEM; - pci->dev = dev; - pci->pp.ops = &ls_pcie_host_ops; + pcie->drvdata = of_device_get_match_data(dev); + pci->dev = dev; pcie->pci = pci; + pci->pp.ops = pcie->drvdata->ops; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); if (IS_ERR(pci->dbi_base)) return PTR_ERR(pci->dbi_base); + pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian"); + + pcie->pf_lut_base = pci->dbi_base + pcie->drvdata->pf_lut_off; + + if (pcie->drvdata->scfg_support) { + pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg"); + if (IS_ERR(pcie->scfg)) { + dev_err(dev, "No syscfg phandle specified\n"); + return PTR_ERR(pcie->scfg); + } + + ret = of_property_read_u32_array(dev->of_node, "fsl,pcie-scfg", index, 2); + if (ret) + return ret; + + pcie->index = index[1]; + } + if (!ls_pcie_is_bridge(pcie)) return -ENODEV; @@ -139,12 +376,42 @@ static int ls_pcie_probe(struct platform_device *pdev) return dw_pcie_host_init(&pci->pp); } +static int ls_pcie_suspend_noirq(struct device *dev) +{ + struct ls_pcie *pcie = dev_get_drvdata(dev); + + if (!pcie->drvdata->pm_support) + return 0; + + return dw_pcie_suspend_noirq(pcie->pci); +} + +static int ls_pcie_resume_noirq(struct device *dev) +{ + struct ls_pcie *pcie = dev_get_drvdata(dev); + int ret; + + if (!pcie->drvdata->pm_support) + return 0; + + ret = pcie->drvdata->exit_from_l2(&pcie->pci->pp); + if (ret) + return ret; + + return dw_pcie_resume_noirq(pcie->pci); +} + +static const struct dev_pm_ops ls_pcie_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(ls_pcie_suspend_noirq, ls_pcie_resume_noirq) +}; + static struct platform_driver ls_pcie_driver = { .probe = ls_pcie_probe, .driver = { .name = "layerscape-pcie", .of_match_table = ls_pcie_of_match, .suppress_bind_attrs = true, + .pm = &ls_pcie_pm_ops, }, }; builtin_platform_driver(ls_pcie_driver); diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index c1527693bed9..6477c83262c2 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -9,7 +9,6 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/pci.h> #include <linux/platform_device.h> @@ -17,6 +16,7 @@ #include <linux/resource.h> #include <linux/types.h> #include <linux/phy/phy.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include "pcie-designware.h" @@ -163,6 +163,13 @@ static int meson_pcie_reset(struct meson_pcie *mp) return 0; } +static inline void meson_pcie_disable_clock(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + static inline struct clk *meson_pcie_probe_clock(struct device *dev, const char *id, u64 rate) { @@ -187,9 +194,7 @@ static inline struct clk *meson_pcie_probe_clock(struct device *dev, return ERR_PTR(ret); } - devm_add_action_or_reset(dev, - (void (*) (void *))clk_disable_unprepare, - clk); + devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); return clk; } @@ -384,7 +389,7 @@ static int meson_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops meson_pcie_host_ops = { - .host_init = meson_pcie_host_init, + .init = meson_pcie_host_init, }; static const struct dw_pcie_ops dw_pcie_ops = { diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index b8cb77c9c4bd..6dfdda59f328 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -311,7 +311,7 @@ static int al_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops al_pcie_host_ops = { - .host_init = al_pcie_host_init, + .init = al_pcie_host_init, }; static int al_pcie_probe(struct platform_device *pdev) diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 5c999e15c357..b5c599ccaacf 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -225,7 +225,7 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) } static const struct dw_pcie_host_ops armada8k_pcie_host_ops = { - .host_init = armada8k_pcie_host_init, + .init = armada8k_pcie_host_init, }; static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 98102079e26d..9ed0a9ba7619 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -10,7 +10,7 @@ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/resource.h> @@ -333,7 +333,7 @@ static int artpec6_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops artpec6_pcie_host_ops = { - .host_init = artpec6_pcie_host_init, + .init = artpec6_pcie_host_init, }; static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) @@ -352,15 +352,15 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep) } static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - dev_err(pci->dev, "EP cannot trigger legacy IRQs\n"); + case PCI_IRQ_INTX: + dev_err(pci->dev, "EP cannot trigger INTx IRQs\n"); return -EINVAL; - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "UNKNOWN IRQ type\n"); @@ -370,7 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = artpec6_pcie_ep_init, + .init = artpec6_pcie_ep_init, .raise_irq = artpec6_pcie_raise_irq, }; diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c index 17e696797ff5..76d0ddea8007 100644 --- a/drivers/pci/controller/dwc/pcie-bt1.c +++ b/drivers/pci/controller/dwc/pcie-bt1.c @@ -559,8 +559,8 @@ static void bt1_pcie_host_deinit(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops bt1_pcie_host_ops = { - .host_init = bt1_pcie_host_init, - .host_deinit = bt1_pcie_host_deinit, + .init = bt1_pcie_host_init, + .deinit = bt1_pcie_host_deinit, }; static struct bt1_pcie *bt1_pcie_create_data(struct platform_device *pdev) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index f9182f8d552f..746a11dcb67f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -6,6 +6,8 @@ * Author: Kishon Vijay Abraham I <kishon@ti.com> */ +#include <linux/align.h> +#include <linux/bitfield.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -42,32 +44,19 @@ dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no) return NULL; } -static unsigned int dw_pcie_ep_func_select(struct dw_pcie_ep *ep, u8 func_no) -{ - unsigned int func_offset = 0; - - if (ep->ops->func_conf_select) - func_offset = ep->ops->func_conf_select(ep, func_no); - - return func_offset; -} - static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no, enum pci_barno bar, int flags) { - u32 reg; - unsigned int func_offset = 0; struct dw_pcie_ep *ep = &pci->ep; + u32 reg; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = func_offset + PCI_BASE_ADDRESS_0 + (4 * bar); + reg = PCI_BASE_ADDRESS_0 + (4 * bar); dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi2(pci, reg, 0x0); - dw_pcie_writel_dbi(pci, reg, 0x0); + dw_pcie_ep_writel_dbi2(ep, func_no, reg, 0x0); + dw_pcie_ep_writel_dbi(ep, func_no, reg, 0x0); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { - dw_pcie_writel_dbi2(pci, reg + 4, 0x0); - dw_pcie_writel_dbi(pci, reg + 4, 0x0); + dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, 0x0); + dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0x0); } dw_pcie_dbi_ro_wr_dis(pci); } @@ -84,19 +73,15 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar); static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no, - u8 cap_ptr, u8 cap) + u8 cap_ptr, u8 cap) { - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - unsigned int func_offset = 0; u8 cap_id, next_cap_ptr; u16 reg; if (!cap_ptr) return 0; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = dw_pcie_readw_dbi(pci, func_offset + cap_ptr); + reg = dw_pcie_ep_readw_dbi(ep, func_no, cap_ptr); cap_id = (reg & 0x00ff); if (cap_id > PCI_CAP_ID_MAX) @@ -111,14 +96,10 @@ static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no, static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) { - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - unsigned int func_offset = 0; u8 next_cap_ptr; u16 reg; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = dw_pcie_readw_dbi(pci, func_offset + PCI_CAPABILITY_LIST); + reg = dw_pcie_ep_readw_dbi(ep, func_no, PCI_CAPABILITY_LIST); next_cap_ptr = (reg & 0x00ff); return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); @@ -129,24 +110,21 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - unsigned int func_offset = 0; - - func_offset = dw_pcie_ep_func_select(ep, func_no); dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writew_dbi(pci, func_offset + PCI_VENDOR_ID, hdr->vendorid); - dw_pcie_writew_dbi(pci, func_offset + PCI_DEVICE_ID, hdr->deviceid); - dw_pcie_writeb_dbi(pci, func_offset + PCI_REVISION_ID, hdr->revid); - dw_pcie_writeb_dbi(pci, func_offset + PCI_CLASS_PROG, hdr->progif_code); - dw_pcie_writew_dbi(pci, func_offset + PCI_CLASS_DEVICE, - hdr->subclass_code | hdr->baseclass_code << 8); - dw_pcie_writeb_dbi(pci, func_offset + PCI_CACHE_LINE_SIZE, - hdr->cache_line_size); - dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_VENDOR_ID, - hdr->subsys_vendor_id); - dw_pcie_writew_dbi(pci, func_offset + PCI_SUBSYSTEM_ID, hdr->subsys_id); - dw_pcie_writeb_dbi(pci, func_offset + PCI_INTERRUPT_PIN, - hdr->interrupt_pin); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_VENDOR_ID, hdr->vendorid); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_DEVICE_ID, hdr->deviceid); + dw_pcie_ep_writeb_dbi(ep, func_no, PCI_REVISION_ID, hdr->revid); + dw_pcie_ep_writeb_dbi(ep, func_no, PCI_CLASS_PROG, hdr->progif_code); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_CLASS_DEVICE, + hdr->subclass_code | hdr->baseclass_code << 8); + dw_pcie_ep_writeb_dbi(ep, func_no, PCI_CACHE_LINE_SIZE, + hdr->cache_line_size); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_SUBSYSTEM_VENDOR_ID, + hdr->subsys_vendor_id); + dw_pcie_ep_writew_dbi(ep, func_no, PCI_SUBSYSTEM_ID, hdr->subsys_id); + dw_pcie_ep_writeb_dbi(ep, func_no, PCI_INTERRUPT_PIN, + hdr->interrupt_pin); dw_pcie_dbi_ro_wr_dis(pci); return 0; @@ -231,13 +209,10 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, enum pci_barno bar = epf_bar->barno; size_t size = epf_bar->size; int flags = epf_bar->flags; - unsigned int func_offset = 0; int ret, type; u32 reg; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset; + reg = PCI_BASE_ADDRESS_0 + (4 * bar); if (!(flags & PCI_BASE_ADDRESS_SPACE)) type = PCIE_ATU_TYPE_MEM; @@ -253,12 +228,12 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); - dw_pcie_writel_dbi(pci, reg, flags); + dw_pcie_ep_writel_dbi2(ep, func_no, reg, lower_32_bits(size - 1)); + dw_pcie_ep_writel_dbi(ep, func_no, reg, flags); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { - dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1)); - dw_pcie_writel_dbi(pci, reg + 4, 0); + dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, upper_32_bits(size - 1)); + dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0); } ep->epf_bar[bar] = epf_bar; @@ -318,23 +293,19 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - u32 val, reg; - unsigned int func_offset = 0; struct dw_pcie_ep_func *ep_func; + u32 val, reg; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msi_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS; - val = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_FLAGS; + val = dw_pcie_ep_readw_dbi(ep, func_no, reg); if (!(val & PCI_MSI_FLAGS_ENABLE)) return -EINVAL; - val = (val & PCI_MSI_FLAGS_QSIZE) >> 4; + val = FIELD_GET(PCI_MSI_FLAGS_QSIZE, val); return val; } @@ -344,22 +315,19 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - u32 val, reg; - unsigned int func_offset = 0; struct dw_pcie_ep_func *ep_func; + u32 val, reg; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msi_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS; - val = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_FLAGS; + val = dw_pcie_ep_readw_dbi(ep, func_no, reg); val &= ~PCI_MSI_FLAGS_QMASK; - val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK; + val |= FIELD_PREP(PCI_MSI_FLAGS_QMASK, interrupts); dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writew_dbi(pci, reg, val); + dw_pcie_ep_writew_dbi(ep, func_no, reg, val); dw_pcie_dbi_ro_wr_dis(pci); return 0; @@ -368,19 +336,15 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - u32 val, reg; - unsigned int func_offset = 0; struct dw_pcie_ep_func *ep_func; + u32 val, reg; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msix_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS; - val = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msix_cap + PCI_MSIX_FLAGS; + val = dw_pcie_ep_readw_dbi(ep, func_no, reg); if (!(val & PCI_MSIX_FLAGS_ENABLE)) return -EINVAL; @@ -394,9 +358,8 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - u32 val, reg; - unsigned int func_offset = 0; struct dw_pcie_ep_func *ep_func; + u32 val, reg; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msix_cap) @@ -404,21 +367,19 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, dw_pcie_dbi_ro_wr_en(pci); - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msix_cap + func_offset + PCI_MSIX_FLAGS; - val = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msix_cap + PCI_MSIX_FLAGS; + val = dw_pcie_ep_readw_dbi(ep, func_no, reg); val &= ~PCI_MSIX_FLAGS_QSIZE; val |= interrupts; dw_pcie_writew_dbi(pci, reg, val); - reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE; + reg = ep_func->msix_cap + PCI_MSIX_TABLE; val = offset | bir; - dw_pcie_writel_dbi(pci, reg, val); + dw_pcie_ep_writel_dbi(ep, func_no, reg, val); - reg = ep_func->msix_cap + func_offset + PCI_MSIX_PBA; + reg = ep_func->msix_cap + PCI_MSIX_PBA; val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir; - dw_pcie_writel_dbi(pci, reg, val); + dw_pcie_ep_writel_dbi(ep, func_no, reg, val); dw_pcie_dbi_ro_wr_dis(pci); @@ -426,7 +387,7 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, } static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -479,56 +440,53 @@ static const struct pci_epc_ops epc_ops = { .get_features = dw_pcie_ep_get_features, }; -int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) +int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct device *dev = pci->dev; - dev_err(dev, "EP cannot trigger legacy IRQs\n"); + dev_err(dev, "EP cannot raise INTX IRQs\n"); return -EINVAL; } -EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_legacy_irq); +EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_intx_irq); int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num) { - struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 msg_addr_lower, msg_addr_upper, reg; struct dw_pcie_ep_func *ep_func; struct pci_epc *epc = ep->epc; unsigned int aligned_offset; - unsigned int func_offset = 0; u16 msg_ctrl, msg_data; - u32 msg_addr_lower, msg_addr_upper, reg; - u64 msg_addr; bool has_upper; + u64 msg_addr; int ret; ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msi_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ - reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS; - msg_ctrl = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_FLAGS; + msg_ctrl = dw_pcie_ep_readw_dbi(ep, func_no, reg); has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); - reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_LO; - msg_addr_lower = dw_pcie_readl_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_ADDRESS_LO; + msg_addr_lower = dw_pcie_ep_readl_dbi(ep, func_no, reg); if (has_upper) { - reg = ep_func->msi_cap + func_offset + PCI_MSI_ADDRESS_HI; - msg_addr_upper = dw_pcie_readl_dbi(pci, reg); - reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_64; - msg_data = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_ADDRESS_HI; + msg_addr_upper = dw_pcie_ep_readl_dbi(ep, func_no, reg); + reg = ep_func->msi_cap + PCI_MSI_DATA_64; + msg_data = dw_pcie_ep_readw_dbi(ep, func_no, reg); } else { msg_addr_upper = 0; - reg = ep_func->msi_cap + func_offset + PCI_MSI_DATA_32; - msg_data = dw_pcie_readw_dbi(pci, reg); + reg = ep_func->msi_cap + PCI_MSI_DATA_32; + msg_data = dw_pcie_ep_readw_dbi(ep, func_no, reg); } - aligned_offset = msg_addr_lower & (epc->mem->window.page_size - 1); - msg_addr = ((u64)msg_addr_upper) << 32 | - (msg_addr_lower & ~aligned_offset); + msg_addr = ((u64)msg_addr_upper) << 32 | msg_addr_lower; + + aligned_offset = msg_addr & (epc->mem->window.page_size - 1); + msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size); ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, epc->mem->window.page_size); if (ret) @@ -565,10 +523,9 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - struct dw_pcie_ep_func *ep_func; struct pci_epf_msix_tbl *msix_tbl; + struct dw_pcie_ep_func *ep_func; struct pci_epc *epc = ep->epc; - unsigned int func_offset = 0; u32 reg, msg_data, vec_ctrl; unsigned int aligned_offset; u32 tbl_offset; @@ -580,11 +537,9 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, if (!ep_func || !ep_func->msix_cap) return -EINVAL; - func_offset = dw_pcie_ep_func_select(ep, func_no); - - reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE; - tbl_offset = dw_pcie_readl_dbi(pci, reg); - bir = (tbl_offset & PCI_MSIX_TABLE_BIR); + reg = ep_func->msix_cap + PCI_MSIX_TABLE; + tbl_offset = dw_pcie_ep_readl_dbi(ep, func_no, reg); + bir = FIELD_GET(PCI_MSIX_TABLE_BIR, tbl_offset); tbl_offset &= PCI_MSIX_TABLE_OFFSET; msix_tbl = ep->epf_bar[bir]->addr + tbl_offset; @@ -598,6 +553,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, } aligned_offset = msg_addr & (epc->mem->window.page_size - 1); + msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size); ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, epc->mem->window.page_size); if (ret) @@ -621,7 +577,11 @@ void dw_pcie_ep_exit(struct dw_pcie_ep *ep) epc->mem->window.page_size); pci_epc_mem_exit(epc); + + if (ep->ops->deinit) + ep->ops->deinit(ep); } +EXPORT_SYMBOL_GPL(dw_pcie_ep_exit); static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap) { @@ -669,8 +629,13 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep) nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >> PCI_REBAR_CTRL_NBAR_SHIFT; + /* + * PCIe r6.0, sec 7.8.6.2 require us to support at least one + * size in the range from 1 MB to 512 GB. Advertise support + * for 1 MB BAR size only. + */ for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) - dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0); + dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, BIT(4)); } /* @@ -723,6 +688,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) ep->phys_base = res->start; ep->addr_size = resource_size(res); + if (ep->ops->pre_init) + ep->ops->pre_init(ep); + dw_pcie_version_detect(pci); dw_pcie_iatu_detect(pci); @@ -770,14 +738,14 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) list_add_tail(&ep_func->list, &ep->func_list); } - if (ep->ops->ep_init) - ep->ops->ep_init(ep); + if (ep->ops->init) + ep->ops->init(ep); ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size, ep->page_size); if (ret < 0) { dev_err(dev, "Failed to initialize address space\n"); - return ret; + goto err_ep_deinit; } ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, @@ -814,6 +782,10 @@ err_free_epc_mem: err_exit_epc_mem: pci_epc_mem_exit(epc); +err_ep_deinit: + if (ep->ops->deinit) + ep->ops->deinit(ep); + return ret; } EXPORT_SYMBOL_GPL(dw_pcie_ep_init); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 9952057c8819..d15a5c2d5b48 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -8,6 +8,7 @@ * Author: Jingoo Han <jg1.han@samsung.com> */ +#include <linux/iopoll.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> #include <linux/msi.h> @@ -16,6 +17,7 @@ #include <linux/pci_regs.h> #include <linux/platform_device.h> +#include "../../pci.h" #include "pcie-designware.h" static struct pci_ops dw_pcie_ops; @@ -326,7 +328,7 @@ static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct device *dev = pci->dev; struct platform_device *pdev = to_platform_device(dev); - u64 *msi_vaddr; + u64 *msi_vaddr = NULL; int ret; u32 ctrl, num_ctrls; @@ -377,15 +379,20 @@ static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) * memory. */ ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); - if (ret) - dev_warn(dev, "Failed to set DMA mask to 32-bit. Devices with only 32-bit MSI support may not work properly\n"); + if (!ret) + msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64), &pp->msi_data, + GFP_KERNEL); - msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64), &pp->msi_data, - GFP_KERNEL); if (!msi_vaddr) { - dev_err(dev, "Failed to alloc and map MSI data\n"); - dw_pcie_free_msi(pp); - return -ENOMEM; + dev_warn(dev, "Failed to allocate 32-bit MSI address\n"); + dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64), &pp->msi_data, + GFP_KERNEL); + if (!msi_vaddr) { + dev_err(dev, "Failed to allocate MSI address\n"); + dw_pcie_free_msi(pp); + return -ENOMEM; + } } return 0; @@ -439,14 +446,14 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) bridge->ops = &dw_pcie_ops; bridge->child_ops = &dw_child_pcie_ops; - if (pp->ops->host_init) { - ret = pp->ops->host_init(pp); + if (pp->ops->init) { + ret = pp->ops->init(pp); if (ret) return ret; } if (pci_msi_enabled()) { - pp->has_msi_ctrl = !(pp->ops->msi_host_init || + pp->has_msi_ctrl = !(pp->ops->msi_init || of_property_read_bool(np, "msi-parent") || of_property_read_bool(np, "msi-map")); @@ -462,8 +469,8 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) goto err_deinit_host; } - if (pp->ops->msi_host_init) { - ret = pp->ops->msi_host_init(pp); + if (pp->ops->msi_init) { + ret = pp->ops->msi_init(pp); if (ret < 0) goto err_deinit_host; } else if (pp->has_msi_ctrl) { @@ -500,6 +507,9 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_stop_link; + if (pp->ops->post_init) + pp->ops->post_init(pp); + return 0; err_stop_link: @@ -513,8 +523,8 @@ err_free_msi: dw_pcie_free_msi(pp); err_deinit_host: - if (pp->ops->host_deinit) - pp->ops->host_deinit(pp); + if (pp->ops->deinit) + pp->ops->deinit(pp); return ret; } @@ -534,8 +544,8 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp) if (pp->has_msi_ctrl) dw_pcie_free_msi(pp); - if (pp->ops->host_deinit) - pp->ops->host_deinit(pp); + if (pp->ops->deinit) + pp->ops->deinit(pp); } EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); @@ -807,3 +817,72 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) return 0; } EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); + +int dw_pcie_suspend_noirq(struct dw_pcie *pci) +{ + u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + u32 val; + int ret; + + /* + * If L1SS is supported, then do not put the link into L2 as some + * devices such as NVMe expect low resume latency. + */ + if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1) + return 0; + + if (dw_pcie_get_ltssm(pci) <= DW_PCIE_LTSSM_DETECT_ACT) + return 0; + + if (!pci->pp.ops->pme_turn_off) + return 0; + + pci->pp.ops->pme_turn_off(&pci->pp); + + ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE, + PCIE_PME_TO_L2_TIMEOUT_US/10, + PCIE_PME_TO_L2_TIMEOUT_US, false, pci); + if (ret) { + dev_err(pci->dev, "Timeout waiting for L2 entry! LTSSM: 0x%x\n", val); + return ret; + } + + if (pci->pp.ops->deinit) + pci->pp.ops->deinit(&pci->pp); + + pci->suspended = true; + + return ret; +} +EXPORT_SYMBOL_GPL(dw_pcie_suspend_noirq); + +int dw_pcie_resume_noirq(struct dw_pcie *pci) +{ + int ret; + + if (!pci->suspended) + return 0; + + pci->suspended = false; + + if (pci->pp.ops->init) { + ret = pci->pp.ops->init(&pci->pp); + if (ret) { + dev_err(pci->dev, "Host init failed: %d\n", ret); + return ret; + } + } + + dw_pcie_setup_rc(&pci->pp); + + ret = dw_pcie_start_link(pci); + if (ret) + return ret; + + ret = dw_pcie_wait_for_link(pci); + if (ret) + return ret; + + return ret; +} +EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 1fcfb840f238..778588b4be70 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -12,7 +12,7 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/resource.h> @@ -42,17 +42,16 @@ static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep) } static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "UNKNOWN IRQ type\n"); @@ -74,7 +73,7 @@ dw_plat_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { - .ep_init = dw_plat_pcie_ep_init, + .init = dw_plat_pcie_ep_init, .raise_irq = dw_plat_pcie_ep_raise_irq, .get_features = dw_plat_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 1f2ee71da4da..250cf7f40b85 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -16,7 +16,7 @@ #include <linux/gpio/consumer.h> #include <linux/ioport.h> #include <linux/of.h> -#include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/sizes.h> #include <linux/types.h> @@ -365,6 +365,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val) if (ret) dev_err(pci->dev, "write DBI address failed\n"); } +EXPORT_SYMBOL_GPL(dw_pcie_write_dbi2); static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir, u32 index) @@ -732,6 +733,53 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen) } +static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes) +{ + u32 lnkcap, lwsc, plc; + u8 cap; + + if (!num_lanes) + return; + + /* Set the number of lanes */ + plc = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); + plc &= ~PORT_LINK_FAST_LINK_MODE; + plc &= ~PORT_LINK_MODE_MASK; + + /* Set link width speed control register */ + lwsc = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); + lwsc &= ~PORT_LOGIC_LINK_WIDTH_MASK; + switch (num_lanes) { + case 1: + plc |= PORT_LINK_MODE_1_LANES; + lwsc |= PORT_LOGIC_LINK_WIDTH_1_LANES; + break; + case 2: + plc |= PORT_LINK_MODE_2_LANES; + lwsc |= PORT_LOGIC_LINK_WIDTH_2_LANES; + break; + case 4: + plc |= PORT_LINK_MODE_4_LANES; + lwsc |= PORT_LOGIC_LINK_WIDTH_4_LANES; + break; + case 8: + plc |= PORT_LINK_MODE_8_LANES; + lwsc |= PORT_LOGIC_LINK_WIDTH_8_LANES; + break; + default: + dev_err(pci->dev, "num-lanes %u: invalid value\n", num_lanes); + return; + } + dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, plc); + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, lwsc); + + cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP); + lnkcap &= ~PCI_EXP_LNKCAP_MLW; + lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, num_lanes); + dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap); +} + void dw_pcie_iatu_detect(struct dw_pcie *pci) { int max_region, ob, ib; @@ -840,8 +888,14 @@ static int dw_pcie_edma_find_chip(struct dw_pcie *pci) * Indirect eDMA CSRs access has been completely removed since v5.40a * thus no space is now reserved for the eDMA channels viewport and * former DMA CTRL register is no longer fixed to FFs. + * + * Note that Renesas R-Car S4-8's PCIe controllers for unknown reason + * have zeros in the eDMA CTRL register even though the HW-manual + * explicitly states there must FFs if the unrolled mapping is enabled. + * For such cases the low-level drivers are supposed to manually + * activate the unrolled mapping to bypass the auto-detection procedure. */ - if (dw_pcie_ver_is_ge(pci, 540A)) + if (dw_pcie_ver_is_ge(pci, 540A) || dw_pcie_cap_is(pci, EDMA_UNROLL)) val = 0xFFFFFFFF; else val = dw_pcie_readl_dbi(pci, PCIE_DMA_VIEWPORT_BASE + PCIE_DMA_CTRL); @@ -1013,49 +1067,5 @@ void dw_pcie_setup(struct dw_pcie *pci) val |= PORT_LINK_DLL_LINK_EN; dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); - if (!pci->num_lanes) { - dev_dbg(pci->dev, "Using h/w default number of lanes\n"); - return; - } - - /* Set the number of lanes */ - val &= ~PORT_LINK_FAST_LINK_MODE; - val &= ~PORT_LINK_MODE_MASK; - switch (pci->num_lanes) { - case 1: - val |= PORT_LINK_MODE_1_LANES; - break; - case 2: - val |= PORT_LINK_MODE_2_LANES; - break; - case 4: - val |= PORT_LINK_MODE_4_LANES; - break; - case 8: - val |= PORT_LINK_MODE_8_LANES; - break; - default: - dev_err(pci->dev, "num-lanes %u: invalid value\n", pci->num_lanes); - return; - } - dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); - - /* Set link width speed control register */ - val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); - val &= ~PORT_LOGIC_LINK_WIDTH_MASK; - switch (pci->num_lanes) { - case 1: - val |= PORT_LOGIC_LINK_WIDTH_1_LANES; - break; - case 2: - val |= PORT_LOGIC_LINK_WIDTH_2_LANES; - break; - case 4: - val |= PORT_LOGIC_LINK_WIDTH_4_LANES; - break; - case 8: - val |= PORT_LOGIC_LINK_WIDTH_8_LANES; - break; - } - dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + dw_pcie_link_set_max_link_width(pci, pci->num_lanes); } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 79713ce075cc..26dae4837462 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -51,8 +51,9 @@ /* DWC PCIe controller capabilities */ #define DW_PCIE_CAP_REQ_RES 0 -#define DW_PCIE_CAP_IATU_UNROLL 1 -#define DW_PCIE_CAP_CDM_CHECK 2 +#define DW_PCIE_CAP_EDMA_UNROLL 1 +#define DW_PCIE_CAP_IATU_UNROLL 2 +#define DW_PCIE_CAP_CDM_CHECK 3 #define dw_pcie_cap_is(_pci, _cap) \ test_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps) @@ -288,10 +289,22 @@ enum dw_pcie_core_rst { DW_PCIE_NUM_CORE_RSTS }; +enum dw_pcie_ltssm { + /* Need to align with PCIE_PORT_DEBUG0 bits 0:5 */ + DW_PCIE_LTSSM_DETECT_QUIET = 0x0, + DW_PCIE_LTSSM_DETECT_ACT = 0x1, + DW_PCIE_LTSSM_L0 = 0x11, + DW_PCIE_LTSSM_L2_IDLE = 0x15, + + DW_PCIE_LTSSM_UNKNOWN = 0xFFFFFFFF, +}; + struct dw_pcie_host_ops { - int (*host_init)(struct dw_pcie_rp *pp); - void (*host_deinit)(struct dw_pcie_rp *pp); - int (*msi_host_init)(struct dw_pcie_rp *pp); + int (*init)(struct dw_pcie_rp *pp); + void (*deinit)(struct dw_pcie_rp *pp); + void (*post_init)(struct dw_pcie_rp *pp); + int (*msi_init)(struct dw_pcie_rp *pp); + void (*pme_turn_off)(struct dw_pcie_rp *pp); }; struct dw_pcie_rp { @@ -318,9 +331,11 @@ struct dw_pcie_rp { }; struct dw_pcie_ep_ops { - void (*ep_init)(struct dw_pcie_ep *ep); + void (*pre_init)(struct dw_pcie_ep *ep); + void (*init)(struct dw_pcie_ep *ep); + void (*deinit)(struct dw_pcie_ep *ep); int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num); + unsigned int type, u16 interrupt_num); const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep); /* * Provide a method to implement the different func config space @@ -329,7 +344,8 @@ struct dw_pcie_ep_ops { * return a 0, and implement code in callback function of platform * driver. */ - unsigned int (*func_conf_select)(struct dw_pcie_ep *ep, u8 func_no); + unsigned int (*get_dbi_offset)(struct dw_pcie_ep *ep, u8 func_no); + unsigned int (*get_dbi2_offset)(struct dw_pcie_ep *ep, u8 func_no); }; struct dw_pcie_ep_func { @@ -364,6 +380,7 @@ struct dw_pcie_ops { void (*write_dbi2)(struct dw_pcie *pcie, void __iomem *base, u32 reg, size_t size, u32 val); int (*link_up)(struct dw_pcie *pcie); + enum dw_pcie_ltssm (*get_ltssm)(struct dw_pcie *pcie); int (*start_link)(struct dw_pcie *pcie); void (*stop_link)(struct dw_pcie *pcie); }; @@ -393,6 +410,7 @@ struct dw_pcie { struct reset_control_bulk_data app_rsts[DW_PCIE_NUM_APP_RSTS]; struct reset_control_bulk_data core_rsts[DW_PCIE_NUM_CORE_RSTS]; struct gpio_desc *pe_rst; + bool suspended; }; #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) @@ -430,6 +448,9 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci); int dw_pcie_edma_detect(struct dw_pcie *pci); void dw_pcie_edma_remove(struct dw_pcie *pci); +int dw_pcie_suspend_noirq(struct dw_pcie *pci); +int dw_pcie_resume_noirq(struct dw_pcie *pci); + static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) { dw_pcie_write_dbi(pci, reg, 0x4, val); @@ -465,6 +486,99 @@ static inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, u32 reg, u32 val) dw_pcie_write_dbi2(pci, reg, 0x4, val); } +static inline unsigned int dw_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep, + u8 func_no) +{ + unsigned int dbi_offset = 0; + + if (ep->ops->get_dbi_offset) + dbi_offset = ep->ops->get_dbi_offset(ep, func_no); + + return dbi_offset; +} + +static inline u32 dw_pcie_ep_read_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, size_t size) +{ + unsigned int offset = dw_pcie_ep_get_dbi_offset(ep, func_no); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + return dw_pcie_read_dbi(pci, offset + reg, size); +} + +static inline void dw_pcie_ep_write_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, size_t size, u32 val) +{ + unsigned int offset = dw_pcie_ep_get_dbi_offset(ep, func_no); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + dw_pcie_write_dbi(pci, offset + reg, size, val); +} + +static inline void dw_pcie_ep_writel_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, u32 val) +{ + dw_pcie_ep_write_dbi(ep, func_no, reg, 0x4, val); +} + +static inline u32 dw_pcie_ep_readl_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg) +{ + return dw_pcie_ep_read_dbi(ep, func_no, reg, 0x4); +} + +static inline void dw_pcie_ep_writew_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, u16 val) +{ + dw_pcie_ep_write_dbi(ep, func_no, reg, 0x2, val); +} + +static inline u16 dw_pcie_ep_readw_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg) +{ + return dw_pcie_ep_read_dbi(ep, func_no, reg, 0x2); +} + +static inline void dw_pcie_ep_writeb_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, u8 val) +{ + dw_pcie_ep_write_dbi(ep, func_no, reg, 0x1, val); +} + +static inline u8 dw_pcie_ep_readb_dbi(struct dw_pcie_ep *ep, u8 func_no, + u32 reg) +{ + return dw_pcie_ep_read_dbi(ep, func_no, reg, 0x1); +} + +static inline unsigned int dw_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, + u8 func_no) +{ + unsigned int dbi2_offset = 0; + + if (ep->ops->get_dbi2_offset) + dbi2_offset = ep->ops->get_dbi2_offset(ep, func_no); + else if (ep->ops->get_dbi_offset) /* for backward compatibility */ + dbi2_offset = ep->ops->get_dbi_offset(ep, func_no); + + return dbi2_offset; +} + +static inline void dw_pcie_ep_write_dbi2(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, size_t size, u32 val) +{ + unsigned int offset = dw_pcie_ep_get_dbi2_offset(ep, func_no); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + dw_pcie_write_dbi2(pci, offset + reg, size, val); +} + +static inline void dw_pcie_ep_writel_dbi2(struct dw_pcie_ep *ep, u8 func_no, + u32 reg, u32 val) +{ + dw_pcie_ep_write_dbi2(ep, func_no, reg, 0x4, val); +} + static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci) { u32 reg; @@ -501,6 +615,18 @@ static inline void dw_pcie_stop_link(struct dw_pcie *pci) pci->ops->stop_link(pci); } +static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci) +{ + u32 val; + + if (pci->ops && pci->ops->get_ltssm) + return pci->ops->get_ltssm(pci); + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0); + + return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val); +} + #ifdef CONFIG_PCIE_DW_HOST irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp); int dw_pcie_setup_rc(struct dw_pcie_rp *pp); @@ -547,7 +673,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep); int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep); void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep); void dw_pcie_ep_exit(struct dw_pcie_ep *ep); -int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no); +int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no); int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, u8 interrupt_num); int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, @@ -580,7 +706,7 @@ static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep) { } -static inline int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) +static inline int dw_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep, u8 func_no) { return 0; } diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index c1e7653e508e..d6842141d384 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -14,7 +14,7 @@ #include <linux/irqdomain.h> #include <linux/mfd/syscon.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_irq.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> @@ -72,7 +72,7 @@ static void rockchip_pcie_writel_apb(struct rockchip_pcie *rockchip, writel_relaxed(val, rockchip->apb_base + reg); } -static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) +static void rockchip_pcie_intx_handler(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc); @@ -202,7 +202,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) if (ret < 0) dev_err(dev, "failed to init irq domain\n"); - irq_set_chained_handler_and_data(irq, rockchip_pcie_legacy_int_handler, + irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler, rockchip); /* LTSSM enable control mode */ @@ -215,7 +215,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops rockchip_pcie_host_ops = { - .host_init = rockchip_pcie_host_init, + .init = rockchip_pcie_host_init, }; static int rockchip_pcie_clk_init(struct rockchip_pcie *rockchip) diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c index 0c90583c078b..66367252032b 100644 --- a/drivers/pci/controller/dwc/pcie-fu740.c +++ b/drivers/pci/controller/dwc/pcie-fu740.c @@ -279,7 +279,7 @@ static int fu740_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops fu740_pcie_host_ops = { - .host_init = fu740_pcie_host_init, + .init = fu740_pcie_host_init, }; static const struct dw_pcie_ops dw_pcie_ops = { @@ -299,6 +299,7 @@ static int fu740_pcie_probe(struct platform_device *pdev) pci->dev = dev; pci->ops = &dw_pcie_ops; pci->pp.ops = &fu740_pcie_host_ops; + pci->pp.num_vectors = MAX_MSI_IRQS; /* SiFive specific region: mgmt */ afp->mgmt_base = devm_platform_ioremap_resource_byname(pdev, "mgmt"); diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index fd484cc7c481..7a11c618b9d9 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -198,7 +198,7 @@ static int histb_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops histb_pcie_host_ops = { - .host_init = histb_pcie_host_init, + .init = histb_pcie_host_init, }; static void histb_pcie_host_disable(struct histb_pcie *hipcie) diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 9c7caed9e706..acbe4f6d3291 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -9,9 +9,11 @@ #include <linux/clk.h> #include <linux/gpio/consumer.h> #include <linux/iopoll.h> +#include <linux/mod_devicetable.h> #include <linux/pci_regs.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/reset.h> #include "../../pci.h" @@ -389,7 +391,7 @@ static const struct dw_pcie_ops intel_pcie_ops = { }; static const struct dw_pcie_host_ops intel_pcie_dw_ops = { - .host_init = intel_pcie_rc_init, + .init = intel_pcie_rc_init, }; static int intel_pcie_probe(struct platform_device *pdev) diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index f90f36bac018..5e8e54f597dd 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -148,6 +148,13 @@ static const struct dw_pcie_ops keembay_pcie_ops = { .stop_link = keembay_pcie_stop_link, }; +static inline void keembay_pcie_disable_clock(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + static inline struct clk *keembay_pcie_probe_clock(struct device *dev, const char *id, u64 rate) { @@ -168,9 +175,7 @@ static inline struct clk *keembay_pcie_probe_clock(struct device *dev, if (ret) return ERR_PTR(ret); - ret = devm_add_action_or_reset(dev, - (void(*)(void *))clk_disable_unprepare, - clk); + ret = devm_add_action_or_reset(dev, keembay_pcie_disable_clock, clk); if (ret) return ERR_PTR(ret); @@ -284,19 +289,18 @@ static void keembay_pcie_ep_init(struct dw_pcie_ep *ep) } static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - /* Legacy interrupts are not supported in Keem Bay */ - dev_err(pci->dev, "Legacy IRQ is not supported\n"); + case PCI_IRQ_INTX: + /* INTx interrupts are not supported in Keem Bay */ + dev_err(pci->dev, "INTx IRQ is not supported\n"); return -EINVAL; - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "Unknown IRQ type %d\n", type); @@ -308,8 +312,12 @@ static const struct pci_epc_features keembay_pcie_epc_features = { .linkup_notifier = false, .msi_capable = true, .msix_capable = true, - .reserved_bar = BIT(BAR_1) | BIT(BAR_3) | BIT(BAR_5), - .bar_fixed_64bit = BIT(BAR_0) | BIT(BAR_2) | BIT(BAR_4), + .bar[BAR_0] = { .only_64bit = true, }, + .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_2] = { .only_64bit = true, }, + .bar[BAR_3] = { .type = BAR_RESERVED, }, + .bar[BAR_4] = { .only_64bit = true, }, + .bar[BAR_5] = { .type = BAR_RESERVED, }, .align = SZ_16K, }; @@ -320,7 +328,7 @@ keembay_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops keembay_pcie_ep_ops = { - .ep_init = keembay_pcie_ep_init, + .init = keembay_pcie_ep_init, .raise_irq = keembay_pcie_ep_raise_irq, .get_features = keembay_pcie_get_features, }; diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index d09507f822a7..d5523f302102 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -16,8 +16,7 @@ #include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/mfd/syscon.h> -#include <linux/of_address.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_pci.h> #include <linux/phy/phy.h> @@ -367,7 +366,6 @@ static int kirin_pcie_get_gpio_enable(struct kirin_pcie *pcie, struct platform_device *pdev) { struct device *dev = &pdev->dev; - char name[32]; int ret, i; /* This is an optional property */ @@ -388,9 +386,8 @@ static int kirin_pcie_get_gpio_enable(struct kirin_pcie *pcie, if (pcie->gpio_id_clkreq[i] < 0) return pcie->gpio_id_clkreq[i]; - sprintf(name, "pcie_clkreq_%d", i); - pcie->clkreq_names[i] = devm_kstrdup_const(dev, name, - GFP_KERNEL); + pcie->clkreq_names[i] = devm_kasprintf(dev, GFP_KERNEL, + "pcie_clkreq_%d", i); if (!pcie->clkreq_names[i]) return -ENOMEM; } @@ -405,7 +402,6 @@ static int kirin_pcie_parse_port(struct kirin_pcie *pcie, struct device *dev = &pdev->dev; struct device_node *parent, *child; int ret, slot, i; - char name[32]; for_each_available_child_of_node(node, parent) { for_each_available_child_of_node(parent, child) { @@ -431,9 +427,9 @@ static int kirin_pcie_parse_port(struct kirin_pcie *pcie, slot = PCI_SLOT(ret); - sprintf(name, "pcie_perst_%d", slot); - pcie->reset_names[i] = devm_kstrdup_const(dev, name, - GFP_KERNEL); + pcie->reset_names[i] = devm_kasprintf(dev, GFP_KERNEL, + "pcie_perst_%d", + slot); if (!pcie->reset_names[i]) { ret = -ENOMEM; goto put_node; @@ -673,7 +669,7 @@ static const struct dw_pcie_ops kirin_dw_pcie_ops = { }; static const struct dw_pcie_host_ops kirin_pcie_host_ops = { - .host_init = kirin_pcie_host_init, + .init = kirin_pcie_host_init, }; static int kirin_pcie_power_off(struct kirin_pcie *kirin_pcie) @@ -742,15 +738,13 @@ err: return ret; } -static int __exit kirin_pcie_remove(struct platform_device *pdev) +static void kirin_pcie_remove(struct platform_device *pdev) { struct kirin_pcie *kirin_pcie = platform_get_drvdata(pdev); dw_pcie_host_deinit(&kirin_pcie->pci->pp); kirin_pcie_power_off(kirin_pcie); - - return 0; } struct kirin_pcie_data { @@ -819,7 +813,7 @@ static int kirin_pcie_probe(struct platform_device *pdev) static struct platform_driver kirin_pcie_driver = { .probe = kirin_pcie_probe, - .remove = __exit_p(kirin_pcie_remove), + .remove_new = kirin_pcie_remove, .driver = { .name = "kirin-pcie", .of_match_table = kirin_pcie_match, diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 0fe7f06f2102..36e5e80cd22f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -13,6 +13,7 @@ #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> +#include <linux/interconnect.h> #include <linux/mfd/syscon.h> #include <linux/phy/pcie.h> #include <linux/phy/phy.h> @@ -22,6 +23,7 @@ #include <linux/reset.h> #include <linux/module.h> +#include "../../pci.h" #include "pcie-designware.h" /* PARF registers */ @@ -74,6 +76,7 @@ #define PARF_INT_ALL_PLS_ERR BIT(15) #define PARF_INT_ALL_PME_LEGACY BIT(16) #define PARF_INT_ALL_PLS_PME BIT(17) +#define PARF_INT_ALL_EDMA BIT(22) /* PARF_BDF_TO_SID_CFG register fields */ #define PARF_BDF_TO_SID_BYPASS BIT(0) @@ -121,6 +124,7 @@ /* ELBI registers */ #define ELBI_SYS_STTS 0x08 +#define ELBI_CS2_ENABLE 0xa4 /* DBI registers */ #define DBI_CON_STATUS 0x44 @@ -133,6 +137,9 @@ #define CORE_RESET_TIME_US_MAX 1005 #define WAKE_DELAY_US 2000 /* 2 ms */ +#define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \ + Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed])) + #define to_pcie_ep(x) dev_get_drvdata((x)->dev) enum qcom_pcie_ep_link_status { @@ -155,6 +162,7 @@ enum qcom_pcie_ep_link_status { * @wake: WAKE# GPIO * @phy: PHY controller block * @debugfs: PCIe Endpoint Debugfs directory + * @icc_mem: Handle to an interconnect path between PCIe and MEM * @clks: PCIe clocks * @num_clks: PCIe clocks count * @perst_en: Flag for PERST enable @@ -178,6 +186,8 @@ struct qcom_pcie_ep { struct phy *phy; struct dentry *debugfs; + struct icc_path *icc_mem; + struct clk_bulk_data *clks; int num_clks; @@ -253,8 +263,46 @@ static void qcom_pcie_dw_stop_link(struct dw_pcie *pci) disable_irq(pcie_ep->perst_irq); } +static void qcom_pcie_dw_write_dbi2(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size, u32 val) +{ + struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci); + int ret; + + writel(1, pcie_ep->elbi + ELBI_CS2_ENABLE); + + ret = dw_pcie_write(pci->dbi_base2 + reg, size, val); + if (ret) + dev_err(pci->dev, "Failed to write DBI2 register (0x%x): %d\n", reg, ret); + + writel(0, pcie_ep->elbi + ELBI_CS2_ENABLE); +} + +static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep) +{ + struct dw_pcie *pci = &pcie_ep->pci; + u32 offset, status; + int speed, width; + int ret; + + if (!pcie_ep->icc_mem) + return; + + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA); + + speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status); + width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status); + + ret = icc_set_bw(pcie_ep->icc_mem, 0, width * QCOM_PCIE_LINK_SPEED_TO_BW(speed)); + if (ret) + dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", + ret); +} + static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep) { + struct dw_pcie *pci = &pcie_ep->pci; int ret; ret = clk_bulk_prepare_enable(pcie_ep->num_clks, pcie_ep->clks); @@ -277,8 +325,24 @@ static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep) if (ret) goto err_phy_exit; + /* + * Some Qualcomm platforms require interconnect bandwidth constraints + * to be set before enabling interconnect clocks. + * + * Set an initial peak bandwidth corresponding to single-lane Gen 1 + * for the pcie-mem path. + */ + ret = icc_set_bw(pcie_ep->icc_mem, 0, QCOM_PCIE_LINK_SPEED_TO_BW(1)); + if (ret) { + dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", + ret); + goto err_phy_off; + } + return 0; +err_phy_off: + phy_power_off(pcie_ep->phy); err_phy_exit: phy_exit(pcie_ep->phy); err_disable_clk: @@ -289,6 +353,7 @@ err_disable_clk: static void qcom_pcie_disable_resources(struct qcom_pcie_ep *pcie_ep) { + icc_set_bw(pcie_ep->icc_mem, 0, 0); phy_power_off(pcie_ep->phy); phy_exit(pcie_ep->phy); clk_bulk_disable_unprepare(pcie_ep->num_clks, pcie_ep->clks); @@ -395,7 +460,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) writel_relaxed(0, pcie_ep->parf + PARF_INT_ALL_MASK); val = PARF_INT_ALL_LINK_DOWN | PARF_INT_ALL_BME | PARF_INT_ALL_PM_TURNOFF | PARF_INT_ALL_DSTATE_CHANGE | - PARF_INT_ALL_LINK_UP; + PARF_INT_ALL_LINK_UP | PARF_INT_ALL_EDMA; writel_relaxed(val, pcie_ep->parf + PARF_INT_ALL_MASK); ret = dw_pcie_ep_init_complete(&pcie_ep->pci.ep); @@ -415,7 +480,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) /* Gate Master AXI clock to MHI bus during L1SS */ val = readl_relaxed(pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL); val &= ~PARF_MSTR_AXI_CLK_EN; - val = readl_relaxed(pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL); + writel_relaxed(val, pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL); dw_pcie_ep_init_notify(&pcie_ep->pci.ep); @@ -451,6 +516,7 @@ static const struct dw_pcie_ops pci_ops = { .link_up = qcom_pcie_dw_link_up, .start_link = qcom_pcie_dw_start_link, .stop_link = qcom_pcie_dw_stop_link, + .write_dbi2 = qcom_pcie_dw_write_dbi2, }; static int qcom_pcie_ep_get_io_resources(struct platform_device *pdev, @@ -550,6 +616,10 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev, if (IS_ERR(pcie_ep->phy)) ret = PTR_ERR(pcie_ep->phy); + pcie_ep->icc_mem = devm_of_icc_get(dev, "pcie-mem"); + if (IS_ERR(pcie_ep->icc_mem)) + ret = PTR_ERR(pcie_ep->icc_mem); + return ret; } @@ -573,6 +643,7 @@ static irqreturn_t qcom_pcie_ep_global_irq_thread(int irq, void *data) } else if (FIELD_GET(PARF_INT_ALL_BME, status)) { dev_dbg(dev, "Received BME event. Link is enabled!\n"); pcie_ep->link_status = QCOM_PCIE_EP_LINK_ENABLED; + qcom_pcie_ep_icc_update(pcie_ep); pci_epc_bme_notify(pci->ep.epc); } else if (FIELD_GET(PARF_INT_ALL_PM_TURNOFF, status)) { dev_dbg(dev, "Received PM Turn-off event! Entering L23\n"); @@ -593,7 +664,7 @@ static irqreturn_t qcom_pcie_ep_global_irq_thread(int irq, void *data) dw_pcie_ep_linkup(&pci->ep); pcie_ep->link_status = QCOM_PCIE_EP_LINK_UP; } else { - dev_dbg(dev, "Received unknown event: %d\n", status); + dev_err(dev, "Received unknown event: %d\n", status); } return IRQ_HANDLED; @@ -655,14 +726,14 @@ static int qcom_pcie_ep_enable_irq_resources(struct platform_device *pdev, } static int qcom_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return dw_pcie_ep_raise_legacy_irq(ep, func_no); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); default: dev_err(pci->dev, "Unknown IRQ type\n"); @@ -706,6 +777,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = { .core_init_notifier = true, .msi_capable = true, .msix_capable = false, + .align = SZ_4K, }; static const struct pci_epc_features * @@ -724,7 +796,7 @@ static void qcom_pcie_ep_init(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pci_ep_ops = { - .ep_init = qcom_pcie_ep_init, + .init = qcom_pcie_ep_init, .raise_irq = qcom_pcie_ep_raise_irq, .get_features = qcom_pcie_epc_get_features, }; @@ -743,6 +815,7 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev) pcie_ep->pci.dev = dev; pcie_ep->pci.ops = &pci_ops; pcie_ep->pci.ep.ops = &pci_ep_ops; + pcie_ep->pci.edma.nr_irqs = 1; platform_set_drvdata(pdev, pcie_ep); ret = qcom_pcie_ep_get_resources(pdev, pcie_ep); diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 7a87a47eb7ed..14772edcf0d3 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -19,7 +19,7 @@ #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_gpio.h> #include <linux/pci.h> #include <linux/pm_runtime.h> @@ -43,7 +43,6 @@ #define PARF_PHY_REFCLK 0x4c #define PARF_CONFIG_BITS 0x50 #define PARF_DBI_BASE_ADDR 0x168 -#define PARF_SLV_ADDR_SPACE_SIZE_2_3_3 0x16c /* Register offset specific to IP ver 2.3.3 */ #define PARF_MHI_CLOCK_RESET_CTRL 0x174 #define PARF_AXI_MSTR_WR_ADDR_HALT 0x178 #define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8 @@ -54,6 +53,7 @@ #define PARF_SLV_ADDR_SPACE_SIZE 0x358 #define PARF_DEVICE_TYPE 0x1000 #define PARF_BDF_TO_SID_TABLE_N 0x2000 +#define PARF_BDF_TO_SID_CFG 0x2c00 /* ELBI registers */ #define ELBI_SYS_CTRL 0x04 @@ -121,6 +121,9 @@ /* PARF_DEVICE_TYPE register fields */ #define DEVICE_TYPE_RC 0x4 +/* PARF_BDF_TO_SID_CFG fields */ +#define BDF_TO_SID_BYPASS BIT(0) + /* ELBI_SYS_CTRL register fields */ #define ELBI_SYS_CTRL_LT_ENABLE BIT(0) @@ -148,6 +151,9 @@ #define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0)) +#define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \ + Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed])) + #define QCOM_PCIE_1_0_0_MAX_CLOCKS 4 struct qcom_pcie_resources_1_0_0 { struct clk_bulk_data clks[QCOM_PCIE_1_0_0_MAX_CLOCKS]; @@ -219,6 +225,7 @@ struct qcom_pcie_ops { int (*get_resources)(struct qcom_pcie *pcie); int (*init)(struct qcom_pcie *pcie); int (*post_init)(struct qcom_pcie *pcie); + void (*host_post_init)(struct qcom_pcie *pcie); void (*deinit)(struct qcom_pcie *pcie); void (*ltssm_enable)(struct qcom_pcie *pcie); int (*config_sid)(struct qcom_pcie *pcie); @@ -226,6 +233,7 @@ struct qcom_pcie_ops { struct qcom_pcie_cfg { const struct qcom_pcie_ops *ops; + bool no_l0s; }; struct qcom_pcie { @@ -269,6 +277,26 @@ static int qcom_pcie_start_link(struct dw_pcie *pci) return 0; } +static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci) +{ + struct qcom_pcie *pcie = to_qcom_pcie(pci); + u16 offset; + u32 val; + + if (!pcie->cfg->no_l0s) + return; + + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + + dw_pcie_dbi_ro_wr_en(pci); + + val = readl(pci->dbi_base + offset + PCI_EXP_LNKCAP); + val &= ~PCI_EXP_LNKCAP_ASPM_L0S; + writel(val, pci->dbi_base + offset + PCI_EXP_LNKCAP); + + dw_pcie_dbi_ro_wr_dis(pci); +} + static void qcom_pcie_clear_hpc(struct dw_pcie *pci) { u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); @@ -797,8 +825,7 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); u32 val; - writel(SLV_ADDR_SPACE_SZ, - pcie->parf + PARF_SLV_ADDR_SPACE_SIZE_2_3_3); + writel(SLV_ADDR_SPACE_SZ, pcie->parf + PARF_SLV_ADDR_SPACE_SIZE); val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; @@ -959,11 +986,31 @@ err_disable_regulators: static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) { + qcom_pcie_clear_aspm_l0s(pcie->pci); qcom_pcie_clear_hpc(pcie->pci); return 0; } +static int qcom_pcie_enable_aspm(struct pci_dev *pdev, void *userdata) +{ + /* + * Downstream devices need to be in D0 state before enabling PCI PM + * substates. + */ + pci_set_power_state_locked(pdev, PCI_D0); + pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL); + + return 0; +} + +static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie) +{ + struct dw_pcie_rp *pp = &pcie->pci->pp; + + pci_walk_bus(pp->bridge->bus, qcom_pcie_enable_aspm, NULL); +} + static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; @@ -987,11 +1034,17 @@ static int qcom_pcie_config_sid_1_9_0(struct qcom_pcie *pcie) u8 qcom_pcie_crc8_table[CRC8_TABLE_SIZE]; int i, nr_map, size = 0; u32 smmu_sid_base; + u32 val; of_get_property(dev->of_node, "iommu-map", &size); if (!size) return 0; + /* Enable BDF to SID translation by disabling bypass mode (default) */ + val = readl(pcie->parf + PARF_BDF_TO_SID_CFG); + val &= ~BDF_TO_SID_BYPASS; + writel(val, pcie->parf + PARF_BDF_TO_SID_CFG); + map = kzalloc(size, GFP_KERNEL); if (!map) return -ENOMEM; @@ -1216,9 +1269,19 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) pcie->cfg->ops->deinit(pcie); } +static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct qcom_pcie *pcie = to_qcom_pcie(pci); + + if (pcie->cfg->ops->host_post_init) + pcie->cfg->ops->host_post_init(pcie); +} + static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { - .host_init = qcom_pcie_host_init, - .host_deinit = qcom_pcie_host_deinit, + .init = qcom_pcie_host_init, + .deinit = qcom_pcie_host_deinit, + .post_init = qcom_pcie_host_post_init, }; /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ @@ -1280,6 +1343,7 @@ static const struct qcom_pcie_ops ops_1_9_0 = { .get_resources = qcom_pcie_get_resources_2_7_0, .init = qcom_pcie_init_2_7_0, .post_init = qcom_pcie_post_init_2_7_0, + .host_post_init = qcom_pcie_host_post_init_2_7_0, .deinit = qcom_pcie_deinit_2_7_0, .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, .config_sid = qcom_pcie_config_sid_1_9_0, @@ -1326,6 +1390,11 @@ static const struct qcom_pcie_cfg cfg_2_9_0 = { .ops = &ops_2_9_0, }; +static const struct qcom_pcie_cfg cfg_sc8280xp = { + .ops = &ops_1_9_0, + .no_l0s = true, +}; + static const struct dw_pcie_ops dw_pcie_ops = { .link_up = qcom_pcie_link_up, .start_link = qcom_pcie_start_link, @@ -1347,7 +1416,7 @@ static int qcom_pcie_icc_init(struct qcom_pcie *pcie) * Set an initial peak bandwidth corresponding to single-lane Gen 1 * for the pcie-mem path. */ - ret = icc_set_bw(pcie->icc_mem, 0, MBps_to_icc(250)); + ret = icc_set_bw(pcie->icc_mem, 0, QCOM_PCIE_LINK_SPEED_TO_BW(1)); if (ret) { dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", ret); @@ -1360,7 +1429,7 @@ static int qcom_pcie_icc_init(struct qcom_pcie *pcie) static void qcom_pcie_icc_update(struct qcom_pcie *pcie) { struct dw_pcie *pci = pcie->pci; - u32 offset, status, bw; + u32 offset, status; int speed, width; int ret; @@ -1377,22 +1446,7 @@ static void qcom_pcie_icc_update(struct qcom_pcie *pcie) speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status); width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status); - switch (speed) { - case 1: - bw = MBps_to_icc(250); - break; - case 2: - bw = MBps_to_icc(500); - break; - default: - WARN_ON_ONCE(1); - fallthrough; - case 3: - bw = MBps_to_icc(985); - break; - } - - ret = icc_set_bw(pcie->icc_mem, 0, width * bw); + ret = icc_set_bw(pcie->icc_mem, 0, width * QCOM_PCIE_LINK_SPEED_TO_BW(speed)); if (ret) { dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", ret); @@ -1612,10 +1666,11 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-ipq8074-gen3", .data = &cfg_2_9_0 }, { .compatible = "qcom,pcie-msm8996", .data = &cfg_2_3_2 }, { .compatible = "qcom,pcie-qcs404", .data = &cfg_2_4_0 }, - { .compatible = "qcom,pcie-sa8540p", .data = &cfg_1_9_0 }, + { .compatible = "qcom,pcie-sa8540p", .data = &cfg_sc8280xp }, + { .compatible = "qcom,pcie-sa8775p", .data = &cfg_1_9_0}, { .compatible = "qcom,pcie-sc7280", .data = &cfg_1_9_0 }, { .compatible = "qcom,pcie-sc8180x", .data = &cfg_1_9_0 }, - { .compatible = "qcom,pcie-sc8280xp", .data = &cfg_1_9_0 }, + { .compatible = "qcom,pcie-sc8280xp", .data = &cfg_sc8280xp }, { .compatible = "qcom,pcie-sdm845", .data = &cfg_2_7_0 }, { .compatible = "qcom,pcie-sdx55", .data = &cfg_1_9_0 }, { .compatible = "qcom,pcie-sm8150", .data = &cfg_1_9_0 }, @@ -1624,6 +1679,7 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-sm8450-pcie0", .data = &cfg_1_9_0 }, { .compatible = "qcom,pcie-sm8450-pcie1", .data = &cfg_1_9_0 }, { .compatible = "qcom,pcie-sm8550", .data = &cfg_1_9_0 }, + { .compatible = "qcom,pcie-x1e80100", .data = &cfg_1_9_0 }, { } }; diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c new file mode 100644 index 000000000000..0be760ed420b --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PCIe controller driver for Renesas R-Car Gen4 Series SoCs + * Copyright (C) 2022-2023 Renesas Electronics Corporation + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include "../../pci.h" +#include "pcie-designware.h" + +/* Renesas-specific */ +/* PCIe Mode Setting Register 0 */ +#define PCIEMSR0 0x0000 +#define BIFUR_MOD_SET_ON BIT(0) +#define DEVICE_TYPE_EP 0 +#define DEVICE_TYPE_RC BIT(4) + +/* PCIe Interrupt Status 0 */ +#define PCIEINTSTS0 0x0084 + +/* PCIe Interrupt Status 0 Enable */ +#define PCIEINTSTS0EN 0x0310 +#define MSI_CTRL_INT BIT(26) +#define SMLH_LINK_UP BIT(7) +#define RDLH_LINK_UP BIT(6) + +/* PCIe DMA Interrupt Status Enable */ +#define PCIEDMAINTSTSEN 0x0314 +#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0) + +/* PCIe Reset Control Register 1 */ +#define PCIERSTCTRL1 0x0014 +#define APP_HOLD_PHY_RST BIT(16) +#define APP_LTSSM_ENABLE BIT(0) + +#define RCAR_NUM_SPEED_CHANGE_RETRIES 10 +#define RCAR_MAX_LINK_SPEED 4 + +#define RCAR_GEN4_PCIE_EP_FUNC_DBI_OFFSET 0x1000 +#define RCAR_GEN4_PCIE_EP_FUNC_DBI2_OFFSET 0x800 + +struct rcar_gen4_pcie { + struct dw_pcie dw; + void __iomem *base; + struct platform_device *pdev; + enum dw_pcie_device_mode mode; +}; +#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw) + +/* Common */ +static void rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar, + bool enable) +{ + u32 val; + + val = readl(rcar->base + PCIERSTCTRL1); + if (enable) { + val |= APP_LTSSM_ENABLE; + val &= ~APP_HOLD_PHY_RST; + } else { + /* + * Since the datasheet of R-Car doesn't mention how to assert + * the APP_HOLD_PHY_RST, don't assert it again. Otherwise, + * hang-up issue happened in the dw_edma_core_off() when + * the controller didn't detect a PCI device. + */ + val &= ~APP_LTSSM_ENABLE; + } + writel(val, rcar->base + PCIERSTCTRL1); +} + +static int rcar_gen4_pcie_link_up(struct dw_pcie *dw) +{ + struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); + u32 val, mask; + + val = readl(rcar->base + PCIEINTSTS0); + mask = RDLH_LINK_UP | SMLH_LINK_UP; + + return (val & mask) == mask; +} + +/* + * Manually initiate the speed change. Return 0 if change succeeded; otherwise + * -ETIMEDOUT. + */ +static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw) +{ + u32 val; + int i; + + val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_SPEED_CHANGE; + dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + + val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL); + val |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + + for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) { + val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL); + if (!(val & PORT_LOGIC_SPEED_CHANGE)) + return 0; + usleep_range(10000, 11000); + } + + return -ETIMEDOUT; +} + +/* + * Enable LTSSM of this controller and manually initiate the speed change. + * Always return 0. + */ +static int rcar_gen4_pcie_start_link(struct dw_pcie *dw) +{ + struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); + int i, changes; + + rcar_gen4_pcie_ltssm_enable(rcar, true); + + /* + * Require direct speed change with retrying here if the link_gen is + * PCIe Gen2 or higher. + */ + changes = min_not_zero(dw->link_gen, RCAR_MAX_LINK_SPEED) - 1; + + /* + * Since dw_pcie_setup_rc() sets it once, PCIe Gen2 will be trained. + * So, this needs remaining times for up to PCIe Gen4 if RC mode. + */ + if (changes && rcar->mode == DW_PCIE_RC_TYPE) + changes--; + + for (i = 0; i < changes; i++) { + /* It may not be connected in EP mode yet. So, break the loop */ + if (rcar_gen4_pcie_speed_change(dw)) + break; + } + + return 0; +} + +static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw) +{ + struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); + + rcar_gen4_pcie_ltssm_enable(rcar, false); +} + +static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie *dw = &rcar->dw; + u32 val; + int ret; + + ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, dw->core_clks); + if (ret) { + dev_err(dw->dev, "Enabling core clocks failed\n"); + return ret; + } + + if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) + reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); + + val = readl(rcar->base + PCIEMSR0); + if (rcar->mode == DW_PCIE_RC_TYPE) { + val |= DEVICE_TYPE_RC; + } else if (rcar->mode == DW_PCIE_EP_TYPE) { + val |= DEVICE_TYPE_EP; + } else { + ret = -EINVAL; + goto err_unprepare; + } + + if (dw->num_lanes < 4) + val |= BIFUR_MOD_SET_ON; + + writel(val, rcar->base + PCIEMSR0); + + ret = reset_control_deassert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); + if (ret) + goto err_unprepare; + + return 0; + +err_unprepare: + clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks); + + return ret; +} + +static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie *dw = &rcar->dw; + + reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); + clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks); +} + +static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar) +{ + struct device *dev = rcar->dw.dev; + int err; + + pm_runtime_enable(dev); + err = pm_runtime_resume_and_get(dev); + if (err < 0) { + dev_err(dev, "Runtime resume failed\n"); + pm_runtime_disable(dev); + } + + return err; +} + +static void rcar_gen4_pcie_unprepare(struct rcar_gen4_pcie *rcar) +{ + struct device *dev = rcar->dw.dev; + + pm_runtime_put(dev); + pm_runtime_disable(dev); +} + +static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar) +{ + /* Renesas-specific registers */ + rcar->base = devm_platform_ioremap_resource_byname(rcar->pdev, "app"); + + return PTR_ERR_OR_ZERO(rcar->base); +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = rcar_gen4_pcie_start_link, + .stop_link = rcar_gen4_pcie_stop_link, + .link_up = rcar_gen4_pcie_link_up, +}; + +static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_gen4_pcie *rcar; + + rcar = devm_kzalloc(dev, sizeof(*rcar), GFP_KERNEL); + if (!rcar) + return ERR_PTR(-ENOMEM); + + rcar->dw.ops = &dw_pcie_ops; + rcar->dw.dev = dev; + rcar->pdev = pdev; + dw_pcie_cap_set(&rcar->dw, EDMA_UNROLL); + dw_pcie_cap_set(&rcar->dw, REQ_RES); + platform_set_drvdata(pdev, rcar); + + return rcar; +} + +/* Host mode */ +static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *dw = to_dw_pcie_from_pp(pp); + struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); + int ret; + u32 val; + + gpiod_set_value_cansleep(dw->pe_rst, 1); + + ret = rcar_gen4_pcie_common_init(rcar); + if (ret) + return ret; + + /* + * According to the section 3.5.7.2 "RC Mode" in DWC PCIe Dual Mode + * Rev.5.20a and 3.5.6.1 "RC mode" in DWC PCIe RC databook v5.20a, we + * should disable two BARs to avoid unnecessary memory assignment + * during device enumeration. + */ + dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_0, 0x0); + dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_1, 0x0); + + /* Enable MSI interrupt signal */ + val = readl(rcar->base + PCIEINTSTS0EN); + val |= MSI_CTRL_INT; + writel(val, rcar->base + PCIEINTSTS0EN); + + msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */ + + gpiod_set_value_cansleep(dw->pe_rst, 0); + + return 0; +} + +static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp) +{ + struct dw_pcie *dw = to_dw_pcie_from_pp(pp); + struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw); + + gpiod_set_value_cansleep(dw->pe_rst, 1); + rcar_gen4_pcie_common_deinit(rcar); +} + +static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = { + .init = rcar_gen4_pcie_host_init, + .deinit = rcar_gen4_pcie_host_deinit, +}; + +static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie_rp *pp = &rcar->dw.pp; + + if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_HOST)) + return -ENODEV; + + pp->num_vectors = MAX_MSI_IRQS; + pp->ops = &rcar_gen4_pcie_host_ops; + + return dw_pcie_host_init(pp); +} + +static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar) +{ + dw_pcie_host_deinit(&rcar->dw.pp); +} + +/* Endpoint mode */ +static void 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); + int ret; + + ret = rcar_gen4_pcie_common_init(rcar); + if (ret) + return; + + writel(PCIEDMAINTSTSEN_INIT, rcar->base + PCIEDMAINTSTSEN); +} + +static void rcar_gen4_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) + dw_pcie_ep_reset_bar(pci, bar); +} + +static void rcar_gen4_pcie_ep_deinit(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); + + writel(0, rcar->base + PCIEDMAINTSTSEN); + rcar_gen4_pcie_common_deinit(rcar); +} + +static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, + unsigned int type, u16 interrupt_num) +{ + struct dw_pcie *dw = to_dw_pcie_from_ep(ep); + + switch (type) { + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: + return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); + default: + dev_err(dw->dev, "Unknown IRQ type\n"); + return -EINVAL; + } + + return 0; +} + +static const struct pci_epc_features rcar_gen4_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = false, + .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_3] = { .type = BAR_RESERVED, }, + .bar[BAR_5] = { .type = BAR_RESERVED, }, + .align = SZ_1M, +}; + +static const struct pci_epc_features* +rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep) +{ + return &rcar_gen4_pcie_epc_features; +} + +static unsigned int rcar_gen4_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep, + u8 func_no) +{ + return func_no * RCAR_GEN4_PCIE_EP_FUNC_DBI_OFFSET; +} + +static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, + u8 func_no) +{ + return func_no * RCAR_GEN4_PCIE_EP_FUNC_DBI2_OFFSET; +} + +static const struct dw_pcie_ep_ops pcie_ep_ops = { + .pre_init = rcar_gen4_pcie_ep_pre_init, + .init = rcar_gen4_pcie_ep_init, + .deinit = rcar_gen4_pcie_ep_deinit, + .raise_irq = rcar_gen4_pcie_ep_raise_irq, + .get_features = rcar_gen4_pcie_ep_get_features, + .get_dbi_offset = rcar_gen4_pcie_ep_get_dbi_offset, + .get_dbi2_offset = rcar_gen4_pcie_ep_get_dbi2_offset, +}; + +static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar) +{ + struct dw_pcie_ep *ep = &rcar->dw.ep; + + if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_EP)) + return -ENODEV; + + ep->ops = &pcie_ep_ops; + + return dw_pcie_ep_init(ep); +} + +static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar) +{ + dw_pcie_ep_exit(&rcar->dw.ep); +} + +/* Common */ +static int rcar_gen4_add_dw_pcie(struct rcar_gen4_pcie *rcar) +{ + rcar->mode = (uintptr_t)of_device_get_match_data(&rcar->pdev->dev); + + switch (rcar->mode) { + case DW_PCIE_RC_TYPE: + return rcar_gen4_add_dw_pcie_rp(rcar); + case DW_PCIE_EP_TYPE: + return rcar_gen4_add_dw_pcie_ep(rcar); + default: + return -EINVAL; + } +} + +static int rcar_gen4_pcie_probe(struct platform_device *pdev) +{ + struct rcar_gen4_pcie *rcar; + int err; + + rcar = rcar_gen4_pcie_alloc(pdev); + if (IS_ERR(rcar)) + return PTR_ERR(rcar); + + err = rcar_gen4_pcie_get_resources(rcar); + if (err) + return err; + + err = rcar_gen4_pcie_prepare(rcar); + if (err) + return err; + + err = rcar_gen4_add_dw_pcie(rcar); + if (err) + goto err_unprepare; + + return 0; + +err_unprepare: + rcar_gen4_pcie_unprepare(rcar); + + return err; +} + +static void rcar_gen4_remove_dw_pcie(struct rcar_gen4_pcie *rcar) +{ + switch (rcar->mode) { + case DW_PCIE_RC_TYPE: + rcar_gen4_remove_dw_pcie_rp(rcar); + break; + case DW_PCIE_EP_TYPE: + rcar_gen4_remove_dw_pcie_ep(rcar); + break; + default: + break; + } +} + +static void rcar_gen4_pcie_remove(struct platform_device *pdev) +{ + struct rcar_gen4_pcie *rcar = platform_get_drvdata(pdev); + + rcar_gen4_remove_dw_pcie(rcar); + rcar_gen4_pcie_unprepare(rcar); +} + +static const struct of_device_id rcar_gen4_pcie_of_match[] = { + { + .compatible = "renesas,rcar-gen4-pcie", + .data = (void *)DW_PCIE_RC_TYPE, + }, + { + .compatible = "renesas,rcar-gen4-pcie-ep", + .data = (void *)DW_PCIE_EP_TYPE, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, rcar_gen4_pcie_of_match); + +static struct platform_driver rcar_gen4_pcie_driver = { + .driver = { + .name = "pcie-rcar-gen4", + .of_match_table = rcar_gen4_pcie_of_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = rcar_gen4_pcie_probe, + .remove_new = rcar_gen4_pcie_remove, +}; +module_platform_driver(rcar_gen4_pcie_driver); + +MODULE_DESCRIPTION("Renesas R-Car Gen4 PCIe controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/dwc/pcie-spear13xx.c b/drivers/pci/controller/dwc/pcie-spear13xx.c index 99d47ae80331..201dced209f0 100644 --- a/drivers/pci/controller/dwc/pcie-spear13xx.c +++ b/drivers/pci/controller/dwc/pcie-spear13xx.c @@ -148,7 +148,7 @@ static int spear13xx_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops spear13xx_pcie_host_ops = { - .host_init = spear13xx_pcie_host_init, + .init = spear13xx_pcie_host_init, }; static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index e1db909f53ec..1f7b662cb8e1 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -9,6 +9,7 @@ * Author: Vidya Sagar <vidyas@nvidia.com> */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/delay.h> @@ -20,7 +21,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/of_pci.h> #include <linux/pci.h> @@ -126,7 +126,7 @@ #define APPL_LTR_MSG_1 0xC4 #define LTR_MSG_REQ BIT(15) -#define LTR_MST_NO_SNOOP_SHIFT 16 +#define LTR_NOSNOOP_MSG_REQ BIT(31) #define APPL_LTR_MSG_2 0xC8 #define APPL_LTR_MSG_2_LTR_MSG_REQ_STATE BIT(3) @@ -322,9 +322,9 @@ static void tegra_pcie_icc_set(struct tegra_pcie_dw *pcie) speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, val); width = FIELD_GET(PCI_EXP_LNKSTA_NLW, val); - val = width * (PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]) / BITS_PER_BYTE); + val = width * PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]); - if (icc_set_bw(pcie->icc_path, MBps_to_icc(val), 0)) + if (icc_set_bw(pcie->icc_path, Mbps_to_icc(val), 0)) dev_err(pcie->dev, "can't set bw[%u]\n", val); if (speed >= ARRAY_SIZE(pcie_gen_freq)) @@ -347,8 +347,7 @@ static void apply_bad_link_workaround(struct dw_pcie_rp *pp) */ val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA); if (val & PCI_EXP_LNKSTA_LBMS) { - current_link_width = (val & PCI_EXP_LNKSTA_NLW) >> - PCI_EXP_LNKSTA_NLW_SHIFT; + current_link_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, val); if (pcie->init_link_width > current_link_width) { dev_warn(pci->dev, "PCIe link is bad, width reduced\n"); val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + @@ -497,8 +496,12 @@ static irqreturn_t tegra_pcie_ep_irq_thread(int irq, void *arg) ktime_t timeout; /* 110us for both snoop and no-snoop */ - val = 110 | (2 << PCI_LTR_SCALE_SHIFT) | LTR_MSG_REQ; - val |= (val << LTR_MST_NO_SNOOP_SHIFT); + val = FIELD_PREP(PCI_LTR_VALUE_MASK, 110) | + FIELD_PREP(PCI_LTR_SCALE_MASK, 2) | + LTR_MSG_REQ | + FIELD_PREP(PCI_LTR_NOSNOOP_VALUE, 110) | + FIELD_PREP(PCI_LTR_NOSNOOP_SCALE, 2) | + LTR_NOSNOOP_MSG_REQ; appl_writel(pcie, val, APPL_LTR_MSG_1); /* Send LTR upstream */ @@ -761,8 +764,7 @@ static void tegra_pcie_enable_system_interrupts(struct dw_pcie_rp *pp) val_w = dw_pcie_readw_dbi(&pcie->pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA); - pcie->init_link_width = (val_w & PCI_EXP_LNKSTA_NLW) >> - PCI_EXP_LNKSTA_NLW_SHIFT; + pcie->init_link_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, val_w); val_w = dw_pcie_readw_dbi(&pcie->pci, pcie->pcie_cap_base + PCI_EXP_LNKCTL); @@ -771,13 +773,13 @@ static void tegra_pcie_enable_system_interrupts(struct dw_pcie_rp *pp) val_w); } -static void tegra_pcie_enable_legacy_interrupts(struct dw_pcie_rp *pp) +static void tegra_pcie_enable_intx_interrupts(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); u32 val; - /* Enable legacy interrupt generation */ + /* Enable INTX interrupt generation */ val = appl_readl(pcie, APPL_INTR_EN_L0_0); val |= APPL_INTR_EN_L0_0_SYS_INTR_EN; val |= APPL_INTR_EN_L0_0_INT_INT_EN; @@ -828,7 +830,7 @@ static void tegra_pcie_enable_interrupts(struct dw_pcie_rp *pp) appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_17); tegra_pcie_enable_system_interrupts(pp); - tegra_pcie_enable_legacy_interrupts(pp); + tegra_pcie_enable_intx_interrupts(pp); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_enable_msi_interrupts(pp); } @@ -900,11 +902,6 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp) pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci, PCI_CAP_ID_EXP); - val_16 = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_DEVCTL); - val_16 &= ~PCI_EXP_DEVCTL_PAYLOAD; - val_16 |= PCI_EXP_DEVCTL_PAYLOAD_256B; - dw_pcie_writew_dbi(pci, pcie->pcie_cap_base + PCI_EXP_DEVCTL, val_16); - val = dw_pcie_readl_dbi(pci, PCI_IO_BASE); val &= ~(IO_BASE_IO_DECODE | IO_BASE_IO_DECODE_BIT8); dw_pcie_writel_dbi(pci, PCI_IO_BASE, val); @@ -923,12 +920,6 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp) AMBA_ERROR_RESPONSE_CRS_SHIFT); dw_pcie_writel_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT, val); - /* Configure Max lane width from DT */ - val = dw_pcie_readl_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP); - val &= ~PCI_EXP_LNKCAP_MLW; - val |= (pcie->num_lanes << PCI_EXP_LNKSTA_NLW_SHIFT); - dw_pcie_writel_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP, val); - /* Clear Slot Clock Configuration bit if SRNS configuration */ if (pcie->enable_srns) { val_16 = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + @@ -1069,7 +1060,7 @@ static const struct dw_pcie_ops tegra_dw_pcie_ops = { }; static const struct dw_pcie_host_ops tegra_pcie_dw_host_ops = { - .host_init = tegra_pcie_dw_host_init, + .init = tegra_pcie_dw_host_init, }; static void tegra_pcie_disable_phy(struct tegra_pcie_dw *pcie) @@ -1887,11 +1878,6 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie) pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci, PCI_CAP_ID_EXP); - val_16 = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_DEVCTL); - val_16 &= ~PCI_EXP_DEVCTL_PAYLOAD; - val_16 |= PCI_EXP_DEVCTL_PAYLOAD_256B; - dw_pcie_writew_dbi(pci, pcie->pcie_cap_base + PCI_EXP_DEVCTL, val_16); - /* Clear Slot Clock Configuration bit if SRNS configuration */ if (pcie->enable_srns) { val_16 = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + @@ -1961,7 +1947,7 @@ static irqreturn_t tegra_pcie_ep_pex_rst_irq(int irq, void *arg) return IRQ_HANDLED; } -static int tegra_pcie_ep_raise_legacy_irq(struct tegra_pcie_dw *pcie, u16 irq) +static int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq) { /* Tegra194 supports only INTA */ if (irq > 1) @@ -1993,20 +1979,19 @@ static int tegra_pcie_ep_raise_msix_irq(struct tegra_pcie_dw *pcie, u16 irq) } static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct tegra_pcie_dw *pcie = to_tegra_pcie(pci); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return tegra_pcie_ep_raise_legacy_irq(pcie, interrupt_num); + case PCI_IRQ_INTX: + return tegra_pcie_ep_raise_intx_irq(pcie, interrupt_num); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_MSI: return tegra_pcie_ep_raise_msi_irq(pcie, interrupt_num); - case PCI_EPC_IRQ_MSIX: + case PCI_IRQ_MSIX: return tegra_pcie_ep_raise_msix_irq(pcie, interrupt_num); default: @@ -2022,9 +2007,13 @@ static const struct pci_epc_features tegra_pcie_epc_features = { .core_init_notifier = true, .msi_capable = false, .msix_capable = false, - .reserved_bar = 1 << BAR_2 | 1 << BAR_3 | 1 << BAR_4 | 1 << BAR_5, - .bar_fixed_64bit = 1 << BAR_0, - .bar_fixed_size[0] = SZ_1M, + .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, + .only_64bit = true, }, + .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_2] = { .type = BAR_RESERVED, }, + .bar[BAR_3] = { .type = BAR_RESERVED, }, + .bar[BAR_4] = { .type = BAR_RESERVED, }, + .bar[BAR_5] = { .type = BAR_RESERVED, }, }; static const struct pci_epc_features* diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index 4d0a587c0ba5..639bc2e12476 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -11,7 +11,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/iopoll.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pci.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> @@ -212,7 +212,7 @@ static void uniphier_pcie_ep_init(struct dw_pcie_ep *ep) dw_pcie_ep_reset_bar(pci, bar); } -static int uniphier_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep) +static int uniphier_pcie_ep_raise_intx_irq(struct dw_pcie_ep *ep) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct uniphier_pcie_ep_priv *priv = to_uniphier_pcie(pci); @@ -256,15 +256,14 @@ static int uniphier_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, } static int uniphier_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, - enum pci_epc_irq_type type, - u16 interrupt_num) + unsigned int type, u16 interrupt_num) { struct dw_pcie *pci = to_dw_pcie_from_ep(ep); switch (type) { - case PCI_EPC_IRQ_LEGACY: - return uniphier_pcie_ep_raise_legacy_irq(ep); - case PCI_EPC_IRQ_MSI: + case PCI_IRQ_INTX: + return uniphier_pcie_ep_raise_intx_irq(ep); + case PCI_IRQ_MSI: return uniphier_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); default: @@ -284,7 +283,7 @@ uniphier_pcie_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops uniphier_pcie_ep_ops = { - .ep_init = uniphier_pcie_ep_init, + .init = uniphier_pcie_ep_init, .raise_irq = uniphier_pcie_ep_raise_irq, .get_features = uniphier_pcie_get_features, }; @@ -412,8 +411,12 @@ static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = { .msi_capable = true, .msix_capable = false, .align = 1 << 16, - .bar_fixed_64bit = BIT(BAR_0) | BIT(BAR_2) | BIT(BAR_4), - .reserved_bar = BIT(BAR_4), + .bar[BAR_0] = { .only_64bit = true, }, + .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_2] = { .only_64bit = true, }, + .bar[BAR_3] = { .type = BAR_RESERVED, }, + .bar[BAR_4] = { .type = BAR_RESERVED, }, + .bar[BAR_5] = { .type = BAR_RESERVED, }, }, }; @@ -426,7 +429,12 @@ static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = { .msi_capable = true, .msix_capable = false, .align = 1 << 12, - .bar_fixed_64bit = BIT(BAR_0) | BIT(BAR_2) | BIT(BAR_4), + .bar[BAR_0] = { .only_64bit = true, }, + .bar[BAR_1] = { .type = BAR_RESERVED, }, + .bar[BAR_2] = { .only_64bit = true, }, + .bar[BAR_3] = { .type = BAR_RESERVED, }, + .bar[BAR_4] = { .only_64bit = true, }, + .bar[BAR_5] = { .type = BAR_RESERVED, }, }, }; diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c index 48c3eba817b4..5757ca3803c9 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier.c +++ b/drivers/pci/controller/dwc/pcie-uniphier.c @@ -67,7 +67,7 @@ struct uniphier_pcie { struct clk *clk; struct reset_control *rst; struct phy *phy; - struct irq_domain *legacy_irq_domain; + struct irq_domain *intx_irq_domain; }; #define to_uniphier_pcie(x) dev_get_drvdata((x)->dev) @@ -253,12 +253,12 @@ static void uniphier_pcie_irq_handler(struct irq_desc *desc) reg = FIELD_GET(PCL_RCV_INTX_ALL_STATUS, val); for_each_set_bit(bit, ®, PCI_NUM_INTX) - generic_handle_domain_irq(pcie->legacy_irq_domain, bit); + generic_handle_domain_irq(pcie->intx_irq_domain, bit); chained_irq_exit(chip, desc); } -static int uniphier_pcie_config_legacy_irq(struct dw_pcie_rp *pp) +static int uniphier_pcie_config_intx_irq(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct uniphier_pcie *pcie = to_uniphier_pcie(pci); @@ -279,9 +279,9 @@ static int uniphier_pcie_config_legacy_irq(struct dw_pcie_rp *pp) goto out_put_node; } - pcie->legacy_irq_domain = irq_domain_add_linear(np_intc, PCI_NUM_INTX, + pcie->intx_irq_domain = irq_domain_add_linear(np_intc, PCI_NUM_INTX, &uniphier_intx_domain_ops, pp); - if (!pcie->legacy_irq_domain) { + if (!pcie->intx_irq_domain) { dev_err(pci->dev, "Failed to get INTx domain\n"); ret = -ENODEV; goto out_put_node; @@ -301,7 +301,7 @@ static int uniphier_pcie_host_init(struct dw_pcie_rp *pp) struct uniphier_pcie *pcie = to_uniphier_pcie(pci); int ret; - ret = uniphier_pcie_config_legacy_irq(pp); + ret = uniphier_pcie_config_intx_irq(pp); if (ret) return ret; @@ -311,7 +311,7 @@ static int uniphier_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops uniphier_pcie_host_ops = { - .host_init = uniphier_pcie_host_init, + .init = uniphier_pcie_host_init, }; static int uniphier_pcie_host_enable(struct uniphier_pcie *pcie) diff --git a/drivers/pci/controller/dwc/pcie-visconti.c b/drivers/pci/controller/dwc/pcie-visconti.c index 71026fefa366..318c278e65c8 100644 --- a/drivers/pci/controller/dwc/pcie-visconti.c +++ b/drivers/pci/controller/dwc/pcie-visconti.c @@ -236,7 +236,7 @@ static int visconti_pcie_host_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_host_ops visconti_pcie_host_ops = { - .host_init = visconti_pcie_host_init, + .init = visconti_pcie_host_init, }; static int visconti_get_resources(struct platform_device *pdev, |