diff options
Diffstat (limited to 'drivers/pci/controller/dwc')
| -rw-r--r-- | drivers/pci/controller/dwc/Kconfig | 25 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pci-dra7xx.c | 2 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 200 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-bt1.c | 4 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-ep.c | 12 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-host.c | 25 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.c | 205 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.h | 21 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-histb.c | 1 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 15 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-tegra194.c | 9 | 
11 files changed, 485 insertions, 34 deletions
| diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index a0d2713f0e88..434f6a4f4041 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -92,10 +92,31 @@ config PCI_EXYNOS  	  functions to implement the driver.  config PCI_IMX6 -	bool "Freescale i.MX6/7/8 PCIe controller" +	bool + +config PCI_IMX6_HOST +	bool "Freescale i.MX6/7/8 PCIe controller host mode"  	depends on ARCH_MXC || COMPILE_TEST  	depends on PCI_MSI  	select PCIE_DW_HOST +	select PCI_IMX6 +	help +	  Enables support for the PCIe controller in the i.MX SoCs to +	  work in Root Complex mode. The PCI controller on i.MX is based +	  on DesignWare hardware and therefore the driver re-uses the +	  DesignWare core functions to implement the driver. + +config PCI_IMX6_EP +	bool "Freescale i.MX6/7/8 PCIe controller endpoint mode" +	depends on ARCH_MXC || COMPILE_TEST +	depends on PCI_ENDPOINT +	select PCIE_DW_EP +	select PCI_IMX6 +	help +	  Enables support for the PCIe controller in the i.MX SoCs to +	  work in endpoint mode. The PCI controller on i.MX is based +	  on DesignWare hardware and therefore the driver re-uses the +	  DesignWare core functions to implement the driver.  config PCIE_SPEAR13XX  	bool "STMicroelectronics SPEAr PCIe controller" @@ -225,7 +246,7 @@ config PCIE_ARTPEC6_EP  config PCIE_BT1  	tristate "Baikal-T1 PCIe controller"  	depends on MIPS_BAIKAL_T1 || COMPILE_TEST -	depends on PCI_MSI_IRQ_DOMAIN +	depends on PCI_MSI  	select PCIE_DW_HOST  	help  	  Enables support for the PCIe controller in the Baikal-T1 SoC to work diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 38462ed11d07..4ae807e7cf79 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -840,7 +840,7 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)  	}  	dra7xx->mode = mode; -	ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler, +	ret = devm_request_threaded_irq(dev, irq, NULL, dra7xx_pcie_irq_handler,  			       IRQF_SHARED, "dra7xx-pcie-main", dra7xx);  	if (ret) {  		dev_err(dev, "failed to request irq\n"); diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 1dde5c579edc..55a0405b921d 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -52,6 +52,9 @@ enum imx6_pcie_variants {  	IMX8MQ,  	IMX8MM,  	IMX8MP, +	IMX8MQ_EP, +	IMX8MM_EP, +	IMX8MP_EP,  };  #define IMX6_PCIE_FLAG_IMX6_PHY			BIT(0) @@ -60,6 +63,7 @@ enum imx6_pcie_variants {  struct imx6_pcie_drvdata {  	enum imx6_pcie_variants variant; +	enum dw_pcie_device_mode mode;  	u32 flags;  	int dbi_length;  	const char *gpr; @@ -152,24 +156,39 @@ struct imx6_pcie {  static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)  {  	WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ && +		imx6_pcie->drvdata->variant != IMX8MQ_EP &&  		imx6_pcie->drvdata->variant != IMX8MM && -		imx6_pcie->drvdata->variant != IMX8MP); +		imx6_pcie->drvdata->variant != IMX8MM_EP && +		imx6_pcie->drvdata->variant != IMX8MP && +		imx6_pcie->drvdata->variant != IMX8MP_EP);  	return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;  }  static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie)  { -	unsigned int mask, val; +	unsigned int mask, val, mode; -	if (imx6_pcie->drvdata->variant == IMX8MQ && -	    imx6_pcie->controller_id == 1) { -		mask = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE; -		val  = FIELD_PREP(IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, -				  PCI_EXP_TYPE_ROOT_PORT); -	} else { +	if (imx6_pcie->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, -				  PCI_EXP_TYPE_ROOT_PORT); +		val  = FIELD_PREP(IMX6Q_GPR12_DEVICE_TYPE, mode); +		break;  	}  	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, mask, val); @@ -304,13 +323,16 @@ static void imx6_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 @@ -561,8 +583,11 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)  	case IMX7D:  		break;  	case IMX8MM: +	case IMX8MM_EP:  	case IMX8MQ: +	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"); @@ -606,8 +631,11 @@ static void imx6_pcie_disable_ref_clk(struct imx6_pcie *imx6_pcie)  				   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: @@ -672,10 +700,13 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)  	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: @@ -713,6 +744,7 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)  	switch (imx6_pcie->drvdata->variant) {  	case IMX8MQ: +	case IMX8MQ_EP:  		reset_control_deassert(imx6_pcie->pciephy_reset);  		break;  	case IMX7D: @@ -751,7 +783,9 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)  		break;  	case IMX6Q:		/* Nothing to do */  	case IMX8MM: +	case IMX8MM_EP:  	case IMX8MP: +	case IMX8MP_EP:  		break;  	} @@ -800,8 +834,11 @@ static void imx6_pcie_ltssm_enable(struct device *dev)  		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;  	} @@ -820,8 +857,11 @@ static void imx6_pcie_ltssm_disable(struct device *dev)  		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;  	} @@ -1003,8 +1043,104 @@ static const struct dw_pcie_host_ops imx6_pcie_host_ops = {  static const struct dw_pcie_ops dw_pcie_ops = {  	.start_link = imx6_pcie_start_link, +	.stop_link = imx6_pcie_stop_link, +}; + +static void imx6_pcie_ep_init(struct dw_pcie_ep *ep) +{ +	enum pci_barno bar; +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	for (bar = BAR_0; bar <= BAR_5; bar++) +		dw_pcie_ep_reset_bar(pci, bar); +} + +static int imx6_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, +				  enum pci_epc_irq_type 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: +		return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); +	case PCI_EPC_IRQ_MSIX: +		return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num); +	default: +		dev_err(pci->dev, "UNKNOWN IRQ type\n"); +		return -EINVAL; +	} + +	return 0; +} + +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, +	.align = SZ_64K, +}; + +static const struct pci_epc_features* +imx6_pcie_ep_get_features(struct dw_pcie_ep *ep) +{ +	return &imx8m_pcie_epc_features; +} + +static const struct dw_pcie_ep_ops pcie_ep_ops = { +	.ep_init = imx6_pcie_ep_init, +	.raise_irq = imx6_pcie_ep_raise_irq, +	.get_features = imx6_pcie_ep_get_features,  }; +static int imx6_add_pcie_ep(struct imx6_pcie *imx6_pcie, +			   struct platform_device *pdev) +{ +	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; + +	imx6_pcie_host_init(pp); +	ep = &pci->ep; +	ep->ops = &pcie_ep_ops; + +	switch (imx6_pcie->drvdata->variant) { +	case IMX8MQ_EP: +	case IMX8MM_EP: +	case IMX8MP_EP: +		pcie_dbi2_offset = SZ_1M; +		break; +	default: +		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; + +	ret = dw_pcie_ep_init(ep); +	if (ret) { +		dev_err(dev, "failed to initialize endpoint\n"); +		return ret; +	} +	/* Start LTSSM. */ +	imx6_pcie_ltssm_enable(dev); + +	return 0; +} +  static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)  {  	struct device *dev = imx6_pcie->pci->dev; @@ -1166,6 +1302,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)  					     "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), @@ -1190,7 +1327,9 @@ static int imx6_pcie_probe(struct platform_device *pdev)  		}  		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), @@ -1279,15 +1418,22 @@ static int imx6_pcie_probe(struct platform_device *pdev)  	if (ret)  		return ret; -	ret = dw_pcie_host_init(&pci->pp); -	if (ret < 0) -		return ret; +	if (imx6_pcie->drvdata->mode == DW_PCIE_EP_TYPE) { +		ret = imx6_add_pcie_ep(imx6_pcie, pdev); +		if (ret < 0) +			return ret; +	} else { +		ret = dw_pcie_host_init(&pci->pp); +		if (ret < 0) +			return ret; + +		if (pci_msi_enabled()) { +			u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI); -	if (pci_msi_enabled()) { -		u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI); -		val = dw_pcie_readw_dbi(pci, offset + PCI_MSI_FLAGS); -		val |= PCI_MSI_FLAGS_ENABLE; -		dw_pcie_writew_dbi(pci, offset + PCI_MSI_FLAGS, val); +			val = dw_pcie_readw_dbi(pci, offset + PCI_MSI_FLAGS); +			val |= PCI_MSI_FLAGS_ENABLE; +			dw_pcie_writew_dbi(pci, offset + PCI_MSI_FLAGS, val); +		}  	}  	return 0; @@ -1343,6 +1489,21 @@ static const struct imx6_pcie_drvdata drvdata[] = {  		.flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND,  		.gpr = "fsl,imx8mp-iomuxc-gpr",  	}, +	[IMX8MQ_EP] = { +		.variant = IMX8MQ_EP, +		.mode = DW_PCIE_EP_TYPE, +		.gpr = "fsl,imx8mq-iomuxc-gpr", +	}, +	[IMX8MM_EP] = { +		.variant = IMX8MM_EP, +		.mode = DW_PCIE_EP_TYPE, +		.gpr = "fsl,imx8mm-iomuxc-gpr", +	}, +	[IMX8MP_EP] = { +		.variant = IMX8MP_EP, +		.mode = DW_PCIE_EP_TYPE, +		.gpr = "fsl,imx8mp-iomuxc-gpr", +	},  };  static const struct of_device_id imx6_pcie_of_match[] = { @@ -1353,6 +1514,9 @@ 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,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], },  	{},  }; diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c index 3346770e6654..95a723a6fd46 100644 --- a/drivers/pci/controller/dwc/pcie-bt1.c +++ b/drivers/pci/controller/dwc/pcie-bt1.c @@ -583,6 +583,10 @@ static int bt1_pcie_add_port(struct bt1_pcie *btpci)  	struct device *dev = &btpci->pdev->dev;  	int ret; +	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); +	if (ret) +		return ret; +  	btpci->dw.version = DW_PCIE_VER_460A;  	btpci->dw.dev = dev;  	btpci->dw.ops = &bt1_pcie_ops; diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index d06654895eba..f9182f8d552f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -612,8 +612,11 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,  void dw_pcie_ep_exit(struct dw_pcie_ep *ep)  { +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);  	struct pci_epc *epc = ep->epc; +	dw_pcie_edma_remove(pci); +  	pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,  			      epc->mem->window.page_size); @@ -785,6 +788,10 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)  		goto err_exit_epc_mem;  	} +	ret = dw_pcie_edma_detect(pci); +	if (ret) +		goto err_free_epc_mem; +  	if (ep->ops->get_features) {  		epc_features = ep->ops->get_features(ep);  		if (epc_features->core_init_notifier) @@ -793,10 +800,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)  	ret = dw_pcie_ep_init_complete(ep);  	if (ret) -		goto err_free_epc_mem; +		goto err_remove_edma;  	return 0; +err_remove_edma: +	dw_pcie_edma_remove(pci); +  err_free_epc_mem:  	pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,  			      epc->mem->window.page_size); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 3ab6ae3712c4..9952057c8819 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -366,7 +366,17 @@ static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)  						    dw_chained_msi_isr, pp);  	} -	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); +	/* +	 * Even though the iMSI-RX Module supports 64-bit addresses some +	 * peripheral PCIe devices may lack 64-bit message support. In +	 * order not to miss MSI TLPs from those devices the MSI target +	 * address has to be within the lowest 4GB. +	 * +	 * Note until there is a better alternative found the reservation is +	 * done by allocating from the artificially limited DMA-coherent +	 * 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"); @@ -467,14 +477,18 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)  	dw_pcie_iatu_detect(pci); -	ret = dw_pcie_setup_rc(pp); +	ret = dw_pcie_edma_detect(pci);  	if (ret)  		goto err_free_msi; +	ret = dw_pcie_setup_rc(pp); +	if (ret) +		goto err_remove_edma; +  	if (!dw_pcie_link_up(pci)) {  		ret = dw_pcie_start_link(pci);  		if (ret) -			goto err_free_msi; +			goto err_remove_edma;  	}  	/* Ignore errors, the link may come up later */ @@ -491,6 +505,9 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)  err_stop_link:  	dw_pcie_stop_link(pci); +err_remove_edma: +	dw_pcie_edma_remove(pci); +  err_free_msi:  	if (pp->has_msi_ctrl)  		dw_pcie_free_msi(pp); @@ -512,6 +529,8 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)  	dw_pcie_stop_link(pci); +	dw_pcie_edma_remove(pci); +  	if (pp->has_msi_ctrl)  		dw_pcie_free_msi(pp); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 6d5d619ab2e9..8e33e6e59e68 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -12,6 +12,7 @@  #include <linux/bitops.h>  #include <linux/clk.h>  #include <linux/delay.h> +#include <linux/dma/edma.h>  #include <linux/gpio/consumer.h>  #include <linux/ioport.h>  #include <linux/of.h> @@ -142,6 +143,18 @@ int dw_pcie_get_resources(struct dw_pcie *pci)  	if (!pci->atu_size)  		pci->atu_size = SZ_4K; +	/* eDMA region can be mapped to a custom base address */ +	if (!pci->edma.reg_base) { +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); +		if (res) { +			pci->edma.reg_base = devm_ioremap_resource(pci->dev, res); +			if (IS_ERR(pci->edma.reg_base)) +				return PTR_ERR(pci->edma.reg_base); +		} else if (pci->atu_size >= 2 * DEFAULT_DBI_DMA_OFFSET) { +			pci->edma.reg_base = pci->atu_base + DEFAULT_DBI_DMA_OFFSET; +		} +	} +  	/* 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); @@ -782,6 +795,188 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci)  		 pci->region_align / SZ_1K, (pci->region_limit + 1) / SZ_1G);  } +static u32 dw_pcie_readl_dma(struct dw_pcie *pci, u32 reg) +{ +	u32 val = 0; +	int ret; + +	if (pci->ops && pci->ops->read_dbi) +		return pci->ops->read_dbi(pci, pci->edma.reg_base, reg, 4); + +	ret = dw_pcie_read(pci->edma.reg_base + reg, 4, &val); +	if (ret) +		dev_err(pci->dev, "Read DMA address failed\n"); + +	return val; +} + +static int dw_pcie_edma_irq_vector(struct device *dev, unsigned int nr) +{ +	struct platform_device *pdev = to_platform_device(dev); +	char name[6]; +	int ret; + +	if (nr >= EDMA_MAX_WR_CH + EDMA_MAX_RD_CH) +		return -EINVAL; + +	ret = platform_get_irq_byname_optional(pdev, "dma"); +	if (ret > 0) +		return ret; + +	snprintf(name, sizeof(name), "dma%u", nr); + +	return platform_get_irq_byname_optional(pdev, name); +} + +static struct dw_edma_core_ops dw_pcie_edma_ops = { +	.irq_vector = dw_pcie_edma_irq_vector, +}; + +static int dw_pcie_edma_find_chip(struct dw_pcie *pci) +{ +	u32 val; + +	/* +	 * 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. +	 */ +	if (dw_pcie_ver_is_ge(pci, 540A)) +		val = 0xFFFFFFFF; +	else +		val = dw_pcie_readl_dbi(pci, PCIE_DMA_VIEWPORT_BASE + PCIE_DMA_CTRL); + +	if (val == 0xFFFFFFFF && pci->edma.reg_base) { +		pci->edma.mf = EDMA_MF_EDMA_UNROLL; + +		val = dw_pcie_readl_dma(pci, PCIE_DMA_CTRL); +	} else if (val != 0xFFFFFFFF) { +		pci->edma.mf = EDMA_MF_EDMA_LEGACY; + +		pci->edma.reg_base = pci->dbi_base + PCIE_DMA_VIEWPORT_BASE; +	} else { +		return -ENODEV; +	} + +	pci->edma.dev = pci->dev; + +	if (!pci->edma.ops) +		pci->edma.ops = &dw_pcie_edma_ops; + +	pci->edma.flags |= DW_EDMA_CHIP_LOCAL; + +	pci->edma.ll_wr_cnt = FIELD_GET(PCIE_DMA_NUM_WR_CHAN, val); +	pci->edma.ll_rd_cnt = FIELD_GET(PCIE_DMA_NUM_RD_CHAN, val); + +	/* Sanity check the channels count if the mapping was incorrect */ +	if (!pci->edma.ll_wr_cnt || pci->edma.ll_wr_cnt > EDMA_MAX_WR_CH || +	    !pci->edma.ll_rd_cnt || pci->edma.ll_rd_cnt > EDMA_MAX_RD_CH) +		return -EINVAL; + +	return 0; +} + +static int dw_pcie_edma_irq_verify(struct dw_pcie *pci) +{ +	struct platform_device *pdev = to_platform_device(pci->dev); +	u16 ch_cnt = pci->edma.ll_wr_cnt + pci->edma.ll_rd_cnt; +	char name[6]; +	int ret; + +	if (pci->edma.nr_irqs == 1) +		return 0; +	else if (pci->edma.nr_irqs > 1) +		return pci->edma.nr_irqs != ch_cnt ? -EINVAL : 0; + +	ret = platform_get_irq_byname_optional(pdev, "dma"); +	if (ret > 0) { +		pci->edma.nr_irqs = 1; +		return 0; +	} + +	for (; pci->edma.nr_irqs < ch_cnt; pci->edma.nr_irqs++) { +		snprintf(name, sizeof(name), "dma%d", pci->edma.nr_irqs); + +		ret = platform_get_irq_byname_optional(pdev, name); +		if (ret <= 0) +			return -EINVAL; +	} + +	return 0; +} + +static int dw_pcie_edma_ll_alloc(struct dw_pcie *pci) +{ +	struct dw_edma_region *ll; +	dma_addr_t paddr; +	int i; + +	for (i = 0; i < pci->edma.ll_wr_cnt; i++) { +		ll = &pci->edma.ll_region_wr[i]; +		ll->sz = DMA_LLP_MEM_SIZE; +		ll->vaddr.mem = dmam_alloc_coherent(pci->dev, ll->sz, +						    &paddr, GFP_KERNEL); +		if (!ll->vaddr.mem) +			return -ENOMEM; + +		ll->paddr = paddr; +	} + +	for (i = 0; i < pci->edma.ll_rd_cnt; i++) { +		ll = &pci->edma.ll_region_rd[i]; +		ll->sz = DMA_LLP_MEM_SIZE; +		ll->vaddr.mem = dmam_alloc_coherent(pci->dev, ll->sz, +						    &paddr, GFP_KERNEL); +		if (!ll->vaddr.mem) +			return -ENOMEM; + +		ll->paddr = paddr; +	} + +	return 0; +} + +int dw_pcie_edma_detect(struct dw_pcie *pci) +{ +	int ret; + +	/* Don't fail if no eDMA was found (for the backward compatibility) */ +	ret = dw_pcie_edma_find_chip(pci); +	if (ret) +		return 0; + +	/* Don't fail on the IRQs verification (for the backward compatibility) */ +	ret = dw_pcie_edma_irq_verify(pci); +	if (ret) { +		dev_err(pci->dev, "Invalid eDMA IRQs found\n"); +		return 0; +	} + +	ret = dw_pcie_edma_ll_alloc(pci); +	if (ret) { +		dev_err(pci->dev, "Couldn't allocate LLP memory\n"); +		return ret; +	} + +	/* Don't fail if the DW eDMA driver can't find the device */ +	ret = dw_edma_probe(&pci->edma); +	if (ret && ret != -ENODEV) { +		dev_err(pci->dev, "Couldn't register eDMA device\n"); +		return ret; +	} + +	dev_info(pci->dev, "eDMA: unroll %s, %hu wr, %hu rd\n", +		 pci->edma.mf == EDMA_MF_EDMA_UNROLL ? "T" : "F", +		 pci->edma.ll_wr_cnt, pci->edma.ll_rd_cnt); + +	return 0; +} + +void dw_pcie_edma_remove(struct dw_pcie *pci) +{ +	dw_edma_remove(&pci->edma); +} +  void dw_pcie_setup(struct dw_pcie *pci)  {  	u32 val; @@ -806,11 +1001,6 @@ void dw_pcie_setup(struct dw_pcie *pci)  		dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);  	} -	val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); -	val &= ~PORT_LINK_FAST_LINK_MODE; -	val |= PORT_LINK_DLL_LINK_EN; -	dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); -  	if (dw_pcie_cap_is(pci, CDM_CHECK)) {  		val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS);  		val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS | @@ -818,6 +1008,11 @@ void dw_pcie_setup(struct dw_pcie *pci)  		dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val);  	} +	val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); +	val &= ~PORT_LINK_FAST_LINK_MODE; +	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; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 393dfb931df6..79713ce075cc 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -15,6 +15,7 @@  #include <linux/bitops.h>  #include <linux/clk.h>  #include <linux/dma-mapping.h> +#include <linux/dma/edma.h>  #include <linux/gpio/consumer.h>  #include <linux/irq.h>  #include <linux/msi.h> @@ -31,6 +32,7 @@  #define DW_PCIE_VER_480A		0x3438302a  #define DW_PCIE_VER_490A		0x3439302a  #define DW_PCIE_VER_520A		0x3532302a +#define DW_PCIE_VER_540A		0x3534302a  #define __dw_pcie_ver_cmp(_pci, _ver, _op) \  	((_pci)->version _op DW_PCIE_VER_ ## _ver) @@ -167,6 +169,18 @@  #define PCIE_MSIX_DOORBELL		0x948  #define PCIE_MSIX_DOORBELL_PF_SHIFT	24 +/* + * eDMA CSRs. DW PCIe IP-core v4.70a and older had the eDMA registers accessible + * over the Port Logic registers space. Afterwards the unrolled mapping was + * introduced so eDMA and iATU could be accessed via a dedicated registers + * space. + */ +#define PCIE_DMA_VIEWPORT_BASE		0x970 +#define PCIE_DMA_UNROLL_BASE		0x80000 +#define PCIE_DMA_CTRL			0x008 +#define PCIE_DMA_NUM_WR_CHAN		GENMASK(3, 0) +#define PCIE_DMA_NUM_RD_CHAN		GENMASK(19, 16) +  #define PCIE_PL_CHK_REG_CONTROL_STATUS			0xB20  #define PCIE_PL_CHK_REG_CHK_REG_START			BIT(0)  #define PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS		BIT(1) @@ -215,6 +229,7 @@   * this offset, if atu_base not set.   */  #define DEFAULT_DBI_ATU_OFFSET (0x3 << 20) +#define DEFAULT_DBI_DMA_OFFSET PCIE_DMA_UNROLL_BASE  #define MAX_MSI_IRQS			256  #define MAX_MSI_IRQS_PER_CTRL		32 @@ -226,6 +241,9 @@  #define MAX_IATU_IN			256  #define MAX_IATU_OUT			256 +/* Default eDMA LLP memory size */ +#define DMA_LLP_MEM_SIZE		PAGE_SIZE +  struct dw_pcie;  struct dw_pcie_rp;  struct dw_pcie_ep; @@ -369,6 +387,7 @@ struct dw_pcie {  	int			num_lanes;  	int			link_gen;  	u8			n_fts[2]; +	struct dw_edma_chip	edma;  	struct clk_bulk_data	app_clks[DW_PCIE_NUM_APP_CLKS];  	struct clk_bulk_data	core_clks[DW_PCIE_NUM_CORE_CLKS];  	struct reset_control_bulk_data	app_rsts[DW_PCIE_NUM_APP_RSTS]; @@ -408,6 +427,8 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,  void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);  void dw_pcie_setup(struct dw_pcie *pci);  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);  static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)  { diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 43c27812dd6d..927ae05dc920 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -450,4 +450,3 @@ static struct platform_driver histb_pcie_platform_driver = {  module_platform_driver(histb_pcie_platform_driver);  MODULE_DESCRIPTION("HiSilicon STB PCIe host controller driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 77e5dc7b88ad..a232b04af048 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1534,8 +1534,19 @@ err_deinit:  	return ret;  } +static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct qcom_pcie *pcie = to_qcom_pcie(pci); + +	qcom_ep_reset_assert(pcie); +	phy_power_off(pcie->phy); +	pcie->cfg->ops->deinit(pcie); +} +  static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { -	.host_init = qcom_pcie_host_init, +	.host_init	= qcom_pcie_host_init, +	.host_deinit	= qcom_pcie_host_deinit,  };  /* Qcom IP rev.: 2.1.0	Synopsys IP rev.: 4.01a */ @@ -1817,6 +1828,7 @@ static const struct of_device_id qcom_pcie_match[] = {  	{ .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 },  	{ .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 },  	{ .compatible = "qcom,pcie-ipq8074", .data = &cfg_2_3_3 }, +	{ .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 }, @@ -1826,6 +1838,7 @@ static const struct of_device_id qcom_pcie_match[] = {  	{ .compatible = "qcom,pcie-sdm845", .data = &cfg_2_7_0 },  	{ .compatible = "qcom,pcie-sm8150", .data = &cfg_1_9_0 },  	{ .compatible = "qcom,pcie-sm8250", .data = &cfg_1_9_0 }, +	{ .compatible = "qcom,pcie-sm8350", .data = &cfg_1_9_0 },  	{ .compatible = "qcom,pcie-sm8450-pcie0", .data = &cfg_1_9_0 },  	{ .compatible = "qcom,pcie-sm8450-pcie1", .data = &cfg_1_9_0 },  	{ } diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 02d78a12b6e7..09825b4a075e 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -286,6 +286,7 @@ struct tegra_pcie_dw {  	struct gpio_desc *pex_refclk_sel_gpiod;  	unsigned int pex_rst_irq;  	int ep_state; +	long link_status;  };  static inline struct tegra_pcie_dw *to_tegra_pcie(struct dw_pcie *pci) @@ -449,9 +450,13 @@ static void pex_ep_event_hot_rst_done(struct tegra_pcie_dw *pcie)  static irqreturn_t tegra_pcie_ep_irq_thread(int irq, void *arg)  {  	struct tegra_pcie_dw *pcie = arg; +	struct dw_pcie_ep *ep = &pcie->pci.ep;  	struct dw_pcie *pci = &pcie->pci;  	u32 val, speed; +	if (test_and_clear_bit(0, &pcie->link_status)) +		dw_pcie_ep_linkup(ep); +  	speed = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA) &  		PCI_EXP_LNKSTA_CLS;  	clk_set_rate(pcie->core_clk, pcie_gen_freq[speed - 1]); @@ -498,7 +503,6 @@ static irqreturn_t tegra_pcie_ep_irq_thread(int irq, void *arg)  static irqreturn_t tegra_pcie_ep_hard_irq(int irq, void *arg)  {  	struct tegra_pcie_dw *pcie = arg; -	struct dw_pcie_ep *ep = &pcie->pci.ep;  	int spurious = 1;  	u32 status_l0, status_l1, link_status; @@ -514,7 +518,8 @@ static irqreturn_t tegra_pcie_ep_hard_irq(int irq, void *arg)  			link_status = appl_readl(pcie, APPL_LINK_STATUS);  			if (link_status & APPL_LINK_STATUS_RDLH_LINK_UP) {  				dev_dbg(pcie->dev, "Link is up with Host\n"); -				dw_pcie_ep_linkup(ep); +				set_bit(0, &pcie->link_status); +				return IRQ_WAKE_THREAD;  			}  		} |