diff options
Diffstat (limited to 'drivers/pci/dwc')
| -rw-r--r-- | drivers/pci/dwc/Kconfig | 36 | ||||
| -rw-r--r-- | drivers/pci/dwc/Makefile | 5 | ||||
| -rw-r--r-- | drivers/pci/dwc/pci-dra7xx.c | 293 | ||||
| -rw-r--r-- | drivers/pci/dwc/pci-exynos.c | 14 | ||||
| -rw-r--r-- | drivers/pci/dwc/pci-imx6.c | 228 | ||||
| -rw-r--r-- | drivers/pci/dwc/pci-keystone-dw.c | 2 | ||||
| -rw-r--r-- | drivers/pci/dwc/pci-layerscape.c | 3 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-armada8k.c | 3 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-artpec6.c | 12 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-designware-ep.c | 342 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-designware-host.c | 39 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-designware-plat.c | 1 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-designware.c | 258 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-designware.h | 135 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-hisi.c | 9 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-qcom.c | 2 | ||||
| -rw-r--r-- | drivers/pci/dwc/pcie-spear13xx.c | 3 | 
17 files changed, 1205 insertions, 180 deletions
diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig index d2d2ba5b8a68..b7e15526d676 100644 --- a/drivers/pci/dwc/Kconfig +++ b/drivers/pci/dwc/Kconfig @@ -9,16 +9,44 @@ config PCIE_DW_HOST  	depends on PCI_MSI_IRQ_DOMAIN          select PCIE_DW +config PCIE_DW_EP +	bool +	depends on PCI_ENDPOINT +	select PCIE_DW +  config PCI_DRA7XX  	bool "TI DRA7xx PCIe controller" -	depends on PCI +	depends on (PCI && PCI_MSI_IRQ_DOMAIN) || PCI_ENDPOINT  	depends on OF && HAS_IOMEM && TI_PIPE3 +	help +	 Enables support for the PCIe controller in the DRA7xx SoC. There +	 are two instances of PCIe controller in DRA7xx. This controller can +	 work either as EP or RC. In order to enable host-specific features +	 PCI_DRA7XX_HOST must be selected and in order to enable device- +	 specific features PCI_DRA7XX_EP must be selected. This uses +	 the Designware core. + +if PCI_DRA7XX + +config PCI_DRA7XX_HOST +	bool "PCI DRA7xx Host Mode" +	depends on PCI  	depends on PCI_MSI_IRQ_DOMAIN  	select PCIE_DW_HOST +	default y  	help -	 Enables support for the PCIe controller in the DRA7xx SoC.  There -	 are two instances of PCIe controller in DRA7xx.  This controller can -	 act both as EP and RC.  This reuses the Designware core. +	 Enables support for the PCIe controller in the DRA7xx SoC to work in +	 host mode. + +config PCI_DRA7XX_EP +	bool "PCI DRA7xx Endpoint Mode" +	depends on PCI_ENDPOINT +	select PCIE_DW_EP +	help +	 Enables support for the PCIe controller in the DRA7xx SoC to work in +	 endpoint mode. + +endif  config PCIE_DW_PLAT  	bool "Platform bus based DesignWare PCIe Controller" diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile index a2df13c28798..f31a8596442a 100644 --- a/drivers/pci/dwc/Makefile +++ b/drivers/pci/dwc/Makefile @@ -1,7 +1,10 @@  obj-$(CONFIG_PCIE_DW) += pcie-designware.o  obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o +obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o  obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o -obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o +ifneq ($(filter y,$(CONFIG_PCI_DRA7XX_HOST) $(CONFIG_PCI_DRA7XX_EP)),) +        obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o +endif  obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o  obj-$(CONFIG_PCI_IMX6) += pci-imx6.o  obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c index 0984baff07e3..8decf46cf525 100644 --- a/drivers/pci/dwc/pci-dra7xx.c +++ b/drivers/pci/dwc/pci-dra7xx.c @@ -10,12 +10,14 @@   * published by the Free Software Foundation.   */ +#include <linux/delay.h>  #include <linux/err.h>  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/irqdomain.h>  #include <linux/kernel.h>  #include <linux/init.h> +#include <linux/of_device.h>  #include <linux/of_gpio.h>  #include <linux/of_pci.h>  #include <linux/pci.h> @@ -24,6 +26,8 @@  #include <linux/pm_runtime.h>  #include <linux/resource.h>  #include <linux/types.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h>  #include "pcie-designware.h" @@ -57,6 +61,11 @@  #define	MSI						BIT(4)  #define	LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD) +#define	PCIECTRL_TI_CONF_DEVICE_TYPE			0x0100 +#define	DEVICE_TYPE_EP					0x0 +#define	DEVICE_TYPE_LEG_EP				0x1 +#define	DEVICE_TYPE_RC					0x4 +  #define	PCIECTRL_DRA7XX_CONF_DEVICE_CMD			0x0104  #define	LTSSM_EN					0x1 @@ -66,6 +75,13 @@  #define EXP_CAP_ID_OFFSET				0x70 +#define	PCIECTRL_TI_CONF_INTX_ASSERT			0x0124 +#define	PCIECTRL_TI_CONF_INTX_DEASSERT			0x0128 + +#define	PCIECTRL_TI_CONF_MSI_XMT			0x012c +#define MSI_REQ_GRANT					BIT(0) +#define MSI_VECTOR_SHIFT				7 +  struct dra7xx_pcie {  	struct dw_pcie		*pci;  	void __iomem		*base;		/* DT ti_conf */ @@ -73,6 +89,11 @@ struct dra7xx_pcie {  	struct phy		**phy;  	int			link_gen;  	struct irq_domain	*irq_domain; +	enum dw_pcie_device_mode mode; +}; + +struct dra7xx_pcie_of_data { +	enum dw_pcie_device_mode mode;  };  #define to_dra7xx_pcie(x)	dev_get_drvdata((x)->dev) @@ -88,6 +109,11 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset,  	writel(value, pcie->base + offset);  } +static u64 dra7xx_pcie_cpu_addr_fixup(u64 pci_addr) +{ +	return pci_addr & DRA7XX_CPU_TO_BUS_ADDR; +} +  static int dra7xx_pcie_link_up(struct dw_pcie *pci)  {  	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); @@ -96,9 +122,19 @@ static int dra7xx_pcie_link_up(struct dw_pcie *pci)  	return !!(reg & LINK_UP);  } -static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx) +static void dra7xx_pcie_stop_link(struct dw_pcie *pci)  { -	struct dw_pcie *pci = dra7xx->pci; +	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); +	u32 reg; + +	reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); +	reg &= ~LTSSM_EN; +	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); +} + +static int dra7xx_pcie_establish_link(struct dw_pcie *pci) +{ +	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);  	struct device *dev = pci->dev;  	u32 reg;  	u32 exp_cap_off = EXP_CAP_ID_OFFSET; @@ -132,34 +168,42 @@ static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)  	reg |= LTSSM_EN;  	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); -	return dw_pcie_wait_for_link(pci); +	return 0;  } -static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) +static void dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie *dra7xx)  { -	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, -			   ~INTERRUPTS); -	dra7xx_pcie_writel(dra7xx, -			   PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS);  	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI,  			   ~LEG_EP_INTERRUPTS & ~MSI); -	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, + +	dra7xx_pcie_writel(dra7xx, +			   PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,  			   MSI | LEG_EP_INTERRUPTS);  } +static void dra7xx_pcie_enable_wrapper_interrupts(struct dra7xx_pcie *dra7xx) +{ +	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, +			   ~INTERRUPTS); +	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, +			   INTERRUPTS); +} + +static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) +{ +	dra7xx_pcie_enable_wrapper_interrupts(dra7xx); +	dra7xx_pcie_enable_msi_interrupts(dra7xx); +} +  static void dra7xx_pcie_host_init(struct pcie_port *pp)  {  	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);  	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); -	pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR; -	pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR; -	pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR; -	pp->cfg1_base &= DRA7XX_CPU_TO_BUS_ADDR; -  	dw_pcie_setup_rc(pp); -	dra7xx_pcie_establish_link(dra7xx); +	dra7xx_pcie_establish_link(pci); +	dw_pcie_wait_for_link(pci);  	dw_pcie_msi_init(pp);  	dra7xx_pcie_enable_interrupts(dra7xx);  } @@ -237,6 +281,7 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)  	struct dra7xx_pcie *dra7xx = arg;  	struct dw_pcie *pci = dra7xx->pci;  	struct device *dev = pci->dev; +	struct dw_pcie_ep *ep = &pci->ep;  	u32 reg;  	reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); @@ -273,8 +318,11 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)  	if (reg & LINK_REQ_RST)  		dev_dbg(dev, "Link Request Reset\n"); -	if (reg & LINK_UP_EVT) +	if (reg & LINK_UP_EVT) { +		if (dra7xx->mode == DW_PCIE_EP_TYPE) +			dw_pcie_ep_linkup(ep);  		dev_dbg(dev, "Link-up state change\n"); +	}  	if (reg & CFG_BME_EVT)  		dev_dbg(dev, "CFG 'Bus Master Enable' change\n"); @@ -287,6 +335,94 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)  	return IRQ_HANDLED;  } +static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) +{ +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + +	dra7xx_pcie_enable_wrapper_interrupts(dra7xx); +} + +static void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx) +{ +	dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1); +	mdelay(1); +	dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_DEASSERT, 0x1); +} + +static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx, +				      u8 interrupt_num) +{ +	u32 reg; + +	reg = (interrupt_num - 1) << MSI_VECTOR_SHIFT; +	reg |= MSI_REQ_GRANT; +	dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_MSI_XMT, reg); +} + +static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, +				 enum pci_epc_irq_type type, u8 interrupt_num) +{ +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); + +	switch (type) { +	case PCI_EPC_IRQ_LEGACY: +		dra7xx_pcie_raise_legacy_irq(dra7xx); +		break; +	case PCI_EPC_IRQ_MSI: +		dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num); +		break; +	default: +		dev_err(pci->dev, "UNKNOWN IRQ type\n"); +	} + +	return 0; +} + +static struct dw_pcie_ep_ops pcie_ep_ops = { +	.ep_init = dra7xx_pcie_ep_init, +	.raise_irq = dra7xx_pcie_raise_irq, +}; + +static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, +				     struct platform_device *pdev) +{ +	int ret; +	struct dw_pcie_ep *ep; +	struct resource *res; +	struct device *dev = &pdev->dev; +	struct dw_pcie *pci = dra7xx->pci; + +	ep = &pci->ep; +	ep->ops = &pcie_ep_ops; + +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics"); +	pci->dbi_base = devm_ioremap(dev, res->start, resource_size(res)); +	if (!pci->dbi_base) +		return -ENOMEM; + +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics2"); +	pci->dbi_base2 = devm_ioremap(dev, res->start, resource_size(res)); +	if (!pci->dbi_base2) +		return -ENOMEM; + +	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); + +	ret = dw_pcie_ep_init(ep); +	if (ret) { +		dev_err(dev, "failed to initialize endpoint\n"); +		return ret; +	} + +	return 0; +} +  static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,  				       struct platform_device *pdev)  { @@ -329,6 +465,9 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,  }  static const struct dw_pcie_ops dw_pcie_ops = { +	.cpu_addr_fixup = dra7xx_pcie_cpu_addr_fixup, +	.start_link = dra7xx_pcie_establish_link, +	.stop_link = dra7xx_pcie_stop_link,  	.link_up = dra7xx_pcie_link_up,  }; @@ -371,6 +510,68 @@ err_phy:  	return ret;  } +static const struct dra7xx_pcie_of_data dra7xx_pcie_rc_of_data = { +	.mode = DW_PCIE_RC_TYPE, +}; + +static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = { +	.mode = DW_PCIE_EP_TYPE, +}; + +static const struct of_device_id of_dra7xx_pcie_match[] = { +	{ +		.compatible = "ti,dra7-pcie", +		.data = &dra7xx_pcie_rc_of_data, +	}, +	{ +		.compatible = "ti,dra7-pcie-ep", +		.data = &dra7xx_pcie_ep_of_data, +	}, +	{}, +}; + +/* + * dra7xx_pcie_ep_unaligned_memaccess: workaround for AM572x/AM571x Errata i870 + * @dra7xx: the dra7xx device where the workaround should be applied + * + * Access to the PCIe slave port that are not 32-bit aligned will result + * in incorrect mapping to TLP Address and Byte enable fields. Therefore, + * byte and half-word accesses are not possible to byte offset 0x1, 0x2, or + * 0x3. + * + * To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1. + */ +static int dra7xx_pcie_ep_unaligned_memaccess(struct device *dev) +{ +	int ret; +	struct device_node *np = dev->of_node; +	struct of_phandle_args args; +	struct regmap *regmap; + +	regmap = syscon_regmap_lookup_by_phandle(np, +						 "ti,syscon-unaligned-access"); +	if (IS_ERR(regmap)) { +		dev_dbg(dev, "can't get ti,syscon-unaligned-access\n"); +		return -EINVAL; +	} + +	ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-unaligned-access", +					       2, 0, &args); +	if (ret) { +		dev_err(dev, "failed to parse ti,syscon-unaligned-access\n"); +		return ret; +	} + +	ret = regmap_update_bits(regmap, args.args[0], args.args[1], +				 args.args[1]); +	if (ret) +		dev_err(dev, "failed to enable unaligned access\n"); + +	of_node_put(args.np); + +	return ret; +} +  static int __init dra7xx_pcie_probe(struct platform_device *pdev)  {  	u32 reg; @@ -388,6 +589,16 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)  	struct device_node *np = dev->of_node;  	char name[10];  	struct gpio_desc *reset; +	const struct of_device_id *match; +	const struct dra7xx_pcie_of_data *data; +	enum dw_pcie_device_mode mode; + +	match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev); +	if (!match) +		return -EINVAL; + +	data = (struct dra7xx_pcie_of_data *)match->data; +	mode = (enum dw_pcie_device_mode)data->mode;  	dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL);  	if (!dra7xx) @@ -409,13 +620,6 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)  		return -EINVAL;  	} -	ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler, -			       IRQF_SHARED, "dra7xx-pcie-main", dra7xx); -	if (ret) { -		dev_err(dev, "failed to request irq\n"); -		return ret; -	} -  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf");  	base = devm_ioremap_nocache(dev, res->start, resource_size(res));  	if (!base) @@ -473,9 +677,37 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)  	if (dra7xx->link_gen < 0 || dra7xx->link_gen > 2)  		dra7xx->link_gen = 2; -	ret = dra7xx_add_pcie_port(dra7xx, pdev); -	if (ret < 0) +	switch (mode) { +	case DW_PCIE_RC_TYPE: +		dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE, +				   DEVICE_TYPE_RC); +		ret = dra7xx_add_pcie_port(dra7xx, pdev); +		if (ret < 0) +			goto err_gpio; +		break; +	case DW_PCIE_EP_TYPE: +		dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE, +				   DEVICE_TYPE_EP); + +		ret = dra7xx_pcie_ep_unaligned_memaccess(dev); +		if (ret) +			goto err_gpio; + +		ret = dra7xx_add_pcie_ep(dra7xx, pdev); +		if (ret < 0) +			goto err_gpio; +		break; +	default: +		dev_err(dev, "INVALID device type %d\n", mode); +	} +	dra7xx->mode = mode; + +	ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler, +			       IRQF_SHARED, "dra7xx-pcie-main", dra7xx); +	if (ret) { +		dev_err(dev, "failed to request irq\n");  		goto err_gpio; +	}  	return 0; @@ -496,6 +728,9 @@ static int dra7xx_pcie_suspend(struct device *dev)  	struct dw_pcie *pci = dra7xx->pci;  	u32 val; +	if (dra7xx->mode != DW_PCIE_RC_TYPE) +		return 0; +  	/* clear MSE */  	val = dw_pcie_readl_dbi(pci, PCI_COMMAND);  	val &= ~PCI_COMMAND_MEMORY; @@ -510,6 +745,9 @@ static int dra7xx_pcie_resume(struct device *dev)  	struct dw_pcie *pci = dra7xx->pci;  	u32 val; +	if (dra7xx->mode != DW_PCIE_RC_TYPE) +		return 0; +  	/* set MSE */  	val = dw_pcie_readl_dbi(pci, PCI_COMMAND);  	val |= PCI_COMMAND_MEMORY; @@ -548,11 +786,6 @@ static const struct dev_pm_ops dra7xx_pcie_pm_ops = {  				      dra7xx_pcie_resume_noirq)  }; -static const struct of_device_id of_dra7xx_pcie_match[] = { -	{ .compatible = "ti,dra7-pcie", }, -	{}, -}; -  static struct platform_driver dra7xx_pcie_driver = {  	.driver = {  		.name	= "dra7-pcie", diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c index 44f774c12fb2..546082ad5a3f 100644 --- a/drivers/pci/dwc/pci-exynos.c +++ b/drivers/pci/dwc/pci-exynos.c @@ -521,23 +521,25 @@ static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep)  		exynos_pcie_msi_init(ep);  } -static u32 exynos_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) +static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, +				u32 reg, size_t size)  {  	struct exynos_pcie *ep = to_exynos_pcie(pci);  	u32 val;  	exynos_pcie_sideband_dbi_r_mode(ep, true); -	val = readl(pci->dbi_base + reg); +	dw_pcie_read(base + reg, size, &val);  	exynos_pcie_sideband_dbi_r_mode(ep, false);  	return val;  } -static void exynos_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) +static void exynos_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, +				  u32 reg, size_t size, u32 val)  {  	struct exynos_pcie *ep = to_exynos_pcie(pci);  	exynos_pcie_sideband_dbi_w_mode(ep, true); -	writel(val, pci->dbi_base + reg); +	dw_pcie_write(base + reg, size, val);  	exynos_pcie_sideband_dbi_w_mode(ep, false);  } @@ -644,8 +646,8 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep,  }  static const struct dw_pcie_ops dw_pcie_ops = { -	.readl_dbi = exynos_pcie_readl_dbi, -	.writel_dbi = exynos_pcie_writel_dbi, +	.read_dbi = exynos_pcie_read_dbi, +	.write_dbi = exynos_pcie_write_dbi,  	.link_up = exynos_pcie_link_up,  }; diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c index 801e46cd266d..19a289b8cc94 100644 --- a/drivers/pci/dwc/pci-imx6.c +++ b/drivers/pci/dwc/pci-imx6.c @@ -17,6 +17,7 @@  #include <linux/kernel.h>  #include <linux/mfd/syscon.h>  #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/mfd/syscon/imx7-iomuxc-gpr.h>  #include <linux/module.h>  #include <linux/of_gpio.h>  #include <linux/of_device.h> @@ -27,6 +28,7 @@  #include <linux/signal.h>  #include <linux/types.h>  #include <linux/interrupt.h> +#include <linux/reset.h>  #include "pcie-designware.h" @@ -36,6 +38,7 @@ enum imx6_pcie_variants {  	IMX6Q,  	IMX6SX,  	IMX6QP, +	IMX7D,  };  struct imx6_pcie { @@ -47,6 +50,8 @@ struct imx6_pcie {  	struct clk		*pcie_inbound_axi;  	struct clk		*pcie;  	struct regmap		*iomuxc_gpr; +	struct reset_control	*pciephy_reset; +	struct reset_control	*apps_reset;  	enum imx6_pcie_variants variant;  	u32			tx_deemph_gen1;  	u32			tx_deemph_gen2_3p5db; @@ -56,6 +61,11 @@ struct imx6_pcie {  	int			link_gen;  }; +/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */ +#define PHY_PLL_LOCK_WAIT_MAX_RETRIES	2000 +#define PHY_PLL_LOCK_WAIT_USLEEP_MIN	50 +#define PHY_PLL_LOCK_WAIT_USLEEP_MAX	200 +  /* PCIe Root Complex registers (memory-mapped) */  #define PCIE_RC_LCR				0x7c  #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1	0x1 @@ -242,12 +252,43 @@ static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)  static int imx6q_pcie_abort_handler(unsigned long addr,  		unsigned int fsr, struct pt_regs *regs)  { -	return 0; +	unsigned long pc = instruction_pointer(regs); +	unsigned long instr = *(unsigned long *)pc; +	int reg = (instr >> 12) & 15; + +	/* +	 * If the instruction being executed was a read, +	 * make it look like it read all-ones. +	 */ +	if ((instr & 0x0c100000) == 0x04100000) { +		unsigned long val; + +		if (instr & 0x00400000) +			val = 255; +		else +			val = -1; + +		regs->uregs[reg] = val; +		regs->ARM_pc += 4; +		return 0; +	} + +	if ((instr & 0x0e100090) == 0x00100090) { +		regs->uregs[reg] = -1; +		regs->ARM_pc += 4; +		return 0; +	} + +	return 1;  }  static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)  {  	switch (imx6_pcie->variant) { +	case IMX7D: +		reset_control_assert(imx6_pcie->pciephy_reset); +		reset_control_assert(imx6_pcie->apps_reset); +		break;  	case IMX6SX:  		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,  				   IMX6SX_GPR12_PCIE_TEST_POWERDOWN, @@ -303,11 +344,32 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)  		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,  				   IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);  		break; +	case IMX7D: +		break;  	}  	return ret;  } +static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie) +{ +	u32 val; +	unsigned int retries; +	struct device *dev = imx6_pcie->pci->dev; + +	for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) { +		regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR22, &val); + +		if (val & IMX7D_GPR22_PCIE_PHY_PLL_LOCKED) +			return; + +		usleep_range(PHY_PLL_LOCK_WAIT_USLEEP_MIN, +			     PHY_PLL_LOCK_WAIT_USLEEP_MAX); +	} + +	dev_err(dev, "PCIe PLL lock timeout\n"); +} +  static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)  {  	struct dw_pcie *pci = imx6_pcie->pci; @@ -351,6 +413,10 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)  	}  	switch (imx6_pcie->variant) { +	case IMX7D: +		reset_control_deassert(imx6_pcie->pciephy_reset); +		imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); +		break;  	case IMX6SX:  		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,  				   IMX6SX_GPR5_PCIE_BTNRST_RESET, 0); @@ -377,35 +443,44 @@ err_pcie_bus:  static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)  { -	if (imx6_pcie->variant == IMX6SX) +	switch (imx6_pcie->variant) { +	case IMX7D: +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, +				   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0); +		break; +	case IMX6SX:  		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,  				   IMX6SX_GPR12_PCIE_RX_EQ_MASK,  				   IMX6SX_GPR12_PCIE_RX_EQ_2); +		/* FALLTHROUGH */ +	default: +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, +				   IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, -			IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); +		/* configure constant input signal to the pcie ctrl and phy */ +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, +				   IMX6Q_GPR12_LOS_LEVEL, 9 << 4); + +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, +				   IMX6Q_GPR8_TX_DEEMPH_GEN1, +				   imx6_pcie->tx_deemph_gen1 << 0); +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, +				   IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, +				   imx6_pcie->tx_deemph_gen2_3p5db << 6); +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, +				   IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, +				   imx6_pcie->tx_deemph_gen2_6db << 12); +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, +				   IMX6Q_GPR8_TX_SWING_FULL, +				   imx6_pcie->tx_swing_full << 18); +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, +				   IMX6Q_GPR8_TX_SWING_LOW, +				   imx6_pcie->tx_swing_low << 25); +		break; +	} -	/* configure constant input signal to the pcie ctrl and phy */  	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,  			IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12); -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, -			IMX6Q_GPR12_LOS_LEVEL, 9 << 4); - -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, -			   IMX6Q_GPR8_TX_DEEMPH_GEN1, -			   imx6_pcie->tx_deemph_gen1 << 0); -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, -			   IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, -			   imx6_pcie->tx_deemph_gen2_3p5db << 6); -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, -			   IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, -			   imx6_pcie->tx_deemph_gen2_6db << 12); -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, -			   IMX6Q_GPR8_TX_SWING_FULL, -			   imx6_pcie->tx_swing_full << 18); -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8, -			   IMX6Q_GPR8_TX_SWING_LOW, -			   imx6_pcie->tx_swing_low << 25);  }  static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) @@ -469,8 +544,11 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)  	dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);  	/* Start LTSSM. */ -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, -			IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); +	if (imx6_pcie->variant == IMX7D) +		reset_control_deassert(imx6_pcie->apps_reset); +	else +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, +				   IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);  	ret = imx6_pcie_wait_for_link(imx6_pcie);  	if (ret) @@ -482,29 +560,40 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)  		tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;  		tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;  		dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp); -	} else { -		dev_info(dev, "Link: Gen2 disabled\n"); -	} - -	/* -	 * Start Directed Speed Change so the best possible speed both link -	 * partners support can be negotiated. -	 */ -	tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); -	tmp |= PORT_LOGIC_SPEED_CHANGE; -	dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); -	ret = imx6_pcie_wait_for_speed_change(imx6_pcie); -	if (ret) { -		dev_err(dev, "Failed to bring link up!\n"); -		goto err_reset_phy; -	} +		/* +		 * Start Directed Speed Change so the best possible +		 * speed both link partners support can be negotiated. +		 */ +		tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); +		tmp |= PORT_LOGIC_SPEED_CHANGE; +		dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); + +		if (imx6_pcie->variant != IMX7D) { +			/* +			 * On i.MX7, DIRECT_SPEED_CHANGE behaves differently +			 * from i.MX6 family when no link speed transition +			 * occurs and we go Gen1 -> yep, Gen1. The difference +			 * is that, in such case, it will not be cleared by HW +			 * which will cause the following code to report false +			 * failure. +			 */ + +			ret = imx6_pcie_wait_for_speed_change(imx6_pcie); +			if (ret) { +				dev_err(dev, "Failed to bring link up!\n"); +				goto err_reset_phy; +			} +		} -	/* Make sure link training is finished as well! */ -	ret = imx6_pcie_wait_for_link(imx6_pcie); -	if (ret) { -		dev_err(dev, "Failed to bring link up!\n"); -		goto err_reset_phy; +		/* Make sure link training is finished as well! */ +		ret = imx6_pcie_wait_for_link(imx6_pcie); +		if (ret) { +			dev_err(dev, "Failed to bring link up!\n"); +			goto err_reset_phy; +		} +	} else { +		dev_info(dev, "Link: Gen2 disabled\n");  	}  	tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR); @@ -544,8 +633,8 @@ static struct dw_pcie_host_ops imx6_pcie_host_ops = {  	.host_init = imx6_pcie_host_init,  }; -static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, -				     struct platform_device *pdev) +static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, +			      struct platform_device *pdev)  {  	struct dw_pcie *pci = imx6_pcie->pci;  	struct pcie_port *pp = &pci->pp; @@ -585,7 +674,7 @@ static const struct dw_pcie_ops dw_pcie_ops = {  	.link_up = imx6_pcie_link_up,  }; -static int __init imx6_pcie_probe(struct platform_device *pdev) +static int imx6_pcie_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev;  	struct dw_pcie *pci; @@ -609,10 +698,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)  	imx6_pcie->variant =  		(enum imx6_pcie_variants)of_device_get_match_data(dev); -	/* Added for PCI abort handling */ -	hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0, -		"imprecise external abort"); -  	dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	pci->dbi_base = devm_ioremap_resource(dev, dbi_base);  	if (IS_ERR(pci->dbi_base)) @@ -632,6 +717,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)  			dev_err(dev, "unable to get reset gpio\n");  			return ret;  		} +	} else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) { +		return imx6_pcie->reset_gpio;  	}  	/* Fetch clocks */ @@ -653,13 +740,31 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)  		return PTR_ERR(imx6_pcie->pcie);  	} -	if (imx6_pcie->variant == IMX6SX) { +	switch (imx6_pcie->variant) { +	case IMX6SX:  		imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,  							   "pcie_inbound_axi");  		if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {  			dev_err(dev, "pcie_inbound_axi clock missing or invalid\n");  			return PTR_ERR(imx6_pcie->pcie_inbound_axi);  		} +		break; +	case IMX7D: +		imx6_pcie->pciephy_reset = devm_reset_control_get(dev, +								  "pciephy"); +		if (IS_ERR(imx6_pcie->pciephy_reset)) { +			dev_err(dev, "Failed to get PCIEPHY reset control\n"); +			return PTR_ERR(imx6_pcie->pciephy_reset); +		} + +		imx6_pcie->apps_reset = devm_reset_control_get(dev, "apps"); +		if (IS_ERR(imx6_pcie->apps_reset)) { +			dev_err(dev, "Failed to get PCIE APPS reset control\n"); +			return PTR_ERR(imx6_pcie->apps_reset); +		} +		break; +	default: +		break;  	}  	/* Grab GPR config register range */ @@ -718,6 +823,7 @@ static const struct of_device_id imx6_pcie_of_match[] = {  	{ .compatible = "fsl,imx6q-pcie",  .data = (void *)IMX6Q,  },  	{ .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },  	{ .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, }, +	{ .compatible = "fsl,imx7d-pcie",  .data = (void *)IMX7D,  },  	{},  }; @@ -725,12 +831,24 @@ static struct platform_driver imx6_pcie_driver = {  	.driver = {  		.name	= "imx6q-pcie",  		.of_match_table = imx6_pcie_of_match, +		.suppress_bind_attrs = true,  	}, +	.probe    = imx6_pcie_probe,  	.shutdown = imx6_pcie_shutdown,  };  static int __init imx6_pcie_init(void)  { -	return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe); +	/* +	 * Since probe() can be deferred we need to make sure that +	 * hook_fault_code is not called after __init memory is freed +	 * by kernel and since imx6q_pcie_abort_handler() is a no-op, +	 * we can install the handler here without risking it +	 * accessing some uninitialized driver state. +	 */ +	hook_fault_code(8, imx6q_pcie_abort_handler, SIGBUS, 0, +			"external abort on non-linefetch"); + +	return platform_driver_register(&imx6_pcie_driver);  }  device_initcall(imx6_pcie_init); diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c index 6b396f6b4615..8bc626e640c8 100644 --- a/drivers/pci/dwc/pci-keystone-dw.c +++ b/drivers/pci/dwc/pci-keystone-dw.c @@ -543,7 +543,7 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,  	/* Index 0 is the config reg. space address */  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	pci->dbi_base = devm_ioremap_resource(dev, res); +	pci->dbi_base = devm_pci_remap_cfg_resource(dev, res);  	if (IS_ERR(pci->dbi_base))  		return PTR_ERR(pci->dbi_base); diff --git a/drivers/pci/dwc/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c index c32e392a0ae6..27d638c4e134 100644 --- a/drivers/pci/dwc/pci-layerscape.c +++ b/drivers/pci/dwc/pci-layerscape.c @@ -283,7 +283,7 @@ static int __init ls_pcie_probe(struct platform_device *pdev)  	pcie->pci = pci;  	dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); -	pci->dbi_base = devm_ioremap_resource(dev, dbi_base); +	pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);  	if (IS_ERR(pci->dbi_base))  		return PTR_ERR(pci->dbi_base); @@ -305,6 +305,7 @@ static struct platform_driver ls_pcie_driver = {  	.driver = {  		.name = "layerscape-pcie",  		.of_match_table = ls_pcie_of_match, +		.suppress_bind_attrs = true,  	},  };  builtin_platform_driver_probe(ls_pcie_driver, ls_pcie_probe); diff --git a/drivers/pci/dwc/pcie-armada8k.c b/drivers/pci/dwc/pcie-armada8k.c index f110e3b24a26..495b023042b3 100644 --- a/drivers/pci/dwc/pcie-armada8k.c +++ b/drivers/pci/dwc/pcie-armada8k.c @@ -230,7 +230,7 @@ static int armada8k_pcie_probe(struct platform_device *pdev)  	/* Get the dw-pcie unit configuration/control registers base. */  	base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); -	pci->dbi_base = devm_ioremap_resource(dev, base); +	pci->dbi_base = devm_pci_remap_cfg_resource(dev, base);  	if (IS_ERR(pci->dbi_base)) {  		dev_err(dev, "couldn't remap regs base %p\n", base);  		ret = PTR_ERR(pci->dbi_base); @@ -262,6 +262,7 @@ static struct platform_driver armada8k_pcie_driver = {  	.driver = {  		.name	= "armada8k-pcie",  		.of_match_table = of_match_ptr(armada8k_pcie_of_match), +		.suppress_bind_attrs = true,  	},  };  builtin_platform_driver(armada8k_pcie_driver); diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 6d23683c0892..82a04acc42fd 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -78,6 +78,11 @@ static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u  	regmap_write(artpec6_pcie->regmap, offset, val);  } +static u64 artpec6_pcie_cpu_addr_fixup(u64 pci_addr) +{ +	return pci_addr & ARTPEC6_CPU_TO_BUS_ADDR; +} +  static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)  {  	struct dw_pcie *pci = artpec6_pcie->pci; @@ -142,11 +147,6 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)  	 */  	dw_pcie_writel_dbi(pci, MISC_CONTROL_1_OFF, DBI_RO_WR_EN); -	pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR; -	pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR; -	pp->cfg0_base &= ARTPEC6_CPU_TO_BUS_ADDR; -	pp->cfg1_base &= ARTPEC6_CPU_TO_BUS_ADDR; -  	/* setup root complex */  	dw_pcie_setup_rc(pp); @@ -235,6 +235,7 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,  }  static const struct dw_pcie_ops dw_pcie_ops = { +	.cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup,  };  static int artpec6_pcie_probe(struct platform_device *pdev) @@ -294,6 +295,7 @@ static struct platform_driver artpec6_pcie_driver = {  	.driver = {  		.name	= "artpec6-pcie",  		.of_match_table = artpec6_pcie_of_match, +		.suppress_bind_attrs = true,  	},  };  builtin_platform_driver(artpec6_pcie_driver); diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c new file mode 100644 index 000000000000..398406393f37 --- /dev/null +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -0,0 +1,342 @@ +/** + * Synopsys Designware PCIe Endpoint controller driver + * + * Copyright (C) 2017 Texas Instruments + * Author: Kishon Vijay Abraham I <[email protected]> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/of.h> + +#include "pcie-designware.h" +#include <linux/pci-epc.h> +#include <linux/pci-epf.h> + +void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) +{ +	struct pci_epc *epc = ep->epc; + +	pci_epc_linkup(epc); +} + +static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +{ +	u32 reg; + +	reg = PCI_BASE_ADDRESS_0 + (4 * bar); +	dw_pcie_writel_dbi2(pci, reg, 0x0); +	dw_pcie_writel_dbi(pci, reg, 0x0); +} + +static int dw_pcie_ep_write_header(struct pci_epc *epc, +				   struct pci_epf_header *hdr) +{ +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, hdr->vendorid); +	dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, hdr->deviceid); +	dw_pcie_writeb_dbi(pci, PCI_REVISION_ID, hdr->revid); +	dw_pcie_writeb_dbi(pci, PCI_CLASS_PROG, hdr->progif_code); +	dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, +			   hdr->subclass_code | hdr->baseclass_code << 8); +	dw_pcie_writeb_dbi(pci, PCI_CACHE_LINE_SIZE, +			   hdr->cache_line_size); +	dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_VENDOR_ID, +			   hdr->subsys_vendor_id); +	dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_ID, hdr->subsys_id); +	dw_pcie_writeb_dbi(pci, PCI_INTERRUPT_PIN, +			   hdr->interrupt_pin); + +	return 0; +} + +static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar, +				  dma_addr_t cpu_addr, +				  enum dw_pcie_as_type as_type) +{ +	int ret; +	u32 free_win; +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	free_win = find_first_zero_bit(&ep->ib_window_map, +				       sizeof(ep->ib_window_map)); +	if (free_win >= ep->num_ib_windows) { +		dev_err(pci->dev, "no free inbound window\n"); +		return -EINVAL; +	} + +	ret = dw_pcie_prog_inbound_atu(pci, free_win, bar, cpu_addr, +				       as_type); +	if (ret < 0) { +		dev_err(pci->dev, "Failed to program IB window\n"); +		return ret; +	} + +	ep->bar_to_atu[bar] = free_win; +	set_bit(free_win, &ep->ib_window_map); + +	return 0; +} + +static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, +				   u64 pci_addr, size_t size) +{ +	u32 free_win; +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	free_win = find_first_zero_bit(&ep->ob_window_map, +				       sizeof(ep->ob_window_map)); +	if (free_win >= ep->num_ob_windows) { +		dev_err(pci->dev, "no free outbound window\n"); +		return -EINVAL; +	} + +	dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM, +				  phys_addr, pci_addr, size); + +	set_bit(free_win, &ep->ob_window_map); +	ep->outbound_addr[free_win] = phys_addr; + +	return 0; +} + +static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar) +{ +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +	u32 atu_index = ep->bar_to_atu[bar]; + +	dw_pcie_ep_reset_bar(pci, bar); + +	dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); +	clear_bit(atu_index, &ep->ib_window_map); +} + +static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar, +			      dma_addr_t bar_phys, size_t size, int flags) +{ +	int ret; +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +	enum dw_pcie_as_type as_type; +	u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); + +	if (!(flags & PCI_BASE_ADDRESS_SPACE)) +		as_type = DW_PCIE_AS_MEM; +	else +		as_type = DW_PCIE_AS_IO; + +	ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type); +	if (ret) +		return ret; + +	dw_pcie_writel_dbi2(pci, reg, size - 1); +	dw_pcie_writel_dbi(pci, reg, flags); + +	return 0; +} + +static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr, +			      u32 *atu_index) +{ +	u32 index; + +	for (index = 0; index < ep->num_ob_windows; index++) { +		if (ep->outbound_addr[index] != addr) +			continue; +		*atu_index = index; +		return 0; +	} + +	return -EINVAL; +} + +static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr) +{ +	int ret; +	u32 atu_index; +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	ret = dw_pcie_find_index(ep, addr, &atu_index); +	if (ret < 0) +		return; + +	dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND); +	clear_bit(atu_index, &ep->ob_window_map); +} + +static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr, +			       u64 pci_addr, size_t size) +{ +	int ret; +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	ret = dw_pcie_ep_outbound_atu(ep, addr, pci_addr, size); +	if (ret) { +		dev_err(pci->dev, "failed to enable address\n"); +		return ret; +	} + +	return 0; +} + +static int dw_pcie_ep_get_msi(struct pci_epc *epc) +{ +	int val; +	u32 lower_addr; +	u32 upper_addr; +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	val = dw_pcie_readb_dbi(pci, MSI_MESSAGE_CONTROL); +	val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; + +	lower_addr = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32); +	upper_addr = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32); + +	if (!(lower_addr || upper_addr)) +		return -EINVAL; + +	return val; +} + +static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int) +{ +	int val; +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	val = (encode_int << MSI_CAP_MMC_SHIFT); +	dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val); + +	return 0; +} + +static int dw_pcie_ep_raise_irq(struct pci_epc *epc, +				enum pci_epc_irq_type type, u8 interrupt_num) +{ +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); + +	if (!ep->ops->raise_irq) +		return -EINVAL; + +	return ep->ops->raise_irq(ep, type, interrupt_num); +} + +static void dw_pcie_ep_stop(struct pci_epc *epc) +{ +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	if (!pci->ops->stop_link) +		return; + +	pci->ops->stop_link(pci); +} + +static int dw_pcie_ep_start(struct pci_epc *epc) +{ +	struct dw_pcie_ep *ep = epc_get_drvdata(epc); +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + +	if (!pci->ops->start_link) +		return -EINVAL; + +	return pci->ops->start_link(pci); +} + +static const struct pci_epc_ops epc_ops = { +	.write_header		= dw_pcie_ep_write_header, +	.set_bar		= dw_pcie_ep_set_bar, +	.clear_bar		= dw_pcie_ep_clear_bar, +	.map_addr		= dw_pcie_ep_map_addr, +	.unmap_addr		= dw_pcie_ep_unmap_addr, +	.set_msi		= dw_pcie_ep_set_msi, +	.get_msi		= dw_pcie_ep_get_msi, +	.raise_irq		= dw_pcie_ep_raise_irq, +	.start			= dw_pcie_ep_start, +	.stop			= dw_pcie_ep_stop, +}; + +void dw_pcie_ep_exit(struct dw_pcie_ep *ep) +{ +	struct pci_epc *epc = ep->epc; + +	pci_epc_mem_exit(epc); +} + +int dw_pcie_ep_init(struct dw_pcie_ep *ep) +{ +	int ret; +	void *addr; +	enum pci_barno bar; +	struct pci_epc *epc; +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +	struct device *dev = pci->dev; +	struct device_node *np = dev->of_node; + +	if (!pci->dbi_base || !pci->dbi_base2) { +		dev_err(dev, "dbi_base/deb_base2 is not populated\n"); +		return -EINVAL; +	} + +	ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows); +	if (ret < 0) { +		dev_err(dev, "unable to read *num-ib-windows* property\n"); +		return ret; +	} + +	ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows); +	if (ret < 0) { +		dev_err(dev, "unable to read *num-ob-windows* property\n"); +		return ret; +	} + +	addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows, +			    GFP_KERNEL); +	if (!addr) +		return -ENOMEM; +	ep->outbound_addr = addr; + +	for (bar = BAR_0; bar <= BAR_5; bar++) +		dw_pcie_ep_reset_bar(pci, bar); + +	if (ep->ops->ep_init) +		ep->ops->ep_init(ep); + +	epc = devm_pci_epc_create(dev, &epc_ops); +	if (IS_ERR(epc)) { +		dev_err(dev, "failed to create epc device\n"); +		return PTR_ERR(epc); +	} + +	ret = of_property_read_u8(np, "max-functions", &epc->max_functions); +	if (ret < 0) +		epc->max_functions = 1; + +	ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size); +	if (ret < 0) { +		dev_err(dev, "Failed to initialize address space\n"); +		return ret; +	} + +	ep->epc = epc; +	epc_set_drvdata(epc, ep); +	dw_pcie_setup(pci); + +	return 0; +} diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c index 5ba334938b52..28ed32ba4f1b 100644 --- a/drivers/pci/dwc/pcie-designware-host.c +++ b/drivers/pci/dwc/pcie-designware-host.c @@ -56,24 +56,25 @@ static struct irq_chip dw_msi_irq_chip = {  /* MSI int handler */  irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)  { -	unsigned long val; +	u32 val;  	int i, pos, irq;  	irqreturn_t ret = IRQ_NONE;  	for (i = 0; i < MAX_MSI_CTRLS; i++) {  		dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, -				    (u32 *)&val); -		if (val) { -			ret = IRQ_HANDLED; -			pos = 0; -			while ((pos = find_next_bit(&val, 32, pos)) != 32) { -				irq = irq_find_mapping(pp->irq_domain, -						       i * 32 + pos); -				dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + -						    i * 12, 4, 1 << pos); -				generic_handle_irq(irq); -				pos++; -			} +				    &val); +		if (!val) +			continue; + +		ret = IRQ_HANDLED; +		pos = 0; +		while ((pos = find_next_bit((unsigned long *) &val, 32, +					    pos)) != 32) { +			irq = irq_find_mapping(pp->irq_domain, i * 32 + pos); +			dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, +					    4, 1 << pos); +			generic_handle_irq(irq); +			pos++;  		}  	} @@ -338,8 +339,9 @@ int dw_pcie_host_init(struct pcie_port *pp)  	}  	if (!pci->dbi_base) { -		pci->dbi_base = devm_ioremap(dev, pp->cfg->start, -					resource_size(pp->cfg)); +		pci->dbi_base = devm_pci_remap_cfgspace(dev, +						pp->cfg->start, +						resource_size(pp->cfg));  		if (!pci->dbi_base) {  			dev_err(dev, "error with ioremap\n");  			ret = -ENOMEM; @@ -350,8 +352,8 @@ int dw_pcie_host_init(struct pcie_port *pp)  	pp->mem_base = pp->mem->start;  	if (!pp->va_cfg0_base) { -		pp->va_cfg0_base = devm_ioremap(dev, pp->cfg0_base, -						pp->cfg0_size); +		pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, +					pp->cfg0_base, pp->cfg0_size);  		if (!pp->va_cfg0_base) {  			dev_err(dev, "error with ioremap in function\n");  			ret = -ENOMEM; @@ -360,7 +362,8 @@ int dw_pcie_host_init(struct pcie_port *pp)  	}  	if (!pp->va_cfg1_base) { -		pp->va_cfg1_base = devm_ioremap(dev, pp->cfg1_base, +		pp->va_cfg1_base = devm_pci_remap_cfgspace(dev, +						pp->cfg1_base,  						pp->cfg1_size);  		if (!pp->va_cfg1_base) {  			dev_err(dev, "error with ioremap\n"); diff --git a/drivers/pci/dwc/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c index f20d494922ab..32091b32f6e1 100644 --- a/drivers/pci/dwc/pcie-designware-plat.c +++ b/drivers/pci/dwc/pcie-designware-plat.c @@ -133,6 +133,7 @@ static struct platform_driver dw_plat_pcie_driver = {  	.driver = {  		.name	= "dw-pcie",  		.of_match_table = dw_plat_pcie_of_match, +		.suppress_bind_attrs = true,  	},  	.probe = dw_plat_pcie_probe,  }; diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c index 7e1fb7d6643c..0e03af279259 100644 --- a/drivers/pci/dwc/pcie-designware.c +++ b/drivers/pci/dwc/pcie-designware.c @@ -61,91 +61,253 @@ int dw_pcie_write(void __iomem *addr, int size, u32 val)  	return PCIBIOS_SUCCESSFUL;  } -u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) +u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, +		       size_t size)  { -	if (pci->ops->readl_dbi) -		return pci->ops->readl_dbi(pci, reg); +	int ret; +	u32 val; -	return readl(pci->dbi_base + reg); +	if (pci->ops->read_dbi) +		return pci->ops->read_dbi(pci, base, reg, size); + +	ret = dw_pcie_read(base + reg, size, &val); +	if (ret) +		dev_err(pci->dev, "read DBI address failed\n"); + +	return val;  } -void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) +void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, +			 size_t size, u32 val)  { -	if (pci->ops->writel_dbi) -		pci->ops->writel_dbi(pci, reg, val); -	else -		writel(val, pci->dbi_base + reg); +	int ret; + +	if (pci->ops->write_dbi) { +		pci->ops->write_dbi(pci, base, reg, size, val); +		return; +	} + +	ret = dw_pcie_write(base + reg, size, val); +	if (ret) +		dev_err(pci->dev, "write DBI address failed\n");  } -static u32 dw_pcie_readl_unroll(struct dw_pcie *pci, u32 index, u32 reg) +static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg)  {  	u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);  	return dw_pcie_readl_dbi(pci, offset + reg);  } -static void dw_pcie_writel_unroll(struct dw_pcie *pci, u32 index, u32 reg, -				  u32 val) +static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg, +				     u32 val)  {  	u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);  	dw_pcie_writel_dbi(pci, offset + reg, val);  } +void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index, int type, +				      u64 cpu_addr, u64 pci_addr, u32 size) +{ +	u32 retries, val; + +	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, +				 lower_32_bits(cpu_addr)); +	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, +				 upper_32_bits(cpu_addr)); +	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT, +				 lower_32_bits(cpu_addr + size - 1)); +	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, +				 lower_32_bits(pci_addr)); +	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, +				 upper_32_bits(pci_addr)); +	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, +				 type); +	dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, +				 PCIE_ATU_ENABLE); + +	/* +	 * Make sure ATU enable takes effect before any subsequent config +	 * and I/O accesses. +	 */ +	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { +		val = dw_pcie_readl_ob_unroll(pci, index, +					      PCIE_ATU_UNR_REGION_CTRL2); +		if (val & PCIE_ATU_ENABLE) +			return; + +		usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); +	} +	dev_err(pci->dev, "outbound iATU is not being enabled\n"); +} +  void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,  			       u64 cpu_addr, u64 pci_addr, u32 size)  {  	u32 retries, val; +	if (pci->ops->cpu_addr_fixup) +		cpu_addr = pci->ops->cpu_addr_fixup(cpu_addr); +  	if (pci->iatu_unroll_enabled) { -		dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, -				      lower_32_bits(cpu_addr)); -		dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, -				      upper_32_bits(cpu_addr)); -		dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LIMIT, -				      lower_32_bits(cpu_addr + size - 1)); -		dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, -				      lower_32_bits(pci_addr)); -		dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, -				      upper_32_bits(pci_addr)); -		dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, -				      type); -		dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, -				      PCIE_ATU_ENABLE); -	} else { -		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, -				   PCIE_ATU_REGION_OUTBOUND | index); -		dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, -				   lower_32_bits(cpu_addr)); -		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, -				   upper_32_bits(cpu_addr)); -		dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, -				   lower_32_bits(cpu_addr + size - 1)); -		dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, -				   lower_32_bits(pci_addr)); -		dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, -				   upper_32_bits(pci_addr)); -		dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); -		dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE); +		dw_pcie_prog_outbound_atu_unroll(pci, index, type, cpu_addr, +						 pci_addr, size); +		return;  	} +	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, +			   PCIE_ATU_REGION_OUTBOUND | index); +	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, +			   lower_32_bits(cpu_addr)); +	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, +			   upper_32_bits(cpu_addr)); +	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, +			   lower_32_bits(cpu_addr + size - 1)); +	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, +			   lower_32_bits(pci_addr)); +	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, +			   upper_32_bits(pci_addr)); +	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); +	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE); +  	/*  	 * Make sure ATU enable takes effect before any subsequent config  	 * and I/O accesses.  	 */  	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { -		if (pci->iatu_unroll_enabled) -			val = dw_pcie_readl_unroll(pci, index, -						   PCIE_ATU_UNR_REGION_CTRL2); -		else -			val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); - +		val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);  		if (val == PCIE_ATU_ENABLE)  			return;  		usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);  	} -	dev_err(pci->dev, "iATU is not being enabled\n"); +	dev_err(pci->dev, "outbound iATU is not being enabled\n"); +} + +static u32 dw_pcie_readl_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg) +{ +	u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); + +	return dw_pcie_readl_dbi(pci, offset + reg); +} + +static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg, +				     u32 val) +{ +	u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index); + +	dw_pcie_writel_dbi(pci, offset + reg, val); +} + +int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index, int bar, +				    u64 cpu_addr, enum dw_pcie_as_type as_type) +{ +	int type; +	u32 retries, val; + +	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, +				 lower_32_bits(cpu_addr)); +	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, +				 upper_32_bits(cpu_addr)); + +	switch (as_type) { +	case DW_PCIE_AS_MEM: +		type = PCIE_ATU_TYPE_MEM; +		break; +	case DW_PCIE_AS_IO: +		type = PCIE_ATU_TYPE_IO; +		break; +	default: +		return -EINVAL; +	} + +	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, type); +	dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, +				 PCIE_ATU_ENABLE | +				 PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); + +	/* +	 * Make sure ATU enable takes effect before any subsequent config +	 * and I/O accesses. +	 */ +	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { +		val = dw_pcie_readl_ib_unroll(pci, index, +					      PCIE_ATU_UNR_REGION_CTRL2); +		if (val & PCIE_ATU_ENABLE) +			return 0; + +		usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); +	} +	dev_err(pci->dev, "inbound iATU is not being enabled\n"); + +	return -EBUSY; +} + +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar, +			     u64 cpu_addr, enum dw_pcie_as_type as_type) +{ +	int type; +	u32 retries, val; + +	if (pci->iatu_unroll_enabled) +		return dw_pcie_prog_inbound_atu_unroll(pci, index, bar, +						       cpu_addr, as_type); + +	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | +			   index); +	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr)); +	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr)); + +	switch (as_type) { +	case DW_PCIE_AS_MEM: +		type = PCIE_ATU_TYPE_MEM; +		break; +	case DW_PCIE_AS_IO: +		type = PCIE_ATU_TYPE_IO; +		break; +	default: +		return -EINVAL; +	} + +	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type); +	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE +			   | PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); + +	/* +	 * Make sure ATU enable takes effect before any subsequent config +	 * and I/O accesses. +	 */ +	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { +		val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); +		if (val & PCIE_ATU_ENABLE) +			return 0; + +		usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); +	} +	dev_err(pci->dev, "inbound iATU is not being enabled\n"); + +	return -EBUSY; +} + +void dw_pcie_disable_atu(struct dw_pcie *pci, int index, +			 enum dw_pcie_region_type type) +{ +	int region; + +	switch (type) { +	case DW_PCIE_REGION_INBOUND: +		region = PCIE_ATU_REGION_INBOUND; +		break; +	case DW_PCIE_REGION_OUTBOUND: +		region = PCIE_ATU_REGION_OUTBOUND; +		break; +	default: +		return; +	} + +	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index); +	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~PCIE_ATU_ENABLE);  }  int dw_pcie_wait_for_link(struct dw_pcie *pci) diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h index cd3b8713fe50..c6a840575796 100644 --- a/drivers/pci/dwc/pcie-designware.h +++ b/drivers/pci/dwc/pcie-designware.h @@ -18,6 +18,9 @@  #include <linux/msi.h>  #include <linux/pci.h> +#include <linux/pci-epc.h> +#include <linux/pci-epf.h> +  /* Parameters for the waiting for link up routine */  #define LINK_WAIT_MAX_RETRIES		10  #define LINK_WAIT_USLEEP_MIN		90000 @@ -89,6 +92,16 @@  #define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region)	\  			((0x3 << 20) | ((region) << 9)) +#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region)				\ +			((0x3 << 20) | ((region) << 9) | (0x1 << 8)) + +#define MSI_MESSAGE_CONTROL		0x52 +#define MSI_CAP_MMC_SHIFT		1 +#define MSI_CAP_MME_SHIFT		4 +#define MSI_CAP_MME_MASK		(7 << MSI_CAP_MME_SHIFT) +#define MSI_MESSAGE_ADDR_L32		0x54 +#define MSI_MESSAGE_ADDR_U32		0x58 +  /*   * Maximum number of MSI IRQs can be 256 per controller. But keep   * it 32 as of now. Probably we will never need more than 32. If needed, @@ -99,6 +112,20 @@  struct pcie_port;  struct dw_pcie; +struct dw_pcie_ep; + +enum dw_pcie_region_type { +	DW_PCIE_REGION_UNKNOWN, +	DW_PCIE_REGION_INBOUND, +	DW_PCIE_REGION_OUTBOUND, +}; + +enum dw_pcie_device_mode { +	DW_PCIE_UNKNOWN_TYPE, +	DW_PCIE_EP_TYPE, +	DW_PCIE_LEG_EP_TYPE, +	DW_PCIE_RC_TYPE, +};  struct dw_pcie_host_ops {  	int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); @@ -142,35 +169,116 @@ struct pcie_port {  	DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);  }; +enum dw_pcie_as_type { +	DW_PCIE_AS_UNKNOWN, +	DW_PCIE_AS_MEM, +	DW_PCIE_AS_IO, +}; + +struct dw_pcie_ep_ops { +	void	(*ep_init)(struct dw_pcie_ep *ep); +	int	(*raise_irq)(struct dw_pcie_ep *ep, enum pci_epc_irq_type type, +			     u8 interrupt_num); +}; + +struct dw_pcie_ep { +	struct pci_epc		*epc; +	struct dw_pcie_ep_ops	*ops; +	phys_addr_t		phys_base; +	size_t			addr_size; +	u8			bar_to_atu[6]; +	phys_addr_t		*outbound_addr; +	unsigned long		ib_window_map; +	unsigned long		ob_window_map; +	u32			num_ib_windows; +	u32			num_ob_windows; +}; +  struct dw_pcie_ops { -	u32	(*readl_dbi)(struct dw_pcie *pcie, u32 reg); -	void	(*writel_dbi)(struct dw_pcie *pcie, u32 reg, u32 val); +	u64	(*cpu_addr_fixup)(u64 cpu_addr); +	u32	(*read_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, +			    size_t size); +	void	(*write_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, +			     size_t size, u32 val);  	int	(*link_up)(struct dw_pcie *pcie); +	int	(*start_link)(struct dw_pcie *pcie); +	void	(*stop_link)(struct dw_pcie *pcie);  };  struct dw_pcie {  	struct device		*dev;  	void __iomem		*dbi_base; +	void __iomem		*dbi_base2;  	u32			num_viewport;  	u8			iatu_unroll_enabled;  	struct pcie_port	pp; +	struct dw_pcie_ep	ep;  	const struct dw_pcie_ops *ops;  };  #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) +#define to_dw_pcie_from_ep(endpoint)   \ +		container_of((endpoint), struct dw_pcie, ep) +  int dw_pcie_read(void __iomem *addr, int size, u32 *val);  int dw_pcie_write(void __iomem *addr, int size, u32 val); -u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg); -void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val); +u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, +		       size_t size); +void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, +			 size_t size, u32 val);  int dw_pcie_link_up(struct dw_pcie *pci);  int dw_pcie_wait_for_link(struct dw_pcie *pci);  void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,  			       int type, u64 cpu_addr, u64 pci_addr,  			       u32 size); +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar, +			     u64 cpu_addr, enum dw_pcie_as_type as_type); +void dw_pcie_disable_atu(struct dw_pcie *pci, int index, +			 enum dw_pcie_region_type type);  void dw_pcie_setup(struct dw_pcie *pci); +static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) +{ +	__dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x4, val); +} + +static inline u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) +{ +	return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x4); +} + +static inline void dw_pcie_writew_dbi(struct dw_pcie *pci, u32 reg, u16 val) +{ +	__dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x2, val); +} + +static inline u16 dw_pcie_readw_dbi(struct dw_pcie *pci, u32 reg) +{ +	return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x2); +} + +static inline void dw_pcie_writeb_dbi(struct dw_pcie *pci, u32 reg, u8 val) +{ +	__dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x1, val); +} + +static inline u8 dw_pcie_readb_dbi(struct dw_pcie *pci, u32 reg) +{ +	return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x1); +} + +static inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, u32 reg, u32 val) +{ +	__dw_pcie_write_dbi(pci, pci->dbi_base2, reg, 0x4, val); +} + +static inline u32 dw_pcie_readl_dbi2(struct dw_pcie *pci, u32 reg) +{ +	return __dw_pcie_read_dbi(pci, pci->dbi_base2, reg, 0x4); +} +  #ifdef CONFIG_PCIE_DW_HOST  irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);  void dw_pcie_msi_init(struct pcie_port *pp); @@ -195,4 +303,23 @@ static inline int dw_pcie_host_init(struct pcie_port *pp)  	return 0;  }  #endif + +#ifdef CONFIG_PCIE_DW_EP +void dw_pcie_ep_linkup(struct dw_pcie_ep *ep); +int dw_pcie_ep_init(struct dw_pcie_ep *ep); +void dw_pcie_ep_exit(struct dw_pcie_ep *ep); +#else +static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) +{ +} + +static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep) +{ +	return 0; +} + +static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep) +{ +} +#endif  #endif /* _PCIE_DESIGNWARE_H */ diff --git a/drivers/pci/dwc/pcie-hisi.c b/drivers/pci/dwc/pcie-hisi.c index cf9d6a9d9fd4..e51acee0ddf3 100644 --- a/drivers/pci/dwc/pcie-hisi.c +++ b/drivers/pci/dwc/pcie-hisi.c @@ -99,7 +99,7 @@ static int hisi_pcie_init(struct pci_config_window *cfg)  		return -ENOMEM;  	} -	reg_base = devm_ioremap(dev, res->start, resource_size(res)); +	reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res));  	if (!reg_base)  		return -ENOMEM; @@ -296,10 +296,9 @@ static int hisi_pcie_probe(struct platform_device *pdev)  	}  	reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); -	pci->dbi_base = devm_ioremap_resource(dev, reg); +	pci->dbi_base = devm_pci_remap_cfg_resource(dev, reg);  	if (IS_ERR(pci->dbi_base))  		return PTR_ERR(pci->dbi_base); -  	platform_set_drvdata(pdev, hisi_pcie);  	ret = hisi_add_pcie_port(hisi_pcie, pdev); @@ -334,6 +333,7 @@ static struct platform_driver hisi_pcie_driver = {  	.driver = {  		   .name = "hisi-pcie",  		   .of_match_table = hisi_pcie_of_match, +		   .suppress_bind_attrs = true,  	},  };  builtin_platform_driver(hisi_pcie_driver); @@ -360,7 +360,7 @@ static int hisi_pcie_platform_init(struct pci_config_window *cfg)  		return -EINVAL;  	} -	reg_base = devm_ioremap(dev, res->start, resource_size(res)); +	reg_base = devm_pci_remap_cfgspace(dev, res->start, resource_size(res));  	if (!reg_base)  		return -ENOMEM; @@ -395,6 +395,7 @@ static struct platform_driver hisi_pcie_almost_ecam_driver = {  	.driver = {  		   .name = "hisi-pcie-almost-ecam",  		   .of_match_table = hisi_pcie_almost_ecam_of_match, +		   .suppress_bind_attrs = true,  	},  };  builtin_platform_driver(hisi_pcie_almost_ecam_driver); diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index 67eb7f5926dd..5bf23d432fdb 100644 --- a/drivers/pci/dwc/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -700,7 +700,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		return PTR_ERR(pcie->parf);  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); -	pci->dbi_base = devm_ioremap_resource(dev, res); +	pci->dbi_base = devm_pci_remap_cfg_resource(dev, res);  	if (IS_ERR(pci->dbi_base))  		return PTR_ERR(pci->dbi_base); diff --git a/drivers/pci/dwc/pcie-spear13xx.c b/drivers/pci/dwc/pcie-spear13xx.c index eaa4ea8e2ea4..8ff36b3dbbdf 100644 --- a/drivers/pci/dwc/pcie-spear13xx.c +++ b/drivers/pci/dwc/pcie-spear13xx.c @@ -273,7 +273,7 @@ static int spear13xx_pcie_probe(struct platform_device *pdev)  	}  	dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); -	pci->dbi_base = devm_ioremap_resource(dev, dbi_base); +	pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);  	if (IS_ERR(pci->dbi_base)) {  		dev_err(dev, "couldn't remap dbi base %p\n", dbi_base);  		ret = PTR_ERR(pci->dbi_base); @@ -308,6 +308,7 @@ static struct platform_driver spear13xx_pcie_driver = {  	.driver = {  		.name	= "spear-pcie",  		.of_match_table = of_match_ptr(spear13xx_pcie_of_match), +		.suppress_bind_attrs = true,  	},  };  |