diff options
Diffstat (limited to 'drivers/pci/controller/dwc')
25 files changed, 1282 insertions, 326 deletions
| diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index ff6b6d9e18ec..349d4657393c 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -20,6 +20,7 @@ config PCIE_DW_HOST  	bool  	select PCIE_DW  	select IRQ_MSI_LIB +	select PCI_HOST_COMMON  config PCIE_DW_EP  	bool @@ -298,6 +299,7 @@ config PCIE_QCOM  	select CRC8  	select PCIE_QCOM_COMMON  	select PCI_HOST_COMMON +	select PCI_PWRCTRL_SLOT  	help  	  Say Y here to enable PCIe controller support on Qualcomm SoCs. The  	  PCIe controller uses the DesignWare core plus Qualcomm-specific @@ -422,6 +424,30 @@ config PCIE_SPEAR13XX  	help  	  Say Y here if you want PCIe support on SPEAr13XX SoCs. +config PCIE_STM32_HOST +	tristate "STMicroelectronics STM32MP25 PCIe Controller (host mode)" +	depends on ARCH_STM32 || COMPILE_TEST +	depends on PCI_MSI +	select PCIE_DW_HOST +	help +	  Enables Root Complex (RC) support for the DesignWare core based PCIe +	  controller found in STM32MP25 SoC. + +	  This driver can also be built as a module. If so, the module +	  will be called pcie-stm32. + +config PCIE_STM32_EP +	tristate "STMicroelectronics STM32MP25 PCIe Controller (endpoint mode)" +	depends on ARCH_STM32 || COMPILE_TEST +	depends on PCI_ENDPOINT +	select PCIE_DW_EP +	help +	  Enables Endpoint (EP) support for the DesignWare core based PCIe +	  controller found in STM32MP25 SoC. + +	  This driver can also be built as a module. If so, the module +	  will be called pcie-stm32-ep. +  config PCI_DRA7XX  	tristate diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 6919d27798d1..7ae28f3b0fb3 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -31,6 +31,8 @@ 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 +obj-$(CONFIG_PCIE_STM32_HOST) += pcie-stm32.o +obj-$(CONFIG_PCIE_STM32_EP) += pcie-stm32-ep.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 f97f5266d196..01cfd9aeb0b8 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -426,7 +426,6 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  static const struct pci_epc_features dra7xx_pcie_epc_features = {  	.linkup_notifier = true,  	.msi_capable = true, -	.msix_capable = false,  };  static const struct pci_epc_features* diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index 1f0e98d07109..0bb7d4f5d784 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -53,7 +53,6 @@  struct exynos_pcie {  	struct dw_pcie			pci; -	void __iomem			*elbi_base;  	struct clk_bulk_data		*clks;  	struct phy			*phy;  	struct regulator_bulk_data	supplies[2]; @@ -71,73 +70,78 @@ static u32 exynos_pcie_readl(void __iomem *base, u32 reg)  static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)  { +	struct dw_pcie *pci = &ep->pci;  	u32 val; -	val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC); +	val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_AWMISC);  	if (on)  		val |= PCIE_ELBI_SLV_DBI_ENABLE;  	else  		val &= ~PCIE_ELBI_SLV_DBI_ENABLE; -	exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC); +	exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_AWMISC);  }  static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)  { +	struct dw_pcie *pci = &ep->pci;  	u32 val; -	val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC); +	val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_ARMISC);  	if (on)  		val |= PCIE_ELBI_SLV_DBI_ENABLE;  	else  		val &= ~PCIE_ELBI_SLV_DBI_ENABLE; -	exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC); +	exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_ARMISC);  }  static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)  { +	struct dw_pcie *pci = &ep->pci;  	u32 val; -	val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET); +	val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET);  	val &= ~PCIE_CORE_RESET_ENABLE; -	exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET); -	exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET); -	exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET); +	exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET); +	exynos_pcie_writel(pci->elbi_base, 0, PCIE_STICKY_RESET); +	exynos_pcie_writel(pci->elbi_base, 0, PCIE_NONSTICKY_RESET);  }  static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)  { +	struct dw_pcie *pci = &ep->pci;  	u32 val; -	val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET); +	val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET);  	val |= PCIE_CORE_RESET_ENABLE; -	exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET); -	exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET); -	exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET); -	exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET); -	exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET); +	exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET); +	exynos_pcie_writel(pci->elbi_base, 1, PCIE_STICKY_RESET); +	exynos_pcie_writel(pci->elbi_base, 1, PCIE_NONSTICKY_RESET); +	exynos_pcie_writel(pci->elbi_base, 1, PCIE_APP_INIT_RESET); +	exynos_pcie_writel(pci->elbi_base, 0, PCIE_APP_INIT_RESET);  }  static int exynos_pcie_start_link(struct dw_pcie *pci)  { -	struct exynos_pcie *ep = to_exynos_pcie(pci);  	u32 val; -	val = exynos_pcie_readl(ep->elbi_base, PCIE_SW_WAKE); +	val = exynos_pcie_readl(pci->elbi_base, PCIE_SW_WAKE);  	val &= ~PCIE_BUS_EN; -	exynos_pcie_writel(ep->elbi_base, val, PCIE_SW_WAKE); +	exynos_pcie_writel(pci->elbi_base, val, PCIE_SW_WAKE);  	/* assert LTSSM enable */ -	exynos_pcie_writel(ep->elbi_base, PCIE_ELBI_LTSSM_ENABLE, +	exynos_pcie_writel(pci->elbi_base, PCIE_ELBI_LTSSM_ENABLE,  			  PCIE_APP_LTSSM_ENABLE);  	return 0;  }  static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)  { -	u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE); +	struct dw_pcie *pci = &ep->pci; -	exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE); +	u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_IRQ_PULSE); + +	exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_PULSE);  }  static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) @@ -150,12 +154,14 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)  static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)  { +	struct dw_pcie *pci = &ep->pci; +  	u32 val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |  		  IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; -	exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE); -	exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_LEVEL); -	exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_SPECIAL); +	exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_EN_PULSE); +	exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_LEVEL); +	exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_SPECIAL);  }  static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, @@ -211,8 +217,7 @@ static struct pci_ops exynos_pci_ops = {  static bool exynos_pcie_link_up(struct dw_pcie *pci)  { -	struct exynos_pcie *ep = to_exynos_pcie(pci); -	u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_RDLH_LINKUP); +	u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_RDLH_LINKUP);  	return val & PCIE_ELBI_XMLH_LINKUP;  } @@ -295,11 +300,6 @@ static int exynos_pcie_probe(struct platform_device *pdev)  	if (IS_ERR(ep->phy))  		return PTR_ERR(ep->phy); -	/* External Local Bus interface (ELBI) registers */ -	ep->elbi_base = devm_platform_ioremap_resource_byname(pdev, "elbi"); -	if (IS_ERR(ep->elbi_base)) -		return PTR_ERR(ep->elbi_base); -  	ret = devm_clk_bulk_get_all_enabled(dev, &ep->clks);  	if (ret < 0)  		return ret; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 80e48746bbaf..4668fc9648bf 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1387,9 +1387,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  }  static const struct pci_epc_features imx8m_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_4] = { .type = BAR_FIXED, .fixed_size = SZ_256, }, @@ -1398,9 +1396,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {  };  static const struct pci_epc_features imx8q_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, }, @@ -1745,6 +1741,10 @@ static int imx_pcie_probe(struct platform_device *pdev)  	pci->max_link_speed = 1;  	of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed); +	ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux"); +	if (ret < 0 && ret != -ENODEV) +		return dev_err_probe(dev, ret, "failed to enable Vaux supply\n"); +  	imx_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie");  	if (IS_ERR(imx_pcie->vpcie)) {  		if (PTR_ERR(imx_pcie->vpcie) != -ENODEV) diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 2b2632e513b5..eb00aa380722 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -960,7 +960,6 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  }  static const struct pci_epc_features ks_pcie_am654_epc_features = { -	.linkup_notifier = false,  	.msi_capable = true,  	.msix_capable = true,  	.bar[BAR_0] = { .type = BAR_RESERVED, }, @@ -1201,8 +1200,8 @@ static int ks_pcie_probe(struct platform_device *pdev)  	if (irq < 0)  		return irq; -	ret = request_irq(irq, ks_pcie_err_irq_handler, IRQF_SHARED, -			  "ks-pcie-error-irq", ks_pcie); +	ret = devm_request_irq(dev, irq, ks_pcie_err_irq_handler, IRQF_SHARED, +			       "ks-pcie-error-irq", ks_pcie);  	if (ret < 0) {  		dev_err(dev, "failed to request error IRQ %d\n",  			irq); @@ -1213,11 +1212,11 @@ static int ks_pcie_probe(struct platform_device *pdev)  	if (ret)  		num_lanes = 1; -	phy = devm_kzalloc(dev, sizeof(*phy) * num_lanes, GFP_KERNEL); +	phy = devm_kcalloc(dev, num_lanes, sizeof(*phy), GFP_KERNEL);  	if (!phy)  		return -ENOMEM; -	link = devm_kzalloc(dev, sizeof(*link) * num_lanes, GFP_KERNEL); +	link = devm_kcalloc(dev, num_lanes, sizeof(*link), GFP_KERNEL);  	if (!link)  		return -ENOMEM; diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index 643115f74092..345c281c74fe 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -352,6 +352,7 @@ static int al_pcie_probe(struct platform_device *pdev)  		return -ENOENT;  	}  	al_pcie->ecam_size = resource_size(ecam_res); +	pci->pp.native_ecam = true;  	controller_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,  						      "controller"); diff --git a/drivers/pci/controller/dwc/pcie-amd-mdb.c b/drivers/pci/controller/dwc/pcie-amd-mdb.c index 9f7251a16d32..3c6e837465bb 100644 --- a/drivers/pci/controller/dwc/pcie-amd-mdb.c +++ b/drivers/pci/controller/dwc/pcie-amd-mdb.c @@ -18,6 +18,7 @@  #include <linux/resource.h>  #include <linux/types.h> +#include "../../pci.h"  #include "pcie-designware.h"  #define AMD_MDB_TLP_IR_STATUS_MISC		0x4C0 @@ -56,6 +57,7 @@   * @slcr: MDB System Level Control and Status Register (SLCR) base   * @intx_domain: INTx IRQ domain pointer   * @mdb_domain: MDB IRQ domain pointer + * @perst_gpio: GPIO descriptor for PERST# signal handling   * @intx_irq: INTx IRQ interrupt number   */  struct amd_mdb_pcie { @@ -63,6 +65,7 @@ struct amd_mdb_pcie {  	void __iomem			*slcr;  	struct irq_domain		*intx_domain;  	struct irq_domain		*mdb_domain; +	struct gpio_desc		*perst_gpio;  	int				intx_irq;  }; @@ -284,7 +287,7 @@ static int amd_mdb_pcie_init_irq_domains(struct amd_mdb_pcie *pcie,  	struct device_node *pcie_intc_node;  	int err; -	pcie_intc_node = of_get_next_child(node, NULL); +	pcie_intc_node = of_get_child_by_name(node, "interrupt-controller");  	if (!pcie_intc_node) {  		dev_err(dev, "No PCIe Intc node found\n");  		return -ENODEV; @@ -402,6 +405,28 @@ static int amd_mdb_setup_irq(struct amd_mdb_pcie *pcie,  	return 0;  } +static int amd_mdb_parse_pcie_port(struct amd_mdb_pcie *pcie) +{ +	struct device *dev = pcie->pci.dev; +	struct device_node *pcie_port_node __maybe_unused; + +	/* +	 * This platform currently supports only one Root Port, so the loop +	 * will execute only once. +	 * TODO: Enhance the driver to handle multiple Root Ports in the future. +	 */ +	for_each_child_of_node_with_prefix(dev->of_node, pcie_port_node, "pcie") { +		pcie->perst_gpio = devm_fwnode_gpiod_get(dev, of_fwnode_handle(pcie_port_node), +							 "reset", GPIOD_OUT_HIGH, NULL); +		if (IS_ERR(pcie->perst_gpio)) +			return dev_err_probe(dev, PTR_ERR(pcie->perst_gpio), +					     "Failed to request reset GPIO\n"); +		return 0; +	} + +	return -ENODEV; +} +  static int amd_mdb_add_pcie_port(struct amd_mdb_pcie *pcie,  				 struct platform_device *pdev)  { @@ -426,6 +451,12 @@ static int amd_mdb_add_pcie_port(struct amd_mdb_pcie *pcie,  	pp->ops = &amd_mdb_pcie_host_ops; +	if (pcie->perst_gpio) { +		mdelay(PCIE_T_PVPERL_MS); +		gpiod_set_value_cansleep(pcie->perst_gpio, 0); +		mdelay(PCIE_RESET_CONFIG_WAIT_MS); +	} +  	err = dw_pcie_host_init(pp);  	if (err) {  		dev_err(dev, "Failed to initialize host, err=%d\n", err); @@ -444,6 +475,7 @@ static int amd_mdb_pcie_probe(struct platform_device *pdev)  	struct device *dev = &pdev->dev;  	struct amd_mdb_pcie *pcie;  	struct dw_pcie *pci; +	int ret;  	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);  	if (!pcie) @@ -454,6 +486,24 @@ static int amd_mdb_pcie_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, pcie); +	ret = amd_mdb_parse_pcie_port(pcie); +	/* +	 * If amd_mdb_parse_pcie_port returns -ENODEV, it indicates that the +	 * PCIe Bridge node was not found in the device tree. This is not +	 * considered a fatal error and will trigger a fallback where the +	 * reset GPIO is acquired directly from the PCIe Host Bridge node. +	 */ +	if (ret) { +		if (ret != -ENODEV) +			return ret; + +		pcie->perst_gpio = devm_gpiod_get_optional(dev, "reset", +							   GPIOD_OUT_HIGH); +		if (IS_ERR(pcie->perst_gpio)) +			return dev_err_probe(dev, PTR_ERR(pcie->perst_gpio), +					     "Failed to request reset GPIO\n"); +	} +  	return amd_mdb_add_pcie_port(pcie, pdev);  } diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 234c8cbcae3a..f4a136ee2daf 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -370,9 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  }  static const struct pci_epc_features artpec6_pcie_epc_features = { -	.linkup_notifier = false,  	.msi_capable = true, -	.msix_capable = false,  };  static const struct pci_epc_features * diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 0ae54a94809b..7f2112c2fb21 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -69,37 +69,10 @@ 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_id, next_cap_ptr; -	u16 reg; - -	if (!cap_ptr) -		return 0; - -	reg = dw_pcie_ep_readw_dbi(ep, func_no, cap_ptr); -	cap_id = (reg & 0x00ff); - -	if (cap_id > PCI_CAP_ID_MAX) -		return 0; - -	if (cap_id == cap) -		return cap_ptr; - -	next_cap_ptr = (reg & 0xff00) >> 8; -	return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); -} -  static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap)  { -	u8 next_cap_ptr; -	u16 reg; - -	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); +	return PCI_FIND_NEXT_CAP(dw_pcie_ep_read_cfg, PCI_CAPABILITY_LIST, +				 cap, ep, func_no);  }  /** diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 952f8594b501..20c9333bcb1c 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/align.h>  #include <linux/iopoll.h>  #include <linux/irqchip/chained_irq.h>  #include <linux/irqchip/irq-msi-lib.h> @@ -32,6 +33,8 @@ static struct pci_ops dw_child_pcie_ops;  				     MSI_FLAG_PCI_MSIX			| \  				     MSI_GENERIC_FLAGS_MASK) +#define IS_256MB_ALIGNED(x) IS_ALIGNED(x, SZ_256M) +  static const struct msi_parent_ops dw_pcie_msi_parent_ops = {  	.required_flags		= DW_PCIE_MSI_FLAGS_REQUIRED,  	.supported_flags	= DW_PCIE_MSI_FLAGS_SUPPORTED, @@ -413,6 +416,95 @@ static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp)  	}  } +static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct dw_pcie_ob_atu_cfg atu = {0}; +	resource_size_t bus_range_max; +	struct resource_entry *bus; +	int ret; + +	bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS); + +	/* +	 * Root bus under the host bridge doesn't require any iATU configuration +	 * as DBI region will be used to access root bus config space. +	 * Immediate bus under Root Bus, needs type 0 iATU configuration and +	 * remaining buses need type 1 iATU configuration. +	 */ +	atu.index = 0; +	atu.type = PCIE_ATU_TYPE_CFG0; +	atu.parent_bus_addr = pp->cfg0_base + SZ_1M; +	/* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */ +	atu.size = SZ_1M; +	atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE; +	ret = dw_pcie_prog_outbound_atu(pci, &atu); +	if (ret) +		return ret; + +	bus_range_max = resource_size(bus->res); + +	if (bus_range_max < 2) +		return 0; + +	/* Configure remaining buses in type 1 iATU configuration */ +	atu.index = 1; +	atu.type = PCIE_ATU_TYPE_CFG1; +	atu.parent_bus_addr = pp->cfg0_base + SZ_2M; +	atu.size = (SZ_1M * bus_range_max) - SZ_2M; +	atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE; + +	return dw_pcie_prog_outbound_atu(pci, &atu); +} + +static int dw_pcie_create_ecam_window(struct dw_pcie_rp *pp, struct resource *res) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct device *dev = pci->dev; +	struct resource_entry *bus; + +	bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS); +	if (!bus) +		return -ENODEV; + +	pp->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops); +	if (IS_ERR(pp->cfg)) +		return PTR_ERR(pp->cfg); + +	pci->dbi_base = pp->cfg->win; +	pci->dbi_phys_addr = res->start; + +	return 0; +} + +static bool dw_pcie_ecam_enabled(struct dw_pcie_rp *pp, struct resource *config_res) +{ +	struct resource *bus_range; +	u64 nr_buses; + +	/* Vendor glue drivers may implement their own ECAM mechanism */ +	if (pp->native_ecam) +		return false; + +	/* +	 * PCIe spec r6.0, sec 7.2.2 mandates the base address used for ECAM to +	 * be aligned on a 2^(n+20) byte boundary, where n is the number of bits +	 * used for representing 'bus' in BDF. Since the DWC cores always use 8 +	 * bits for representing 'bus', the base address has to be aligned to +	 * 2^28 byte boundary, which is 256 MiB. +	 */ +	if (!IS_256MB_ALIGNED(config_res->start)) +		return false; + +	bus_range = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS)->res; +	if (!bus_range) +		return false; + +	nr_buses = resource_size(config_res) >> PCIE_ECAM_BUS_SHIFT; + +	return nr_buses >= resource_size(bus_range); +} +  static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)  {  	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -422,10 +514,6 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)  	struct resource *res;  	int ret; -	ret = dw_pcie_get_resources(pci); -	if (ret) -		return ret; -  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");  	if (!res) {  		dev_err(dev, "Missing \"config\" reg space\n"); @@ -435,9 +523,32 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)  	pp->cfg0_size = resource_size(res);  	pp->cfg0_base = res->start; -	pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res); -	if (IS_ERR(pp->va_cfg0_base)) -		return PTR_ERR(pp->va_cfg0_base); +	pp->ecam_enabled = dw_pcie_ecam_enabled(pp, res); +	if (pp->ecam_enabled) { +		ret = dw_pcie_create_ecam_window(pp, res); +		if (ret) +			return ret; + +		pp->bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops; +		pp->bridge->sysdata = pp->cfg; +		pp->cfg->priv = pp; +	} else { +		pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res); +		if (IS_ERR(pp->va_cfg0_base)) +			return PTR_ERR(pp->va_cfg0_base); + +		/* Set default bus ops */ +		pp->bridge->ops = &dw_pcie_ops; +		pp->bridge->child_ops = &dw_child_pcie_ops; +		pp->bridge->sysdata = pp; +	} + +	ret = dw_pcie_get_resources(pci); +	if (ret) { +		if (pp->cfg) +			pci_ecam_free(pp->cfg); +		return ret; +	}  	/* Get the I/O range from DT */  	win = resource_list_first_type(&pp->bridge->windows, IORESOURCE_IO); @@ -476,14 +587,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)  	if (ret)  		return ret; -	/* Set default bus ops */ -	bridge->ops = &dw_pcie_ops; -	bridge->child_ops = &dw_child_pcie_ops; -  	if (pp->ops->init) {  		ret = pp->ops->init(pp);  		if (ret) -			return ret; +			goto err_free_ecam;  	}  	if (pci_msi_enabled()) { @@ -525,6 +632,14 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)  	if (ret)  		goto err_free_msi; +	if (pp->ecam_enabled) { +		ret = dw_pcie_config_ecam_iatu(pp); +		if (ret) { +			dev_err(dev, "Failed to configure iATU in ECAM mode\n"); +			goto err_free_msi; +		} +	} +  	/*  	 * Allocate the resource for MSG TLP before programming the iATU  	 * outbound window in dw_pcie_setup_rc(). Since the allocation depends @@ -560,8 +675,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)  		/* Ignore errors, the link may come up later */  		dw_pcie_wait_for_link(pci); -	bridge->sysdata = pp; -  	ret = pci_host_probe(bridge);  	if (ret)  		goto err_stop_link; @@ -587,6 +700,10 @@ err_deinit_host:  	if (pp->ops->deinit)  		pp->ops->deinit(pp); +err_free_ecam: +	if (pp->cfg) +		pci_ecam_free(pp->cfg); +  	return ret;  }  EXPORT_SYMBOL_GPL(dw_pcie_host_init); @@ -609,6 +726,9 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)  	if (pp->ops->deinit)  		pp->ops->deinit(pp); + +	if (pp->cfg) +		pci_ecam_free(pp->cfg);  }  EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 771b9d9be077..12f41886c65d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -61,7 +61,6 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  }  static const struct pci_epc_features dw_plat_pcie_epc_features = { -	.linkup_notifier = false,  	.msi_capable = true,  	.msix_capable = true,  }; diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 89aad5a08928..c644216995f6 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -167,6 +167,14 @@ int dw_pcie_get_resources(struct dw_pcie *pci)  		}  	} +	/* ELBI is an optional resource */ +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); +	if (res) { +		pci->elbi_base = devm_ioremap_resource(pci->dev, res); +		if (IS_ERR(pci->elbi_base)) +			return PTR_ERR(pci->elbi_base); +	} +  	/* LLDD is supposed to manually switch the clocks and resets state */  	if (dw_pcie_cap_is(pci, REQ_RES)) {  		ret = dw_pcie_get_clocks(pci); @@ -213,83 +221,16 @@ void dw_pcie_version_detect(struct dw_pcie *pci)  		pci->type = ver;  } -/* - * These interfaces resemble the pci_find_*capability() interfaces, but these - * are for configuring host controllers, which are bridges *to* PCI devices but - * are not PCI devices themselves. - */ -static u8 __dw_pcie_find_next_cap(struct dw_pcie *pci, u8 cap_ptr, -				  u8 cap) -{ -	u8 cap_id, next_cap_ptr; -	u16 reg; - -	if (!cap_ptr) -		return 0; - -	reg = dw_pcie_readw_dbi(pci, cap_ptr); -	cap_id = (reg & 0x00ff); - -	if (cap_id > PCI_CAP_ID_MAX) -		return 0; - -	if (cap_id == cap) -		return cap_ptr; - -	next_cap_ptr = (reg & 0xff00) >> 8; -	return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); -} -  u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap)  { -	u8 next_cap_ptr; -	u16 reg; - -	reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST); -	next_cap_ptr = (reg & 0x00ff); - -	return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); +	return PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap, +				 pci);  }  EXPORT_SYMBOL_GPL(dw_pcie_find_capability); -static u16 dw_pcie_find_next_ext_capability(struct dw_pcie *pci, u16 start, -					    u8 cap) -{ -	u32 header; -	int ttl; -	int pos = PCI_CFG_SPACE_SIZE; - -	/* minimum 8 bytes per capability */ -	ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; - -	if (start) -		pos = start; - -	header = dw_pcie_readl_dbi(pci, pos); -	/* -	 * If we have no capabilities, this is indicated by cap ID, -	 * cap version and next pointer all being 0. -	 */ -	if (header == 0) -		return 0; - -	while (ttl-- > 0) { -		if (PCI_EXT_CAP_ID(header) == cap && pos != start) -			return pos; - -		pos = PCI_EXT_CAP_NEXT(header); -		if (pos < PCI_CFG_SPACE_SIZE) -			break; - -		header = dw_pcie_readl_dbi(pci, pos); -	} - -	return 0; -} -  u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap)  { -	return dw_pcie_find_next_ext_capability(pci, 0, cap); +	return PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, pci);  }  EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability); @@ -302,8 +243,8 @@ static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id,  	if (vendor_id != dw_pcie_readw_dbi(pci, PCI_VENDOR_ID))  		return 0; -	while ((vsec = dw_pcie_find_next_ext_capability(pci, vsec, -						       PCI_EXT_CAP_ID_VNDR))) { +	while ((vsec = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, vsec, +					     PCI_EXT_CAP_ID_VNDR, pci))) {  		header = dw_pcie_readl_dbi(pci, vsec + PCI_VNDR_HEADER);  		if (PCI_VNDR_HEADER_ID(header) == vsec_id)  			return vsec; @@ -567,7 +508,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,  		val = dw_pcie_enable_ecrc(val);  	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val); -	val = PCIE_ATU_ENABLE; +	val = PCIE_ATU_ENABLE | atu->ctrl2;  	if (atu->type == PCIE_ATU_TYPE_MSG) {  		/* The data-less messages only for now */  		val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code; @@ -841,6 +782,9 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)  	case 8:  		plc |= PORT_LINK_MODE_8_LANES;  		break; +	case 16: +		plc |= PORT_LINK_MODE_16_LANES; +		break;  	default:  		dev_err(pci->dev, "num-lanes %u: invalid value\n", num_lanes);  		return; @@ -1045,9 +989,7 @@ static int dw_pcie_edma_irq_verify(struct dw_pcie *pci)  	char name[15];  	int ret; -	if (pci->edma.nr_irqs == 1) -		return 0; -	else if (pci->edma.nr_irqs > 1) +	if (pci->edma.nr_irqs > 1)  		return pci->edma.nr_irqs != ch_cnt ? -EINVAL : 0;  	ret = platform_get_irq_byname_optional(pdev, "dma"); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 00f52d472dcd..e995f692a1ec 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -20,6 +20,7 @@  #include <linux/irq.h>  #include <linux/msi.h>  #include <linux/pci.h> +#include <linux/pci-ecam.h>  #include <linux/reset.h>  #include <linux/pci-epc.h> @@ -90,6 +91,7 @@  #define PORT_LINK_MODE_2_LANES		PORT_LINK_MODE(0x3)  #define PORT_LINK_MODE_4_LANES		PORT_LINK_MODE(0x7)  #define PORT_LINK_MODE_8_LANES		PORT_LINK_MODE(0xf) +#define PORT_LINK_MODE_16_LANES		PORT_LINK_MODE(0x1f)  #define PCIE_PORT_LANE_SKEW		0x714  #define PORT_LANE_SKEW_INSERT_MASK	GENMASK(23, 0) @@ -123,7 +125,6 @@  #define GEN3_RELATED_OFF_GEN3_EQ_DISABLE	BIT(16)  #define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT	24  #define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK	GENMASK(25, 24) -#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT	0x1  #define GEN3_EQ_CONTROL_OFF			0x8A8  #define GEN3_EQ_CONTROL_OFF_FB_MODE		GENMASK(3, 0) @@ -134,8 +135,8 @@  #define GEN3_EQ_FB_MODE_DIR_CHANGE_OFF		0x8AC  #define GEN3_EQ_FMDC_T_MIN_PHASE23		GENMASK(4, 0)  #define GEN3_EQ_FMDC_N_EVALS			GENMASK(9, 5) -#define GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA	GENMASK(13, 10) -#define GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA	GENMASK(17, 14) +#define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA	GENMASK(13, 10) +#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA	GENMASK(17, 14)  #define PCIE_PORT_MULTI_LANE_CTRL	0x8C0  #define PORT_MLTI_UPCFG_SUPPORT		BIT(7) @@ -169,6 +170,7 @@  #define PCIE_ATU_REGION_CTRL2		0x004  #define PCIE_ATU_ENABLE			BIT(31)  #define PCIE_ATU_BAR_MODE_ENABLE	BIT(30) +#define PCIE_ATU_CFG_SHIFT_MODE_ENABLE	BIT(28)  #define PCIE_ATU_INHIBIT_PAYLOAD	BIT(22)  #define PCIE_ATU_FUNC_NUM_MATCH_EN      BIT(19)  #define PCIE_ATU_LOWER_BASE		0x008 @@ -387,6 +389,7 @@ struct dw_pcie_ob_atu_cfg {  	u8 func_no;  	u8 code;  	u8 routing; +	u32 ctrl2;  	u64 parent_bus_addr;  	u64 pci_addr;  	u64 size; @@ -425,6 +428,9 @@ struct dw_pcie_rp {  	struct resource		*msg_res;  	bool			use_linkup_irq;  	struct pci_eq_presets	presets; +	struct pci_config_window *cfg; +	bool			ecam_enabled; +	bool			native_ecam;  };  struct dw_pcie_ep_ops { @@ -492,6 +498,7 @@ struct dw_pcie {  	resource_size_t		dbi_phys_addr;  	void __iomem		*dbi_base2;  	void __iomem		*atu_base; +	void __iomem		*elbi_base;  	resource_size_t		atu_phys_addr;  	size_t			atu_size;  	resource_size_t		parent_bus_offset; @@ -609,6 +616,27 @@ 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 int dw_pcie_read_cfg_byte(struct dw_pcie *pci, int where, +					u8 *val) +{ +	*val = dw_pcie_readb_dbi(pci, where); +	return PCIBIOS_SUCCESSFUL; +} + +static inline int dw_pcie_read_cfg_word(struct dw_pcie *pci, int where, +					u16 *val) +{ +	*val = dw_pcie_readw_dbi(pci, where); +	return PCIBIOS_SUCCESSFUL; +} + +static inline int dw_pcie_read_cfg_dword(struct dw_pcie *pci, int where, +					 u32 *val) +{ +	*val = dw_pcie_readl_dbi(pci, where); +	return PCIBIOS_SUCCESSFUL; +} +  static inline unsigned int dw_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep,  						     u8 func_no)  { @@ -674,6 +702,27 @@ static inline u8 dw_pcie_ep_readb_dbi(struct dw_pcie_ep *ep, u8 func_no,  	return dw_pcie_ep_read_dbi(ep, func_no, reg, 0x1);  } +static inline int dw_pcie_ep_read_cfg_byte(struct dw_pcie_ep *ep, u8 func_no, +					   int where, u8 *val) +{ +	*val = dw_pcie_ep_readb_dbi(ep, func_no, where); +	return PCIBIOS_SUCCESSFUL; +} + +static inline int dw_pcie_ep_read_cfg_word(struct dw_pcie_ep *ep, u8 func_no, +					   int where, u16 *val) +{ +	*val = dw_pcie_ep_readw_dbi(ep, func_no, where); +	return PCIBIOS_SUCCESSFUL; +} + +static inline int dw_pcie_ep_read_cfg_dword(struct dw_pcie_ep *ep, u8 func_no, +					    int where, u32 *val) +{ +	*val = dw_pcie_ep_readl_dbi(ep, func_no, where); +	return PCIBIOS_SUCCESSFUL; +} +  static inline unsigned int dw_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep,  						      u8 func_no)  { diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 5d7f6f544942..3e2752c7dd09 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -331,7 +331,6 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {  	.linkup_notifier = true,  	.msi_capable = true,  	.msix_capable = true, -	.intx_capable = false,  	.align = SZ_64K,  	.bar[BAR_0] = { .type = BAR_RESIZABLE, },  	.bar[BAR_1] = { .type = BAR_RESIZABLE, }, @@ -352,7 +351,6 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {  	.linkup_notifier = true,  	.msi_capable = true,  	.msix_capable = true, -	.intx_capable = false,  	.align = SZ_64K,  	.bar[BAR_0] = { .type = BAR_RESIZABLE, },  	.bar[BAR_1] = { .type = BAR_RESIZABLE, }, diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 67dd3337b447..60e74ac782af 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -309,7 +309,6 @@ static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  }  static const struct pci_epc_features keembay_pcie_epc_features = { -	.linkup_notifier	= false,  	.msi_capable		= true,  	.msix_capable		= true,  	.bar[BAR_0]		= { .only_64bit = true, }, diff --git a/drivers/pci/controller/dwc/pcie-qcom-common.c b/drivers/pci/controller/dwc/pcie-qcom-common.c index 3aad19b56da8..01c5387e53bf 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-common.c +++ b/drivers/pci/controller/dwc/pcie-qcom-common.c @@ -8,9 +8,11 @@  #include "pcie-designware.h"  #include "pcie-qcom-common.h" -void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci) +void qcom_pcie_common_set_equalization(struct dw_pcie *pci)  { +	struct device *dev = pci->dev;  	u32 reg; +	u16 speed;  	/*  	 * GEN3_RELATED_OFF register is repurposed to apply equalization @@ -19,32 +21,40 @@ void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci)  	 * determines the data rate for which these equalization settings are  	 * applied.  	 */ -	reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); -	reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; -	reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; -	reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, -			  GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT); -	dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg); -	reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF); -	reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 | -		GEN3_EQ_FMDC_N_EVALS | -		GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA | -		GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA); -	reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) | -		FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) | -		FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA, 0x5) | -		FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA, 0x5); -	dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg); +	for (speed = PCIE_SPEED_8_0GT; speed <= pcie_link_speed[pci->max_link_speed]; speed++) { +		if (speed > PCIE_SPEED_32_0GT) { +			dev_warn(dev, "Skipped equalization settings for unsupported data rate\n"); +			break; +		} -	reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); -	reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE | -		GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE | -		GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL | -		GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC); -	dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg); +		reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); +		reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; +		reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; +		reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, +			  speed - PCIE_SPEED_8_0GT); +		dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg); + +		reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF); +		reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 | +			GEN3_EQ_FMDC_N_EVALS | +			GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA | +			GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA); +		reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) | +			FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) | +			FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, 0x5) | +			FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, 0x5); +		dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg); + +		reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); +		reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE | +			GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE | +			GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL | +			GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC); +		dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg); +	}  } -EXPORT_SYMBOL_GPL(qcom_pcie_common_set_16gt_equalization); +EXPORT_SYMBOL_GPL(qcom_pcie_common_set_equalization);  void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci)  { diff --git a/drivers/pci/controller/dwc/pcie-qcom-common.h b/drivers/pci/controller/dwc/pcie-qcom-common.h index 7d88d29e4766..7f5ca2fd9a72 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-common.h +++ b/drivers/pci/controller/dwc/pcie-qcom-common.h @@ -8,7 +8,7 @@  struct dw_pcie; -void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci); +void qcom_pcie_common_set_equalization(struct dw_pcie *pci);  void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci);  #endif diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index bf7c6ac0f3e3..f1bc0ac81a92 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -179,7 +179,6 @@ struct qcom_pcie_ep_cfg {   * struct qcom_pcie_ep - Qualcomm PCIe Endpoint Controller   * @pci: Designware PCIe controller struct   * @parf: Qualcomm PCIe specific PARF register base - * @elbi: Designware PCIe specific ELBI register base   * @mmio: MMIO register base   * @perst_map: PERST regmap   * @mmio_res: MMIO region resource @@ -202,7 +201,6 @@ struct qcom_pcie_ep {  	struct dw_pcie pci;  	void __iomem *parf; -	void __iomem *elbi;  	void __iomem *mmio;  	struct regmap *perst_map;  	struct resource *mmio_res; @@ -267,10 +265,9 @@ static void qcom_pcie_ep_configure_tcsr(struct qcom_pcie_ep *pcie_ep)  static bool qcom_pcie_dw_link_up(struct dw_pcie *pci)  { -	struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);  	u32 reg; -	reg = readl_relaxed(pcie_ep->elbi + ELBI_SYS_STTS); +	reg = readl_relaxed(pci->elbi_base + ELBI_SYS_STTS);  	return reg & XMLH_LINK_UP;  } @@ -294,16 +291,15 @@ static void qcom_pcie_dw_stop_link(struct dw_pcie *pci)  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); +	writel(1, pci->elbi_base + 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); +	writel(0, pci->elbi_base + ELBI_CS2_ENABLE);  }  static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep) @@ -511,10 +507,10 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)  		goto err_disable_resources;  	} -	if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) { -		qcom_pcie_common_set_16gt_equalization(pci); +	qcom_pcie_common_set_equalization(pci); + +	if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT)  		qcom_pcie_common_set_16gt_lane_margining(pci); -	}  	/*  	 * The physical address of the MMIO region which is exposed as the BAR @@ -583,11 +579,6 @@ static int qcom_pcie_ep_get_io_resources(struct platform_device *pdev,  		return PTR_ERR(pci->dbi_base);  	pci->dbi_base2 = pci->dbi_base; -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); -	pcie_ep->elbi = devm_pci_remap_cfg_resource(dev, res); -	if (IS_ERR(pcie_ep->elbi)) -		return PTR_ERR(pcie_ep->elbi); -  	pcie_ep->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,  							 "mmio");  	if (!pcie_ep->mmio_res) { @@ -831,7 +822,6 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)  static const struct pci_epc_features qcom_pcie_epc_features = {  	.linkup_notifier = true,  	.msi_capable = true, -	.msix_capable = false,  	.align = SZ_4K,  	.bar[BAR_0] = { .only_64bit = true, },  	.bar[BAR_1] = { .type = BAR_RESERVED, }, @@ -874,7 +864,6 @@ 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;  	pcie_ep->cfg = of_device_get_match_data(dev);  	if (pcie_ep->cfg && pcie_ep->cfg->hdma_support) { diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 294babe1816e..805edbbfe7eb 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -55,6 +55,7 @@  #define PARF_AXI_MSTR_WR_ADDR_HALT_V2		0x1a8  #define PARF_Q2A_FLUSH				0x1ac  #define PARF_LTSSM				0x1b0 +#define PARF_SLV_DBI_ELBI			0x1b4  #define PARF_INT_ALL_STATUS			0x224  #define PARF_INT_ALL_CLEAR			0x228  #define PARF_INT_ALL_MASK			0x22c @@ -64,6 +65,16 @@  #define PARF_DBI_BASE_ADDR_V2_HI		0x354  #define PARF_SLV_ADDR_SPACE_SIZE_V2		0x358  #define PARF_SLV_ADDR_SPACE_SIZE_V2_HI		0x35c +#define PARF_BLOCK_SLV_AXI_WR_BASE		0x360 +#define PARF_BLOCK_SLV_AXI_WR_BASE_HI		0x364 +#define PARF_BLOCK_SLV_AXI_WR_LIMIT		0x368 +#define PARF_BLOCK_SLV_AXI_WR_LIMIT_HI		0x36c +#define PARF_BLOCK_SLV_AXI_RD_BASE		0x370 +#define PARF_BLOCK_SLV_AXI_RD_BASE_HI		0x374 +#define PARF_BLOCK_SLV_AXI_RD_LIMIT		0x378 +#define PARF_BLOCK_SLV_AXI_RD_LIMIT_HI		0x37c +#define PARF_ECAM_BASE				0x380 +#define PARF_ECAM_BASE_HI			0x384  #define PARF_NO_SNOOP_OVERRIDE			0x3d4  #define PARF_ATU_BASE_ADDR			0x634  #define PARF_ATU_BASE_ADDR_HI			0x638 @@ -87,6 +98,7 @@  /* PARF_SYS_CTRL register fields */  #define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN	BIT(29) +#define PCIE_ECAM_BLOCKER_EN			BIT(26)  #define MST_WAKEUP_EN				BIT(13)  #define SLV_WAKEUP_EN				BIT(12)  #define MSTR_ACLK_CGC_DIS			BIT(10) @@ -134,6 +146,9 @@  /* PARF_LTSSM register fields */  #define LTSSM_EN				BIT(8) +/* PARF_SLV_DBI_ELBI */ +#define SLV_DBI_ELBI_ADDR_BASE			GENMASK(11, 0) +  /* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */  #define PARF_INT_ALL_LINK_UP			BIT(13)  #define PARF_INT_MSI_DEV_0_7			GENMASK(30, 23) @@ -247,7 +262,6 @@ 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); @@ -276,11 +290,8 @@ struct qcom_pcie_port {  struct qcom_pcie {  	struct dw_pcie *pci;  	void __iomem *parf;			/* DT parf */ -	void __iomem *elbi;			/* DT elbi */  	void __iomem *mhi;  	union qcom_pcie_resources res; -	struct phy *phy; -	struct gpio_desc *reset;  	struct icc_path *icc_mem;  	struct icc_path *icc_cpu;  	const struct qcom_pcie_cfg *cfg; @@ -297,11 +308,8 @@ static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert)  	struct qcom_pcie_port *port;  	int val = assert ? 1 : 0; -	if (list_empty(&pcie->ports)) -		gpiod_set_value_cansleep(pcie->reset, val); -	else -		list_for_each_entry(port, &pcie->ports, list) -			gpiod_set_value_cansleep(port->reset, val); +	list_for_each_entry(port, &pcie->ports, list) +		gpiod_set_value_cansleep(port->reset, val);  	usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);  } @@ -318,14 +326,55 @@ static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)  	qcom_perst_assert(pcie, false);  } +static void qcom_pci_config_ecam(struct dw_pcie_rp *pp) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct qcom_pcie *pcie = to_qcom_pcie(pci); +	u64 addr, addr_end; +	u32 val; + +	writel_relaxed(lower_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE); +	writel_relaxed(upper_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE_HI); + +	/* +	 * The only device on the root bus is a single Root Port. If we try to +	 * access any devices other than Device/Function 00.0 on Bus 0, the TLP +	 * will go outside of the controller to the PCI bus. But with CFG Shift +	 * Feature (ECAM) enabled in iATU, there is no guarantee that the +	 * response is going to be all F's. Hence, to make sure that the +	 * requester gets all F's response for accesses other than the Root +	 * Port, configure iATU to block the transactions starting from +	 * function 1 of the root bus to the end of the root bus (i.e., from +	 * dbi_base + 4KB to dbi_base + 1MB). +	 */ +	addr = pci->dbi_phys_addr + SZ_4K; +	writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE); +	writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE_HI); + +	writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE); +	writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE_HI); + +	addr_end = pci->dbi_phys_addr + SZ_1M - 1; + +	writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT); +	writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT_HI); + +	writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT); +	writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT_HI); + +	val = readl_relaxed(pcie->parf + PARF_SYS_CTRL); +	val |= PCIE_ECAM_BLOCKER_EN; +	writel_relaxed(val, pcie->parf + PARF_SYS_CTRL); +} +  static int qcom_pcie_start_link(struct dw_pcie *pci)  {  	struct qcom_pcie *pcie = to_qcom_pcie(pci); -	if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) { -		qcom_pcie_common_set_16gt_equalization(pci); +	qcom_pcie_common_set_equalization(pci); + +	if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT)  		qcom_pcie_common_set_16gt_lane_margining(pci); -	}  	/* Enable Link Training state machine */  	if (pcie->cfg->ops->ltssm_enable) @@ -414,12 +463,17 @@ static void qcom_pcie_configure_dbi_atu_base(struct qcom_pcie *pcie)  static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie)  { +	struct dw_pcie *pci = pcie->pci;  	u32 val; +	if (!pci->elbi_base) { +		dev_err(pci->dev, "ELBI is not present\n"); +		return; +	}  	/* enable link training */ -	val = readl(pcie->elbi + ELBI_SYS_CTRL); +	val = readl(pci->elbi_base + ELBI_SYS_CTRL);  	val |= ELBI_SYS_CTRL_LT_ENABLE; -	writel(val, pcie->elbi + ELBI_SYS_CTRL); +	writel(val, pci->elbi_base + ELBI_SYS_CTRL);  }  static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) @@ -1040,25 +1094,6 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)  	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; @@ -1253,63 +1288,39 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci)  	return val & PCI_EXP_LNKSTA_DLLLA;  } -static void qcom_pcie_phy_exit(struct qcom_pcie *pcie) -{ -	struct qcom_pcie_port *port; - -	if (list_empty(&pcie->ports)) -		phy_exit(pcie->phy); -	else -		list_for_each_entry(port, &pcie->ports, list) -			phy_exit(port->phy); -} -  static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie)  {  	struct qcom_pcie_port *port; -	if (list_empty(&pcie->ports)) { -		phy_power_off(pcie->phy); -	} else { -		list_for_each_entry(port, &pcie->ports, list) -			phy_power_off(port->phy); -	} +	list_for_each_entry(port, &pcie->ports, list) +		phy_power_off(port->phy);  }  static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie)  {  	struct qcom_pcie_port *port; -	int ret = 0; +	int ret; -	if (list_empty(&pcie->ports)) { -		ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); +	list_for_each_entry(port, &pcie->ports, list) { +		ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);  		if (ret)  			return ret; -		ret = phy_power_on(pcie->phy); -		if (ret) +		ret = phy_power_on(port->phy); +		if (ret) { +			qcom_pcie_phy_power_off(pcie);  			return ret; -	} else { -		list_for_each_entry(port, &pcie->ports, list) { -			ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); -			if (ret) -				return ret; - -			ret = phy_power_on(port->phy); -			if (ret) { -				qcom_pcie_phy_power_off(pcie); -				return ret; -			}  		}  	} -	return ret; +	return 0;  }  static int qcom_pcie_host_init(struct dw_pcie_rp *pp)  {  	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);  	struct qcom_pcie *pcie = to_qcom_pcie(pci); +	u16 offset;  	int ret;  	qcom_ep_reset_assert(pcie); @@ -1318,6 +1329,17 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)  	if (ret)  		return ret; +	if (pp->ecam_enabled) { +		/* +		 * Override ELBI when ECAM is enabled, as when ECAM is enabled, +		 * ELBI moves under the 'config' space. +		 */ +		offset = FIELD_GET(SLV_DBI_ELBI_ADDR_BASE, readl(pcie->parf + PARF_SLV_DBI_ELBI)); +		pci->elbi_base = pci->dbi_base + offset; + +		qcom_pci_config_ecam(pp); +	} +  	ret = qcom_pcie_phy_power_on(pcie);  	if (ret)  		goto err_deinit; @@ -1358,19 +1380,9 @@ 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 = {  	.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 */ @@ -1432,7 +1444,6 @@ 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, @@ -1443,7 +1454,6 @@ static const struct qcom_pcie_ops ops_1_21_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,  }; @@ -1740,6 +1750,8 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)  	int ret = -ENOENT;  	for_each_available_child_of_node_scoped(dev->of_node, of_port) { +		if (!of_node_is_type(of_port, "pci")) +			continue;  		ret = qcom_pcie_parse_port(pcie, of_port);  		if (ret)  			goto err_port_del; @@ -1748,8 +1760,10 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)  	return ret;  err_port_del: -	list_for_each_entry_safe(port, tmp, &pcie->ports, list) +	list_for_each_entry_safe(port, tmp, &pcie->ports, list) { +		phy_exit(port->phy);  		list_del(&port->list); +	}  	return ret;  } @@ -1757,20 +1771,32 @@ err_port_del:  static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)  {  	struct device *dev = pcie->pci->dev; +	struct qcom_pcie_port *port; +	struct gpio_desc *reset; +	struct phy *phy;  	int ret; -	pcie->phy = devm_phy_optional_get(dev, "pciephy"); -	if (IS_ERR(pcie->phy)) -		return PTR_ERR(pcie->phy); +	phy = devm_phy_optional_get(dev, "pciephy"); +	if (IS_ERR(phy)) +		return PTR_ERR(phy); -	pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); -	if (IS_ERR(pcie->reset)) -		return PTR_ERR(pcie->reset); +	reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); +	if (IS_ERR(reset)) +		return PTR_ERR(reset); -	ret = phy_init(pcie->phy); +	ret = phy_init(phy);  	if (ret)  		return ret; +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); +	if (!port) +		return -ENOMEM; + +	port->reset = reset; +	port->phy = phy; +	INIT_LIST_HEAD(&port->list); +	list_add_tail(&port->list, &pcie->ports); +  	return 0;  } @@ -1861,12 +1887,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_pm_runtime_put;  	} -	pcie->elbi = devm_platform_ioremap_resource_byname(pdev, "elbi"); -	if (IS_ERR(pcie->elbi)) { -		ret = PTR_ERR(pcie->elbi); -		goto err_pm_runtime_put; -	} -  	/* MHI region is optional */  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mhi");  	if (res) { @@ -1984,9 +2004,10 @@ static int qcom_pcie_probe(struct platform_device *pdev)  err_host_deinit:  	dw_pcie_host_deinit(pp);  err_phy_exit: -	qcom_pcie_phy_exit(pcie); -	list_for_each_entry_safe(port, tmp, &pcie->ports, list) +	list_for_each_entry_safe(port, tmp, &pcie->ports, list) { +		phy_exit(port->phy);  		list_del(&port->list); +	}  err_pm_runtime_put:  	pm_runtime_put(dev);  	pm_runtime_disable(dev); diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 18055807a4f5..80778917d2dd 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -182,8 +182,17 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)  		return ret;  	} -	if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) +	if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {  		reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); +		/* +		 * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr. +		 * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B) +		 * indicates that for peripherals in HSC domain, after +		 * reset has been asserted by writing a matching reset bit +		 * into register SRCR, it is mandatory to wait 1ms. +		 */ +		fsleep(1000); +	}  	val = readl(rcar->base + PCIEMSR0);  	if (rcar->drvdata->mode == DW_PCIE_RC_TYPE) { @@ -204,6 +213,19 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)  	if (ret)  		goto err_unprepare; +	/* +	 * Assure the reset is latched and the core is ready for DBI access. +	 * On R-Car V4H, the PCIe reset is asynchronous and does not take +	 * effect immediately, but needs a short time to complete. In case +	 * DBI access happens in that short time, that access generates an +	 * SError. To make sure that condition can never happen, read back the +	 * state of the reset, which should turn the asynchronous reset into +	 * synchronous one, and wait a little over 1ms to add additional +	 * safety margin. +	 */ +	reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc); +	fsleep(1000); +  	if (rcar->drvdata->additional_common_init)  		rcar->drvdata->additional_common_init(rcar); @@ -398,9 +420,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  }  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_4] = { .type = BAR_FIXED, .fixed_size = 256 }, @@ -701,7 +721,7 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable  	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(23, 22), BIT(22));  	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(18, 16), GENMASK(17, 16));  	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(7, 6), BIT(6)); -	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(2, 0), GENMASK(11, 0)); +	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(2, 0), GENMASK(1, 0));  	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x1d4, GENMASK(16, 15), GENMASK(16, 15));  	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x514, BIT(26), BIT(26));  	rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x0f8, BIT(16), 0); @@ -711,7 +731,7 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable  	val &= ~APP_HOLD_PHY_RST;  	writel(val, rcar->base + PCIERSTCTRL1); -	ret = readl_poll_timeout(rcar->phy_base + 0x0f8, val, !(val & BIT(18)), 100, 10000); +	ret = readl_poll_timeout(rcar->phy_base + 0x0f8, val, val & BIT(18), 100, 10000);  	if (ret < 0)  		return ret; diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c new file mode 100644 index 000000000000..3400c7cd2d88 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics STM32MP25 PCIe endpoint driver. + * + * Copyright (C) 2025 STMicroelectronics + * Author: Christian Bruel <christian.bruel@foss.st.com> + */ + +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include "pcie-designware.h" +#include "pcie-stm32.h" + +struct stm32_pcie { +	struct dw_pcie pci; +	struct regmap *regmap; +	struct reset_control *rst; +	struct phy *phy; +	struct clk *clk; +	struct gpio_desc *perst_gpio; +	unsigned int perst_irq; +}; + +static void stm32_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 int stm32_pcie_enable_link(struct dw_pcie *pci) +{ +	struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + +	regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, +			   STM32MP25_PCIECR_LTSSM_EN, +			   STM32MP25_PCIECR_LTSSM_EN); + +	return dw_pcie_wait_for_link(pci); +} + +static void stm32_pcie_disable_link(struct dw_pcie *pci) +{ +	struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + +	regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, STM32MP25_PCIECR_LTSSM_EN, 0); +} + +static int stm32_pcie_start_link(struct dw_pcie *pci) +{ +	struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); +	int ret; + +	dev_dbg(pci->dev, "Enable link\n"); + +	ret = stm32_pcie_enable_link(pci); +	if (ret) { +		dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret); +		return ret; +	} + +	enable_irq(stm32_pcie->perst_irq); + +	return 0; +} + +static void stm32_pcie_stop_link(struct dw_pcie *pci) +{ +	struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + +	dev_dbg(pci->dev, "Disable link\n"); + +	disable_irq(stm32_pcie->perst_irq); + +	stm32_pcie_disable_link(pci); +} + +static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, +				unsigned int type, u16 interrupt_num) +{ +	struct dw_pcie *pci = 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(pci->dev, "UNKNOWN IRQ type\n"); +		return -EINVAL; +	} +} + +static const struct pci_epc_features stm32_pcie_epc_features = { +	.msi_capable = true, +	.align = SZ_64K, +}; + +static const struct pci_epc_features* +stm32_pcie_get_features(struct dw_pcie_ep *ep) +{ +	return &stm32_pcie_epc_features; +} + +static const struct dw_pcie_ep_ops stm32_pcie_ep_ops = { +	.init = stm32_pcie_ep_init, +	.raise_irq = stm32_pcie_raise_irq, +	.get_features = stm32_pcie_get_features, +}; + +static const struct dw_pcie_ops dw_pcie_ops = { +	.start_link = stm32_pcie_start_link, +	.stop_link = stm32_pcie_stop_link, +}; + +static int stm32_pcie_enable_resources(struct stm32_pcie *stm32_pcie) +{ +	int ret; + +	ret = phy_init(stm32_pcie->phy); +	if (ret) +		return ret; + +	ret = clk_prepare_enable(stm32_pcie->clk); +	if (ret) +		phy_exit(stm32_pcie->phy); + +	return ret; +} + +static void stm32_pcie_disable_resources(struct stm32_pcie *stm32_pcie) +{ +	clk_disable_unprepare(stm32_pcie->clk); + +	phy_exit(stm32_pcie->phy); +} + +static void stm32_pcie_perst_assert(struct dw_pcie *pci) +{ +	struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); +	struct dw_pcie_ep *ep = &stm32_pcie->pci.ep; +	struct device *dev = pci->dev; + +	dev_dbg(dev, "PERST asserted by host\n"); + +	pci_epc_deinit_notify(ep->epc); + +	stm32_pcie_disable_resources(stm32_pcie); + +	pm_runtime_put_sync(dev); +} + +static void stm32_pcie_perst_deassert(struct dw_pcie *pci) +{ +	struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); +	struct device *dev = pci->dev; +	struct dw_pcie_ep *ep = &pci->ep; +	int ret; + +	dev_dbg(dev, "PERST de-asserted by host\n"); + +	ret = pm_runtime_resume_and_get(dev); +	if (ret < 0) { +		dev_err(dev, "Failed to resume runtime PM: %d\n", ret); +		return; +	} + +	ret = stm32_pcie_enable_resources(stm32_pcie); +	if (ret) { +		dev_err(dev, "Failed to enable resources: %d\n", ret); +		goto err_pm_put_sync; +	} + +	/* +	 * Reprogram the configuration space registers here because the DBI +	 * registers were reset by the PHY RCC during phy_init(). +	 */ +	ret = dw_pcie_ep_init_registers(ep); +	if (ret) { +		dev_err(dev, "Failed to complete initialization: %d\n", ret); +		goto err_disable_resources; +	} + +	pci_epc_init_notify(ep->epc); + +	return; + +err_disable_resources: +	stm32_pcie_disable_resources(stm32_pcie); + +err_pm_put_sync: +	pm_runtime_put_sync(dev); +} + +static irqreturn_t stm32_pcie_ep_perst_irq_thread(int irq, void *data) +{ +	struct stm32_pcie *stm32_pcie = data; +	struct dw_pcie *pci = &stm32_pcie->pci; +	u32 perst; + +	perst = gpiod_get_value(stm32_pcie->perst_gpio); +	if (perst) +		stm32_pcie_perst_assert(pci); +	else +		stm32_pcie_perst_deassert(pci); + +	irq_set_irq_type(gpiod_to_irq(stm32_pcie->perst_gpio), +			 (perst ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW)); + +	return IRQ_HANDLED; +} + +static int stm32_add_pcie_ep(struct stm32_pcie *stm32_pcie, +			     struct platform_device *pdev) +{ +	struct dw_pcie_ep *ep = &stm32_pcie->pci.ep; +	struct device *dev = &pdev->dev; +	int ret; + +	ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, +				 STM32MP25_PCIECR_TYPE_MASK, +				 STM32MP25_PCIECR_EP); +	if (ret) +		return ret; + +	reset_control_assert(stm32_pcie->rst); +	reset_control_deassert(stm32_pcie->rst); + +	ep->ops = &stm32_pcie_ep_ops; + +	ret = dw_pcie_ep_init(ep); +	if (ret) { +		dev_err(dev, "Failed to initialize ep: %d\n", ret); +		return ret; +	} + +	ret = stm32_pcie_enable_resources(stm32_pcie); +	if (ret) { +		dev_err(dev, "Failed to enable resources: %d\n", ret); +		dw_pcie_ep_deinit(ep); +		return ret; +	} + +	return 0; +} + +static int stm32_pcie_probe(struct platform_device *pdev) +{ +	struct stm32_pcie *stm32_pcie; +	struct device *dev = &pdev->dev; +	int ret; + +	stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL); +	if (!stm32_pcie) +		return -ENOMEM; + +	stm32_pcie->pci.dev = dev; +	stm32_pcie->pci.ops = &dw_pcie_ops; + +	stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg"); +	if (IS_ERR(stm32_pcie->regmap)) +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap), +				     "No syscfg specified\n"); + +	stm32_pcie->phy = devm_phy_get(dev, NULL); +	if (IS_ERR(stm32_pcie->phy)) +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy), +				     "failed to get pcie-phy\n"); + +	stm32_pcie->clk = devm_clk_get(dev, NULL); +	if (IS_ERR(stm32_pcie->clk)) +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk), +				     "Failed to get PCIe clock source\n"); + +	stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL); +	if (IS_ERR(stm32_pcie->rst)) +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst), +				     "Failed to get PCIe reset\n"); + +	stm32_pcie->perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN); +	if (IS_ERR(stm32_pcie->perst_gpio)) +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio), +				     "Failed to get reset GPIO\n"); + +	ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE); +	if (ret) +		return ret; + +	platform_set_drvdata(pdev, stm32_pcie); + +	pm_runtime_get_noresume(dev); + +	ret = devm_pm_runtime_enable(dev); +	if (ret < 0) { +		pm_runtime_put_noidle(&pdev->dev); +		return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); +	} + +	stm32_pcie->perst_irq = gpiod_to_irq(stm32_pcie->perst_gpio); + +	/* Will be enabled in start_link when device is initialized. */ +	irq_set_status_flags(stm32_pcie->perst_irq, IRQ_NOAUTOEN); + +	ret = devm_request_threaded_irq(dev, stm32_pcie->perst_irq, NULL, +					stm32_pcie_ep_perst_irq_thread, +					IRQF_TRIGGER_HIGH | IRQF_ONESHOT, +					"perst_irq", stm32_pcie); +	if (ret) { +		pm_runtime_put_noidle(&pdev->dev); +		return dev_err_probe(dev, ret, "Failed to request PERST IRQ\n"); +	} + +	ret = stm32_add_pcie_ep(stm32_pcie, pdev); +	if (ret) +		pm_runtime_put_noidle(&pdev->dev); + +	return ret; +} + +static void stm32_pcie_remove(struct platform_device *pdev) +{ +	struct stm32_pcie *stm32_pcie = platform_get_drvdata(pdev); +	struct dw_pcie *pci = &stm32_pcie->pci; +	struct dw_pcie_ep *ep = &pci->ep; + +	dw_pcie_stop_link(pci); + +	pci_epc_deinit_notify(ep->epc); +	dw_pcie_ep_deinit(ep); + +	stm32_pcie_disable_resources(stm32_pcie); + +	pm_runtime_put_sync(&pdev->dev); +} + +static const struct of_device_id stm32_pcie_ep_of_match[] = { +	{ .compatible = "st,stm32mp25-pcie-ep" }, +	{}, +}; + +static struct platform_driver stm32_pcie_ep_driver = { +	.probe = stm32_pcie_probe, +	.remove = stm32_pcie_remove, +	.driver = { +		.name = "stm32-ep-pcie", +		.of_match_table = stm32_pcie_ep_of_match, +	}, +}; + +module_platform_driver(stm32_pcie_ep_driver); + +MODULE_AUTHOR("Christian Bruel <christian.bruel@foss.st.com>"); +MODULE_DESCRIPTION("STM32MP25 PCIe Endpoint Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, stm32_pcie_ep_of_match); diff --git a/drivers/pci/controller/dwc/pcie-stm32.c b/drivers/pci/controller/dwc/pcie-stm32.c new file mode 100644 index 000000000000..96a5fb893af4 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-stm32.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics STM32MP25 PCIe root complex driver. + * + * Copyright (C) 2025 STMicroelectronics + * Author: Christian Bruel <christian.bruel@foss.st.com> + */ + +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeirq.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include "pcie-designware.h" +#include "pcie-stm32.h" +#include "../../pci.h" + +struct stm32_pcie { +	struct dw_pcie pci; +	struct regmap *regmap; +	struct reset_control *rst; +	struct phy *phy; +	struct clk *clk; +	struct gpio_desc *perst_gpio; +	struct gpio_desc *wake_gpio; +}; + +static void stm32_pcie_deassert_perst(struct stm32_pcie *stm32_pcie) +{ +	if (stm32_pcie->perst_gpio) { +		msleep(PCIE_T_PVPERL_MS); +		gpiod_set_value(stm32_pcie->perst_gpio, 0); +	} + +	msleep(PCIE_RESET_CONFIG_WAIT_MS); +} + +static void stm32_pcie_assert_perst(struct stm32_pcie *stm32_pcie) +{ +	gpiod_set_value(stm32_pcie->perst_gpio, 1); +} + +static int stm32_pcie_start_link(struct dw_pcie *pci) +{ +	struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + +	return regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, +				  STM32MP25_PCIECR_LTSSM_EN, +				  STM32MP25_PCIECR_LTSSM_EN); +} + +static void stm32_pcie_stop_link(struct dw_pcie *pci) +{ +	struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + +	regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, +			   STM32MP25_PCIECR_LTSSM_EN, 0); +} + +static int stm32_pcie_suspend_noirq(struct device *dev) +{ +	struct stm32_pcie *stm32_pcie = dev_get_drvdata(dev); +	int ret; + +	ret = dw_pcie_suspend_noirq(&stm32_pcie->pci); +	if (ret) +		return ret; + +	stm32_pcie_assert_perst(stm32_pcie); + +	clk_disable_unprepare(stm32_pcie->clk); + +	if (!device_wakeup_path(dev)) +		phy_exit(stm32_pcie->phy); + +	return pinctrl_pm_select_sleep_state(dev); +} + +static int stm32_pcie_resume_noirq(struct device *dev) +{ +	struct stm32_pcie *stm32_pcie = dev_get_drvdata(dev); +	int ret; + +	/* +	 * The core clock is gated with CLKREQ# from the COMBOPHY REFCLK, +	 * thus if no device is present, must deassert it with a GPIO from +	 * pinctrl pinmux before accessing the DBI registers. +	 */ +	ret = pinctrl_pm_select_init_state(dev); +	if (ret) { +		dev_err(dev, "Failed to activate pinctrl pm state: %d\n", ret); +		return ret; +	} + +	if (!device_wakeup_path(dev)) { +		ret = phy_init(stm32_pcie->phy); +		if (ret) { +			pinctrl_pm_select_default_state(dev); +			return ret; +		} +	} + +	ret = clk_prepare_enable(stm32_pcie->clk); +	if (ret) +		goto err_phy_exit; + +	stm32_pcie_deassert_perst(stm32_pcie); + +	ret = dw_pcie_resume_noirq(&stm32_pcie->pci); +	if (ret) +		goto err_disable_clk; + +	pinctrl_pm_select_default_state(dev); + +	return 0; + +err_disable_clk: +	stm32_pcie_assert_perst(stm32_pcie); +	clk_disable_unprepare(stm32_pcie->clk); + +err_phy_exit: +	phy_exit(stm32_pcie->phy); +	pinctrl_pm_select_default_state(dev); + +	return ret; +} + +static const struct dev_pm_ops stm32_pcie_pm_ops = { +	NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_pcie_suspend_noirq, +				  stm32_pcie_resume_noirq) +}; + +static const struct dw_pcie_host_ops stm32_pcie_host_ops = { +}; + +static const struct dw_pcie_ops dw_pcie_ops = { +	.start_link = stm32_pcie_start_link, +	.stop_link = stm32_pcie_stop_link +}; + +static int stm32_add_pcie_port(struct stm32_pcie *stm32_pcie) +{ +	struct device *dev = stm32_pcie->pci.dev; +	unsigned int wake_irq; +	int ret; + +	ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE); +	if (ret) +		return ret; + +	ret = phy_init(stm32_pcie->phy); +	if (ret) +		return ret; + +	ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, +				 STM32MP25_PCIECR_TYPE_MASK, +				 STM32MP25_PCIECR_RC); +	if (ret) +		goto err_phy_exit; + +	stm32_pcie_deassert_perst(stm32_pcie); + +	if (stm32_pcie->wake_gpio) { +		wake_irq = gpiod_to_irq(stm32_pcie->wake_gpio); +		ret = dev_pm_set_dedicated_wake_irq(dev, wake_irq); +		if (ret) { +			dev_err(dev, "Failed to enable wakeup irq %d\n", ret); +			goto err_assert_perst; +		} +		irq_set_irq_type(wake_irq, IRQ_TYPE_EDGE_FALLING); +	} + +	return 0; + +err_assert_perst: +	stm32_pcie_assert_perst(stm32_pcie); + +err_phy_exit: +	phy_exit(stm32_pcie->phy); + +	return ret; +} + +static void stm32_remove_pcie_port(struct stm32_pcie *stm32_pcie) +{ +	dev_pm_clear_wake_irq(stm32_pcie->pci.dev); + +	stm32_pcie_assert_perst(stm32_pcie); + +	phy_exit(stm32_pcie->phy); +} + +static int stm32_pcie_parse_port(struct stm32_pcie *stm32_pcie) +{ +	struct device *dev = stm32_pcie->pci.dev; +	struct device_node *root_port; + +	root_port = of_get_next_available_child(dev->of_node, NULL); + +	stm32_pcie->phy = devm_of_phy_get(dev, root_port, NULL); +	if (IS_ERR(stm32_pcie->phy)) { +		of_node_put(root_port); +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy), +				     "Failed to get pcie-phy\n"); +	} + +	stm32_pcie->perst_gpio = devm_fwnode_gpiod_get(dev, of_fwnode_handle(root_port), +						       "reset", GPIOD_OUT_HIGH, NULL); +	if (IS_ERR(stm32_pcie->perst_gpio)) { +		if (PTR_ERR(stm32_pcie->perst_gpio) != -ENOENT) { +			of_node_put(root_port); +			return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio), +					     "Failed to get reset GPIO\n"); +		} +		stm32_pcie->perst_gpio = NULL; +	} + +	stm32_pcie->wake_gpio = devm_fwnode_gpiod_get(dev, of_fwnode_handle(root_port), +						      "wake", GPIOD_IN, NULL); + +	if (IS_ERR(stm32_pcie->wake_gpio)) { +		if (PTR_ERR(stm32_pcie->wake_gpio) != -ENOENT) { +			of_node_put(root_port); +			return dev_err_probe(dev, PTR_ERR(stm32_pcie->wake_gpio), +					     "Failed to get wake GPIO\n"); +		} +		stm32_pcie->wake_gpio = NULL; +	} + +	of_node_put(root_port); + +	return 0; +} + +static int stm32_pcie_probe(struct platform_device *pdev) +{ +	struct stm32_pcie *stm32_pcie; +	struct device *dev = &pdev->dev; +	int ret; + +	stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL); +	if (!stm32_pcie) +		return -ENOMEM; + +	stm32_pcie->pci.dev = dev; +	stm32_pcie->pci.ops = &dw_pcie_ops; +	stm32_pcie->pci.pp.ops = &stm32_pcie_host_ops; + +	stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg"); +	if (IS_ERR(stm32_pcie->regmap)) +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap), +				     "No syscfg specified\n"); + +	stm32_pcie->clk = devm_clk_get(dev, NULL); +	if (IS_ERR(stm32_pcie->clk)) +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk), +				     "Failed to get PCIe clock source\n"); + +	stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL); +	if (IS_ERR(stm32_pcie->rst)) +		return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst), +				     "Failed to get PCIe reset\n"); + +	ret = stm32_pcie_parse_port(stm32_pcie); +	if (ret) +		return ret; + +	platform_set_drvdata(pdev, stm32_pcie); + +	ret = stm32_add_pcie_port(stm32_pcie); +	if (ret) +		return ret; + +	reset_control_assert(stm32_pcie->rst); +	reset_control_deassert(stm32_pcie->rst); + +	ret = clk_prepare_enable(stm32_pcie->clk); +	if (ret) { +		dev_err(dev, "Core clock enable failed %d\n", ret); +		goto err_remove_port; +	} + +	ret = pm_runtime_set_active(dev); +	if (ret < 0) { +		dev_err_probe(dev, ret, "Failed to activate runtime PM\n"); +		goto err_disable_clk; +	} + +	pm_runtime_no_callbacks(dev); + +	ret = devm_pm_runtime_enable(dev); +	if (ret < 0) { +		dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); +		goto err_disable_clk; +	} + +	ret = dw_pcie_host_init(&stm32_pcie->pci.pp); +	if (ret) +		goto err_disable_clk; + +	if (stm32_pcie->wake_gpio) +		device_init_wakeup(dev, true); + +	return 0; + +err_disable_clk: +	clk_disable_unprepare(stm32_pcie->clk); + +err_remove_port: +	stm32_remove_pcie_port(stm32_pcie); + +	return ret; +} + +static void stm32_pcie_remove(struct platform_device *pdev) +{ +	struct stm32_pcie *stm32_pcie = platform_get_drvdata(pdev); +	struct dw_pcie_rp *pp = &stm32_pcie->pci.pp; + +	if (stm32_pcie->wake_gpio) +		device_init_wakeup(&pdev->dev, false); + +	dw_pcie_host_deinit(pp); + +	clk_disable_unprepare(stm32_pcie->clk); + +	stm32_remove_pcie_port(stm32_pcie); + +	pm_runtime_put_noidle(&pdev->dev); +} + +static const struct of_device_id stm32_pcie_of_match[] = { +	{ .compatible = "st,stm32mp25-pcie-rc" }, +	{}, +}; + +static struct platform_driver stm32_pcie_driver = { +	.probe = stm32_pcie_probe, +	.remove = stm32_pcie_remove, +	.driver = { +		.name = "stm32-pcie", +		.of_match_table = stm32_pcie_of_match, +		.pm = &stm32_pcie_pm_ops, +		.probe_type = PROBE_PREFER_ASYNCHRONOUS, +	}, +}; + +module_platform_driver(stm32_pcie_driver); + +MODULE_AUTHOR("Christian Bruel <christian.bruel@foss.st.com>"); +MODULE_DESCRIPTION("STM32MP25 PCIe Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, stm32_pcie_of_match); diff --git a/drivers/pci/controller/dwc/pcie-stm32.h b/drivers/pci/controller/dwc/pcie-stm32.h new file mode 100644 index 000000000000..09d39f04e469 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-stm32.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ST PCIe driver definitions for STM32-MP25 SoC + * + * Copyright (C) 2025 STMicroelectronics - All Rights Reserved + * Author: Christian Bruel <christian.bruel@foss.st.com> + */ + +#define to_stm32_pcie(x)	dev_get_drvdata((x)->dev) + +#define STM32MP25_PCIECR_TYPE_MASK	GENMASK(11, 8) +#define STM32MP25_PCIECR_EP		0 +#define STM32MP25_PCIECR_LTSSM_EN	BIT(2) +#define STM32MP25_PCIECR_RC		BIT(10) + +#define SYSCFG_PCIECR			0x6000 diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 4f26086f25da..10e74458e667 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1214,6 +1214,7 @@ static int tegra_pcie_bpmp_set_ctrl_state(struct tegra_pcie_dw *pcie,  	struct mrq_uphy_response resp;  	struct tegra_bpmp_message msg;  	struct mrq_uphy_request req; +	int err;  	/*  	 * Controller-5 doesn't need to have its state set by BPMP-FW in @@ -1236,7 +1237,13 @@ static int tegra_pcie_bpmp_set_ctrl_state(struct tegra_pcie_dw *pcie,  	msg.rx.data = &resp;  	msg.rx.size = sizeof(resp); -	return tegra_bpmp_transfer(pcie->bpmp, &msg); +	err = tegra_bpmp_transfer(pcie->bpmp, &msg); +	if (err) +		return err; +	if (msg.rx.ret) +		return -EINVAL; + +	return 0;  }  static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie, @@ -1245,6 +1252,7 @@ static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie,  	struct mrq_uphy_response resp;  	struct tegra_bpmp_message msg;  	struct mrq_uphy_request req; +	int err;  	memset(&req, 0, sizeof(req));  	memset(&resp, 0, sizeof(resp)); @@ -1264,13 +1272,19 @@ static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie,  	msg.rx.data = &resp;  	msg.rx.size = sizeof(resp); -	return tegra_bpmp_transfer(pcie->bpmp, &msg); +	err = tegra_bpmp_transfer(pcie->bpmp, &msg); +	if (err) +		return err; +	if (msg.rx.ret) +		return -EINVAL; + +	return 0;  }  static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie)  {  	struct dw_pcie_rp *pp = &pcie->pci.pp; -	struct pci_bus *child, *root_bus = NULL; +	struct pci_bus *child, *root_port_bus = NULL;  	struct pci_dev *pdev;  	/* @@ -1283,19 +1297,19 @@ static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie)  	 */  	list_for_each_entry(child, &pp->bridge->bus->children, node) { -		/* Bring downstream devices to D0 if they are not already in */  		if (child->parent == pp->bridge->bus) { -			root_bus = child; +			root_port_bus = child;  			break;  		}  	} -	if (!root_bus) { -		dev_err(pcie->dev, "Failed to find downstream devices\n"); +	if (!root_port_bus) { +		dev_err(pcie->dev, "Failed to find downstream bus of Root Port\n");  		return;  	} -	list_for_each_entry(pdev, &root_bus->devices, bus_list) { +	/* Bring downstream devices to D0 if they are not already in */ +	list_for_each_entry(pdev, &root_port_bus->devices, bus_list) {  		if (PCI_SLOT(pdev->devfn) == 0) {  			if (pci_set_power_state(pdev, PCI_D0))  				dev_err(pcie->dev, @@ -1722,9 +1736,9 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie)  				ret);  	} -	ret = tegra_pcie_bpmp_set_pll_state(pcie, false); +	ret = tegra_pcie_bpmp_set_ctrl_state(pcie, false);  	if (ret) -		dev_err(pcie->dev, "Failed to turn off UPHY: %d\n", ret); +		dev_err(pcie->dev, "Failed to disable controller: %d\n", ret);  	pcie->ep_state = EP_STATE_DISABLED;  	dev_dbg(pcie->dev, "Uninitialization of endpoint is completed\n"); @@ -1941,6 +1955,15 @@ static irqreturn_t tegra_pcie_ep_pex_rst_irq(int irq, void *arg)  	return IRQ_HANDLED;  } +static void tegra_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 int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq)  {  	/* Tegra194 supports only INTA */ @@ -1955,10 +1978,10 @@ static int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq)  static int tegra_pcie_ep_raise_msi_irq(struct tegra_pcie_dw *pcie, u16 irq)  { -	if (unlikely(irq > 31)) +	if (unlikely(irq > 32))  		return -EINVAL; -	appl_writel(pcie, BIT(irq), APPL_MSI_CTRL_1); +	appl_writel(pcie, BIT(irq - 1), APPL_MSI_CTRL_1);  	return 0;  } @@ -1998,8 +2021,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  static const struct pci_epc_features tegra_pcie_epc_features = {  	.linkup_notifier = true, -	.msi_capable = false, -	.msix_capable = false, +	.msi_capable = true,  	.bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,  			.only_64bit = true, },  	.bar[BAR_1] = { .type = BAR_RESERVED, }, @@ -2017,6 +2039,7 @@ tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)  }  static const struct dw_pcie_ep_ops pcie_ep_ops = { +	.init = tegra_pcie_ep_init,  	.raise_irq = tegra_pcie_ep_raise_irq,  	.get_features = tegra_pcie_ep_get_features,  }; | 
