summaryrefslogtreecommitdiff
path: root/drivers/pci/dwc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/dwc')
-rw-r--r--drivers/pci/dwc/Kconfig70
-rw-r--r--drivers/pci/dwc/Makefile6
-rw-r--r--drivers/pci/dwc/pci-dra7xx.c47
-rw-r--r--drivers/pci/dwc/pci-exynos.c227
-rw-r--r--drivers/pci/dwc/pci-imx6.c5
-rw-r--r--drivers/pci/dwc/pci-keystone-dw.c6
-rw-r--r--drivers/pci/dwc/pci-keystone.c10
-rw-r--r--drivers/pci/dwc/pci-keystone.h6
-rw-r--r--drivers/pci/dwc/pci-layerscape.c5
-rw-r--r--drivers/pci/dwc/pcie-armada8k.c5
-rw-r--r--drivers/pci/dwc/pcie-artpec6.c475
-rw-r--r--drivers/pci/dwc/pcie-designware-ep.c140
-rw-r--r--drivers/pci/dwc/pcie-designware-host.c20
-rw-r--r--drivers/pci/dwc/pcie-designware-plat.c5
-rw-r--r--drivers/pci/dwc/pcie-designware.c7
-rw-r--r--drivers/pci/dwc/pcie-designware.h41
-rw-r--r--drivers/pci/dwc/pcie-hisi.c5
-rw-r--r--drivers/pci/dwc/pcie-histb.c5
-rw-r--r--drivers/pci/dwc/pcie-kirin.c5
-rw-r--r--drivers/pci/dwc/pcie-qcom.c14
-rw-r--r--drivers/pci/dwc/pcie-spear13xx.c5
21 files changed, 654 insertions, 455 deletions
diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig
index 113e09440f85..0f666b1ce289 100644
--- a/drivers/pci/dwc/Kconfig
+++ b/drivers/pci/dwc/Kconfig
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
menu "DesignWare PCI Core Support"
config PCIE_DW
@@ -15,39 +17,38 @@ config PCIE_DW_EP
select PCIE_DW
config PCI_DRA7XX
- bool "TI DRA7xx PCIe controller"
- depends on SOC_DRA7XX || COMPILE_TEST
- 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
+ bool
config PCI_DRA7XX_HOST
- bool "PCI DRA7xx Host Mode"
- depends on PCI
- depends on PCI_MSI_IRQ_DOMAIN
+ bool "TI DRA7xx PCIe controller Host Mode"
+ depends on SOC_DRA7XX || COMPILE_TEST
+ depends on PCI && PCI_MSI_IRQ_DOMAIN
+ depends on OF && HAS_IOMEM && TI_PIPE3
select PCIE_DW_HOST
+ select PCI_DRA7XX
default y
help
- Enables support for the PCIe controller in the DRA7xx SoC to work in
- host mode.
+ Enables support for the PCIe controller in the DRA7xx SoC to work in
+ host mode. 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.
config PCI_DRA7XX_EP
- bool "PCI DRA7xx Endpoint Mode"
+ bool "TI DRA7xx PCIe controller Endpoint Mode"
+ depends on SOC_DRA7XX || COMPILE_TEST
depends on PCI_ENDPOINT
+ depends on OF && HAS_IOMEM && TI_PIPE3
select PCIE_DW_EP
+ select PCI_DRA7XX
help
- Enables support for the PCIe controller in the DRA7xx SoC to work in
- endpoint mode.
-
-endif
+ Enables support for the PCIe controller in the DRA7xx SoC to work in
+ endpoint mode. 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.
config PCIE_DW_PLAT
bool "Platform bus based DesignWare PCIe Controller"
@@ -149,15 +150,28 @@ config PCIE_ARMADA_8K
DesignWare core functions to implement the driver.
config PCIE_ARTPEC6
- bool "Axis ARTPEC-6 PCIe controller"
- depends on PCI
+ bool
+
+config PCIE_ARTPEC6_HOST
+ bool "Axis ARTPEC-6 PCIe controller Host Mode"
depends on MACH_ARTPEC6
- depends on PCI_MSI_IRQ_DOMAIN
+ depends on PCI && PCI_MSI_IRQ_DOMAIN
select PCIEPORTBUS
select PCIE_DW_HOST
+ select PCIE_ARTPEC6
+ help
+ Enables support for the PCIe controller in the ARTPEC-6 SoC to work in
+ host mode. This uses the DesignWare core.
+
+config PCIE_ARTPEC6_EP
+ bool "Axis ARTPEC-6 PCIe controller Endpoint Mode"
+ depends on MACH_ARTPEC6
+ depends on PCI_ENDPOINT
+ select PCIE_DW_EP
+ select PCIE_ARTPEC6
help
- Say Y here to enable PCIe controller support on Axis ARTPEC-6
- SoCs. This PCIe controller uses the DesignWare core.
+ Enables support for the PCIe controller in the ARTPEC-6 SoC to work in
+ endpoint mode. This uses the DesignWare core.
config PCIE_KIRIN
depends on OF && ARM64
diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile
index 41ba499c96ee..5d2ce72c7a52 100644
--- a/drivers/pci/dwc/Makefile
+++ b/drivers/pci/dwc/Makefile
@@ -3,9 +3,7 @@ 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
-ifneq ($(filter y,$(CONFIG_PCI_DRA7XX_HOST) $(CONFIG_PCI_DRA7XX_EP)),)
- obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
-endif
+obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
@@ -27,4 +25,6 @@ obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o
# ARM64 and use internal ifdefs to only build the pieces we need
# depending on whether ACPI, the DT driver, or both are enabled.
+ifdef CONFIG_PCI
obj-$(CONFIG_ARM64) += pcie-hisi.o
+endif
diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index e77a4ceed74c..ed8558d638e5 100644
--- a/drivers/pci/dwc/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs
*
* Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
@@ -110,7 +107,7 @@ 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)
+static u64 dra7xx_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr)
{
return pci_addr & DRA7XX_CPU_TO_BUS_ADDR;
}
@@ -226,6 +223,7 @@ static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
static const struct irq_domain_ops intx_domain_ops = {
.map = dra7xx_pcie_intx_map,
+ .xlate = pci_irqd_intx_xlate,
};
static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
@@ -256,7 +254,8 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
struct dra7xx_pcie *dra7xx = arg;
struct dw_pcie *pci = dra7xx->pci;
struct pcie_port *pp = &pci->pp;
- u32 reg;
+ unsigned long reg;
+ u32 virq, bit;
reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI);
@@ -268,8 +267,11 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
case INTB:
case INTC:
case INTD:
- generic_handle_irq(irq_find_mapping(dra7xx->irq_domain,
- ffs(reg)));
+ for_each_set_bit(bit, &reg, PCI_NUM_INTX) {
+ virq = irq_find_mapping(dra7xx->irq_domain, bit);
+ if (virq)
+ generic_handle_irq(virq);
+ }
break;
}
@@ -337,15 +339,6 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
-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 void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
@@ -375,7 +368,7 @@ static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx,
dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_MSI_XMT, reg);
}
-static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep,
+static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
@@ -470,6 +463,8 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
if (!pci->dbi_base)
return -ENOMEM;
+ pp->ops = &dra7xx_pcie_host_ops;
+
ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(dev, "failed to initialize host\n");
@@ -599,7 +594,6 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
void __iomem *base;
struct resource *res;
struct dw_pcie *pci;
- struct pcie_port *pp;
struct dra7xx_pcie *dra7xx;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
@@ -627,9 +621,6 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
pci->dev = dev;
pci->ops = &dw_pcie_ops;
- pp = &pci->pp;
- pp->ops = &dra7xx_pcie_host_ops;
-
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "missing IRQ resource: %d\n", irq);
@@ -705,6 +696,11 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
switch (mode) {
case DW_PCIE_RC_TYPE:
+ if (!IS_ENABLED(CONFIG_PCI_DRA7XX_HOST)) {
+ ret = -ENODEV;
+ goto err_gpio;
+ }
+
dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
DEVICE_TYPE_RC);
ret = dra7xx_add_pcie_port(dra7xx, pdev);
@@ -712,6 +708,11 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
goto err_gpio;
break;
case DW_PCIE_EP_TYPE:
+ if (!IS_ENABLED(CONFIG_PCI_DRA7XX_EP)) {
+ ret = -ENODEV;
+ goto err_gpio;
+ }
+
dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
DEVICE_TYPE_EP);
@@ -810,7 +811,7 @@ static int dra7xx_pcie_resume_noirq(struct device *dev)
}
#endif
-void dra7xx_pcie_shutdown(struct platform_device *pdev)
+static void dra7xx_pcie_shutdown(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c
index 5596fdedbb94..ca6278113936 100644
--- a/drivers/pci/dwc/pci-exynos.c
+++ b/drivers/pci/dwc/pci-exynos.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Samsung EXYNOS SoCs
*
@@ -5,10 +6,6 @@
* http://www.samsung.com
*
* Author: Jingoo Han <jg1.han@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/clk.h>
@@ -55,49 +52,8 @@
#define PCIE_ELBI_SLV_ARMISC 0x120
#define PCIE_ELBI_SLV_DBI_ENABLE BIT(21)
-/* PCIe Purple registers */
-#define PCIE_PHY_GLOBAL_RESET 0x000
-#define PCIE_PHY_COMMON_RESET 0x004
-#define PCIE_PHY_CMN_REG 0x008
-#define PCIE_PHY_MAC_RESET 0x00c
-#define PCIE_PHY_PLL_LOCKED 0x010
-#define PCIE_PHY_TRSVREG_RESET 0x020
-#define PCIE_PHY_TRSV_RESET 0x024
-
-/* PCIe PHY registers */
-#define PCIE_PHY_IMPEDANCE 0x004
-#define PCIE_PHY_PLL_DIV_0 0x008
-#define PCIE_PHY_PLL_BIAS 0x00c
-#define PCIE_PHY_DCC_FEEDBACK 0x014
-#define PCIE_PHY_PLL_DIV_1 0x05c
-#define PCIE_PHY_COMMON_POWER 0x064
-#define PCIE_PHY_COMMON_PD_CMN BIT(3)
-#define PCIE_PHY_TRSV0_EMP_LVL 0x084
-#define PCIE_PHY_TRSV0_DRV_LVL 0x088
-#define PCIE_PHY_TRSV0_RXCDR 0x0ac
-#define PCIE_PHY_TRSV0_POWER 0x0c4
-#define PCIE_PHY_TRSV0_PD_TSV BIT(7)
-#define PCIE_PHY_TRSV0_LVCC 0x0dc
-#define PCIE_PHY_TRSV1_EMP_LVL 0x144
-#define PCIE_PHY_TRSV1_RXCDR 0x16c
-#define PCIE_PHY_TRSV1_POWER 0x184
-#define PCIE_PHY_TRSV1_PD_TSV BIT(7)
-#define PCIE_PHY_TRSV1_LVCC 0x19c
-#define PCIE_PHY_TRSV2_EMP_LVL 0x204
-#define PCIE_PHY_TRSV2_RXCDR 0x22c
-#define PCIE_PHY_TRSV2_POWER 0x244
-#define PCIE_PHY_TRSV2_PD_TSV BIT(7)
-#define PCIE_PHY_TRSV2_LVCC 0x25c
-#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4
-#define PCIE_PHY_TRSV3_RXCDR 0x2ec
-#define PCIE_PHY_TRSV3_POWER 0x304
-#define PCIE_PHY_TRSV3_PD_TSV BIT(7)
-#define PCIE_PHY_TRSV3_LVCC 0x31c
-
struct exynos_pcie_mem_res {
void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */
- void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
- void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
};
struct exynos_pcie_clk_res {
@@ -112,8 +68,6 @@ struct exynos_pcie {
const struct exynos_pcie_ops *ops;
int reset_gpio;
- /* For Generic PHY Framework */
- bool using_phy;
struct phy *phy;
};
@@ -141,20 +95,6 @@ static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
if (IS_ERR(ep->mem_res->elbi_base))
return PTR_ERR(ep->mem_res->elbi_base);
- /* If using the PHY framework, doesn't need to get other resource */
- if (ep->using_phy)
- return 0;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(ep->mem_res->phy_base))
- return PTR_ERR(ep->mem_res->phy_base);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- ep->mem_res->block_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(ep->mem_res->block_base))
- return PTR_ERR(ep->mem_res->block_base);
-
return 0;
}
@@ -279,111 +219,6 @@ static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
- exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
-}
-
-static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
-{
- exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
- exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
-}
-
-static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
-{
- exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
- exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
- exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
- exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
- exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
- exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
-}
-
-static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
-{
- u32 val;
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
- val &= ~PCIE_PHY_COMMON_PD_CMN;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
- val &= ~PCIE_PHY_TRSV0_PD_TSV;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
- val &= ~PCIE_PHY_TRSV1_PD_TSV;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
- val &= ~PCIE_PHY_TRSV2_PD_TSV;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
- val &= ~PCIE_PHY_TRSV3_PD_TSV;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
-}
-
-static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
-{
- u32 val;
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
- val |= PCIE_PHY_COMMON_PD_CMN;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
- val |= PCIE_PHY_TRSV0_PD_TSV;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
- val |= PCIE_PHY_TRSV1_PD_TSV;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
- val |= PCIE_PHY_TRSV2_PD_TSV;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
-
- val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
- val |= PCIE_PHY_TRSV3_PD_TSV;
- exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
-}
-
-static void exynos_pcie_init_phy(struct exynos_pcie *ep)
-{
- /* DCC feedback control off */
- exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
-
- /* set TX/RX impedance */
- exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
-
- /* set 50Mhz PHY clock */
- exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
- exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
-
- /* set TX Differential output for lane 0 */
- exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
-
- /* set TX Pre-emphasis Level Control for lane 0 to minimum */
- exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
-
- /* set RX clock and data recovery bandwidth */
- exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
- exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
- exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
- exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
- exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
-
- /* change TX Pre-emphasis Level Control for lanes */
- exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
- exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
- exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
- exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
-
- /* set LVCC */
- exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
- exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
- exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
- exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
}
static void exynos_pcie_assert_reset(struct exynos_pcie *ep)
@@ -401,7 +236,6 @@ static int exynos_pcie_establish_link(struct exynos_pcie *ep)
struct dw_pcie *pci = ep->pci;
struct pcie_port *pp = &pci->pp;
struct device *dev = pci->dev;
- u32 val;
if (dw_pcie_link_up(pci)) {
dev_err(dev, "Link already up\n");
@@ -410,32 +244,13 @@ static int exynos_pcie_establish_link(struct exynos_pcie *ep)
exynos_pcie_assert_core_reset(ep);
- if (ep->using_phy) {
- phy_reset(ep->phy);
-
- exynos_pcie_writel(ep->mem_res->elbi_base, 1,
- PCIE_PWR_RESET);
-
- phy_power_on(ep->phy);
- phy_init(ep->phy);
- } else {
- exynos_pcie_assert_phy_reset(ep);
- exynos_pcie_deassert_phy_reset(ep);
- exynos_pcie_power_on_phy(ep);
- exynos_pcie_init_phy(ep);
-
- /* pulse for common reset */
- exynos_pcie_writel(ep->mem_res->block_base, 1,
- PCIE_PHY_COMMON_RESET);
- udelay(500);
- exynos_pcie_writel(ep->mem_res->block_base, 0,
- PCIE_PHY_COMMON_RESET);
- }
+ phy_reset(ep->phy);
- /* pulse for common reset */
- exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_COMMON_RESET);
- udelay(500);
- exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1,
+ PCIE_PWR_RESET);
+
+ phy_power_on(ep->phy);
+ phy_init(ep->phy);
exynos_pcie_deassert_core_reset(ep);
dw_pcie_setup_rc(pp);
@@ -449,18 +264,7 @@ static int exynos_pcie_establish_link(struct exynos_pcie *ep)
if (!dw_pcie_wait_for_link(pci))
return 0;
- if (ep->using_phy) {
- phy_power_off(ep->phy);
- return -ETIMEDOUT;
- }
-
- while (exynos_pcie_readl(ep->mem_res->phy_base,
- PCIE_PHY_PLL_LOCKED) == 0) {
- val = exynos_pcie_readl(ep->mem_res->block_base,
- PCIE_PHY_PLL_LOCKED);
- dev_info(dev, "PLL Locked: 0x%x\n", val);
- }
- exynos_pcie_power_off_phy(ep);
+ phy_power_off(ep->phy);
return -ETIMEDOUT;
}
@@ -678,16 +482,13 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
- /* Assume that controller doesn't use the PHY framework */
- ep->using_phy = false;
-
ep->phy = devm_of_phy_get(dev, np, NULL);
if (IS_ERR(ep->phy)) {
if (PTR_ERR(ep->phy) == -EPROBE_DEFER)
return PTR_ERR(ep->phy);
- dev_warn(dev, "Use the 'phy' property. Current DT of pci-exynos was deprecated!!\n");
- } else
- ep->using_phy = true;
+
+ ep->phy = NULL;
+ }
if (ep->ops && ep->ops->get_mem_resources) {
ret = ep->ops->get_mem_resources(pdev, ep);
@@ -695,7 +496,8 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
return ret;
}
- if (ep->ops && ep->ops->get_clk_resources) {
+ if (ep->ops && ep->ops->get_clk_resources &&
+ ep->ops->init_clk_resources) {
ret = ep->ops->get_clk_resources(ep);
if (ret)
return ret;
@@ -713,8 +515,7 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
return 0;
fail_probe:
- if (ep->using_phy)
- phy_exit(ep->phy);
+ phy_exit(ep->phy);
if (ep->ops && ep->ops->deinit_clk_resources)
ep->ops->deinit_clk_resources(ep);
diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c
index b73483534a5b..4fddbd08b089 100644
--- a/drivers/pci/dwc/pci-imx6.c
+++ b/drivers/pci/dwc/pci-imx6.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Freescale i.MX6 SoCs
*
@@ -5,10 +6,6 @@
* http://www.kosagi.com
*
* Author: Sean Cross <xobs@kosagi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/clk.h>
diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c
index 2fb20b887d2a..99a0e7076221 100644
--- a/drivers/pci/dwc/pci-keystone-dw.c
+++ b/drivers/pci/dwc/pci-keystone-dw.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* DesignWare application register space functions for Keystone PCI controller
*
@@ -5,11 +6,6 @@
* http://www.ti.com
*
* Author: Murali Karicheri <m-karicheri2@ti.com>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/irq.h>
diff --git a/drivers/pci/dwc/pci-keystone.c b/drivers/pci/dwc/pci-keystone.c
index 5bee3af47588..d4f8ab90c018 100644
--- a/drivers/pci/dwc/pci-keystone.c
+++ b/drivers/pci/dwc/pci-keystone.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Texas Instruments Keystone SoCs
*
@@ -6,10 +7,6 @@
*
* Author: Murali Karicheri <m-karicheri2@ti.com>
* Implementation based on pci-exynos.c and pcie-designware.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/irqchip/chained_irq.h>
@@ -178,7 +175,7 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
}
/* interrupt controller is in a child node */
- *np_temp = of_find_node_by_name(np_pcie, controller);
+ *np_temp = of_get_child_by_name(np_pcie, controller);
if (!(*np_temp)) {
dev_err(dev, "Node for %s is absent\n", controller);
return -EINVAL;
@@ -187,6 +184,7 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
temp = of_irq_count(*np_temp);
if (!temp) {
dev_err(dev, "No IRQ entries in %s\n", controller);
+ of_node_put(*np_temp);
return -EINVAL;
}
@@ -204,6 +202,8 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
break;
}
+ of_node_put(*np_temp);
+
if (temp) {
*num_irqs = temp;
return 0;
diff --git a/drivers/pci/dwc/pci-keystone.h b/drivers/pci/dwc/pci-keystone.h
index 30b7bc2ac380..1dd1f3ef98e7 100644
--- a/drivers/pci/dwc/pci-keystone.h
+++ b/drivers/pci/dwc/pci-keystone.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Keystone PCI Controller's common includes
*
@@ -5,11 +6,6 @@
* http://www.ti.com
*
* Author: Murali Karicheri <m-karicheri2@ti.com>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#define MAX_MSI_HOST_IRQS 8
diff --git a/drivers/pci/dwc/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c
index 8f34c2fdc600..a7b4159631ae 100644
--- a/drivers/pci/dwc/pci-layerscape.c
+++ b/drivers/pci/dwc/pci-layerscape.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Freescale Layerscape SoCs
*
* Copyright (C) 2014 Freescale Semiconductor.
*
* Author: Minghuan Lian <Minghuan.Lian@freescale.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
diff --git a/drivers/pci/dwc/pcie-armada8k.c b/drivers/pci/dwc/pcie-armada8k.c
index 370d057c0046..b587352f8b9f 100644
--- a/drivers/pci/dwc/pcie-armada8k.c
+++ b/drivers/pci/dwc/pcie-armada8k.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Marvell Armada-8K SoCs
*
@@ -7,10 +8,6 @@
*
* Author: Yehuda Yitshak <yehuday@marvell.com>
* Author: Shadi Ammouri <shadi@marvell.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c
index 6653619db6a1..93b3df9ed1b5 100644
--- a/drivers/pci/dwc/pcie-artpec6.c
+++ b/drivers/pci/dwc/pcie-artpec6.c
@@ -1,18 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Axis ARTPEC-6 SoC
*
* Author: Niklas Cassel <niklas.cassel@axis.com>
*
* Based on work done by Phil Edworthy <phil@edworthys.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/of_device.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/resource.h>
@@ -26,44 +24,72 @@
#define to_artpec6_pcie(x) dev_get_drvdata((x)->dev)
+enum artpec_pcie_variants {
+ ARTPEC6,
+ ARTPEC7,
+};
+
struct artpec6_pcie {
struct dw_pcie *pci;
struct regmap *regmap; /* DT axis,syscon-pcie */
void __iomem *phy_base; /* DT phy */
+ enum artpec_pcie_variants variant;
+ enum dw_pcie_device_mode mode;
};
+struct artpec_pcie_of_data {
+ enum artpec_pcie_variants variant;
+ enum dw_pcie_device_mode mode;
+};
+
+static const struct of_device_id artpec6_pcie_of_match[];
+
/* PCIe Port Logic registers (memory-mapped) */
#define PL_OFFSET 0x700
-#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
-#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
-#define MISC_CONTROL_1_OFF (PL_OFFSET + 0x1bc)
-#define DBI_RO_WR_EN 1
+#define ACK_F_ASPM_CTRL_OFF (PL_OFFSET + 0xc)
+#define ACK_N_FTS_MASK GENMASK(15, 8)
+#define ACK_N_FTS(x) (((x) << 8) & ACK_N_FTS_MASK)
+
+#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0)
+#define FAST_TRAINING_SEQ(x) (((x) << 0) & FAST_TRAINING_SEQ_MASK)
/* ARTPEC-6 specific registers */
#define PCIECFG 0x18
-#define PCIECFG_DBG_OEN (1 << 24)
-#define PCIECFG_CORE_RESET_REQ (1 << 21)
-#define PCIECFG_LTSSM_ENABLE (1 << 20)
-#define PCIECFG_CLKREQ_B (1 << 11)
-#define PCIECFG_REFCLK_ENABLE (1 << 10)
-#define PCIECFG_PLL_ENABLE (1 << 9)
-#define PCIECFG_PCLK_ENABLE (1 << 8)
-#define PCIECFG_RISRCREN (1 << 4)
-#define PCIECFG_MODE_TX_DRV_EN (1 << 3)
-#define PCIECFG_CISRREN (1 << 2)
-#define PCIECFG_MACRO_ENABLE (1 << 0)
+#define PCIECFG_DBG_OEN BIT(24)
+#define PCIECFG_CORE_RESET_REQ BIT(21)
+#define PCIECFG_LTSSM_ENABLE BIT(20)
+#define PCIECFG_DEVICE_TYPE_MASK GENMASK(19, 16)
+#define PCIECFG_CLKREQ_B BIT(11)
+#define PCIECFG_REFCLK_ENABLE BIT(10)
+#define PCIECFG_PLL_ENABLE BIT(9)
+#define PCIECFG_PCLK_ENABLE BIT(8)
+#define PCIECFG_RISRCREN BIT(4)
+#define PCIECFG_MODE_TX_DRV_EN BIT(3)
+#define PCIECFG_CISRREN BIT(2)
+#define PCIECFG_MACRO_ENABLE BIT(0)
+/* ARTPEC-7 specific fields */
+#define PCIECFG_REFCLKSEL BIT(23)
+#define PCIECFG_NOC_RESET BIT(3)
+
+#define PCIESTAT 0x1c
+/* ARTPEC-7 specific fields */
+#define PCIESTAT_EXTREFCLK BIT(3)
#define NOCCFG 0x40
-#define NOCCFG_ENABLE_CLK_PCIE (1 << 4)
-#define NOCCFG_POWER_PCIE_IDLEACK (1 << 3)
-#define NOCCFG_POWER_PCIE_IDLE (1 << 2)
-#define NOCCFG_POWER_PCIE_IDLEREQ (1 << 1)
+#define NOCCFG_ENABLE_CLK_PCIE BIT(4)
+#define NOCCFG_POWER_PCIE_IDLEACK BIT(3)
+#define NOCCFG_POWER_PCIE_IDLE BIT(2)
+#define NOCCFG_POWER_PCIE_IDLEREQ BIT(1)
#define PHY_STATUS 0x118
-#define PHY_COSPLLLOCK (1 << 0)
+#define PHY_COSPLLLOCK BIT(0)
+
+#define PHY_TX_ASIC_OUT 0x4040
+#define PHY_TX_ASIC_OUT_TX_ACK BIT(0)
-#define ARTPEC6_CPU_TO_BUS_ADDR 0x0fffffff
+#define PHY_RX_ASIC_OUT 0x405c
+#define PHY_RX_ASIC_OUT_ACK BIT(0)
static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset)
{
@@ -78,22 +104,123 @@ 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)
+static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr)
{
- return pci_addr & ARTPEC6_CPU_TO_BUS_ADDR;
+ struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
+ struct pcie_port *pp = &pci->pp;
+ struct dw_pcie_ep *ep = &pci->ep;
+
+ switch (artpec6_pcie->mode) {
+ case DW_PCIE_RC_TYPE:
+ return pci_addr - pp->cfg0_base;
+ case DW_PCIE_EP_TYPE:
+ return pci_addr - ep->phys_base;
+ default:
+ dev_err(pci->dev, "UNKNOWN device type\n");
+ }
+ return pci_addr;
}
-static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
+static int artpec6_pcie_establish_link(struct dw_pcie *pci)
{
- struct dw_pcie *pci = artpec6_pcie->pci;
- struct pcie_port *pp = &pci->pp;
+ struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
+ u32 val;
+
+ val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
+ val |= PCIECFG_LTSSM_ENABLE;
+ artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
+
+ return 0;
+}
+
+static void artpec6_pcie_stop_link(struct dw_pcie *pci)
+{
+ struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
u32 val;
- unsigned int retries;
- /* Hold DW core in reset */
val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
- val |= PCIECFG_CORE_RESET_REQ;
+ val &= ~PCIECFG_LTSSM_ENABLE;
artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
+}
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup,
+ .start_link = artpec6_pcie_establish_link,
+ .stop_link = artpec6_pcie_stop_link,
+};
+
+static void artpec6_pcie_wait_for_phy_a6(struct artpec6_pcie *artpec6_pcie)
+{
+ struct dw_pcie *pci = artpec6_pcie->pci;
+ struct device *dev = pci->dev;
+ u32 val;
+ unsigned int retries;
+
+ retries = 50;
+ do {
+ usleep_range(1000, 2000);
+ val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+ retries--;
+ } while (retries &&
+ (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
+ if (!retries)
+ dev_err(dev, "PCIe clock manager did not leave idle state\n");
+
+ retries = 50;
+ do {
+ usleep_range(1000, 2000);
+ val = readl(artpec6_pcie->phy_base + PHY_STATUS);
+ retries--;
+ } while (retries && !(val & PHY_COSPLLLOCK));
+ if (!retries)
+ dev_err(dev, "PHY PLL did not lock\n");
+}
+
+static void artpec6_pcie_wait_for_phy_a7(struct artpec6_pcie *artpec6_pcie)
+{
+ struct dw_pcie *pci = artpec6_pcie->pci;
+ struct device *dev = pci->dev;
+ u32 val;
+ u16 phy_status_tx, phy_status_rx;
+ unsigned int retries;
+
+ retries = 50;
+ do {
+ usleep_range(1000, 2000);
+ val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+ retries--;
+ } while (retries &&
+ (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
+ if (!retries)
+ dev_err(dev, "PCIe clock manager did not leave idle state\n");
+
+ retries = 50;
+ do {
+ usleep_range(1000, 2000);
+ phy_status_tx = readw(artpec6_pcie->phy_base + PHY_TX_ASIC_OUT);
+ phy_status_rx = readw(artpec6_pcie->phy_base + PHY_RX_ASIC_OUT);
+ retries--;
+ } while (retries && ((phy_status_tx & PHY_TX_ASIC_OUT_TX_ACK) ||
+ (phy_status_rx & PHY_RX_ASIC_OUT_ACK)));
+ if (!retries)
+ dev_err(dev, "PHY did not enter Pn state\n");
+}
+
+static void artpec6_pcie_wait_for_phy(struct artpec6_pcie *artpec6_pcie)
+{
+ switch (artpec6_pcie->variant) {
+ case ARTPEC6:
+ artpec6_pcie_wait_for_phy_a6(artpec6_pcie);
+ break;
+ case ARTPEC7:
+ artpec6_pcie_wait_for_phy_a7(artpec6_pcie);
+ break;
+ }
+}
+
+static void artpec6_pcie_init_phy_a6(struct artpec6_pcie *artpec6_pcie)
+{
+ u32 val;
val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */
@@ -119,45 +246,110 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
val &= ~NOCCFG_POWER_PCIE_IDLEREQ;
artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
+}
- retries = 50;
- do {
- usleep_range(1000, 2000);
- val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
- retries--;
- } while (retries &&
- (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
+static void artpec6_pcie_init_phy_a7(struct artpec6_pcie *artpec6_pcie)
+{
+ struct dw_pcie *pci = artpec6_pcie->pci;
+ u32 val;
+ bool extrefclk;
- retries = 50;
- do {
- usleep_range(1000, 2000);
- val = readl(artpec6_pcie->phy_base + PHY_STATUS);
- retries--;
- } while (retries && !(val & PHY_COSPLLLOCK));
+ /* Check if external reference clock is connected */
+ val = artpec6_pcie_readl(artpec6_pcie, PCIESTAT);
+ extrefclk = !!(val & PCIESTAT_EXTREFCLK);
+ dev_dbg(pci->dev, "Using reference clock: %s\n",
+ extrefclk ? "external" : "internal");
- /* Take DW core out of reset */
val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
- val &= ~PCIECFG_CORE_RESET_REQ;
+ val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */
+ PCIECFG_PCLK_ENABLE;
+ if (extrefclk)
+ val |= PCIECFG_REFCLKSEL;
+ else
+ val &= ~PCIECFG_REFCLKSEL;
artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
- usleep_range(100, 200);
+ usleep_range(10, 20);
- /* setup root complex */
- dw_pcie_setup_rc(pp);
+ val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+ val |= NOCCFG_ENABLE_CLK_PCIE;
+ artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
+ usleep_range(20, 30);
+
+ val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+ val &= ~NOCCFG_POWER_PCIE_IDLEREQ;
+ artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
+}
+
+static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie)
+{
+ switch (artpec6_pcie->variant) {
+ case ARTPEC6:
+ artpec6_pcie_init_phy_a6(artpec6_pcie);
+ break;
+ case ARTPEC7:
+ artpec6_pcie_init_phy_a7(artpec6_pcie);
+ break;
+ }
+}
+
+static void artpec6_pcie_set_nfts(struct artpec6_pcie *artpec6_pcie)
+{
+ struct dw_pcie *pci = artpec6_pcie->pci;
+ u32 val;
+
+ if (artpec6_pcie->variant != ARTPEC7)
+ return;
+
+ /*
+ * Increase the N_FTS (Number of Fast Training Sequences)
+ * to be transmitted when transitioning from L0s to L0.
+ */
+ val = dw_pcie_readl_dbi(pci, ACK_F_ASPM_CTRL_OFF);
+ val &= ~ACK_N_FTS_MASK;
+ val |= ACK_N_FTS(180);
+ dw_pcie_writel_dbi(pci, ACK_F_ASPM_CTRL_OFF, val);
+
+ /*
+ * Set the Number of Fast Training Sequences that the core
+ * advertises as its N_FTS during Gen2 or Gen3 link training.
+ */
+ val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ val &= ~FAST_TRAINING_SEQ_MASK;
+ val |= FAST_TRAINING_SEQ(180);
+ dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+}
+
+static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie)
+{
+ u32 val;
- /* assert LTSSM enable */
val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
- val |= PCIECFG_LTSSM_ENABLE;
+ switch (artpec6_pcie->variant) {
+ case ARTPEC6:
+ val |= PCIECFG_CORE_RESET_REQ;
+ break;
+ case ARTPEC7:
+ val &= ~PCIECFG_NOC_RESET;
+ break;
+ }
artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
+}
- /* check if the link is up or not */
- if (!dw_pcie_wait_for_link(pci))
- return 0;
-
- dev_dbg(pci->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1));
+static void artpec6_pcie_deassert_core_reset(struct artpec6_pcie *artpec6_pcie)
+{
+ u32 val;
- return -ETIMEDOUT;
+ val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
+ switch (artpec6_pcie->variant) {
+ case ARTPEC6:
+ val &= ~PCIECFG_CORE_RESET_REQ;
+ break;
+ case ARTPEC7:
+ val |= PCIECFG_NOC_RESET;
+ break;
+ }
+ artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
+ usleep_range(100, 200);
}
static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie)
@@ -174,7 +366,14 @@ static int artpec6_pcie_host_init(struct pcie_port *pp)
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
- artpec6_pcie_establish_link(artpec6_pcie);
+ artpec6_pcie_assert_core_reset(artpec6_pcie);
+ artpec6_pcie_init_phy(artpec6_pcie);
+ artpec6_pcie_deassert_core_reset(artpec6_pcie);
+ artpec6_pcie_wait_for_phy(artpec6_pcie);
+ artpec6_pcie_set_nfts(artpec6_pcie);
+ dw_pcie_setup_rc(pp);
+ artpec6_pcie_establish_link(pci);
+ dw_pcie_wait_for_link(pci);
artpec6_pcie_enable_interrupts(artpec6_pcie);
return 0;
@@ -230,10 +429,78 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
return 0;
}
-static const struct dw_pcie_ops dw_pcie_ops = {
- .cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup,
+static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
+ enum pci_barno bar;
+
+ artpec6_pcie_assert_core_reset(artpec6_pcie);
+ artpec6_pcie_init_phy(artpec6_pcie);
+ artpec6_pcie_deassert_core_reset(artpec6_pcie);
+ artpec6_pcie_wait_for_phy(artpec6_pcie);
+ artpec6_pcie_set_nfts(artpec6_pcie);
+
+ for (bar = BAR_0; bar <= BAR_5; bar++)
+ dw_pcie_ep_reset_bar(pci, bar);
+}
+
+static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
+ enum pci_epc_irq_type type, u8 interrupt_num)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ switch (type) {
+ case PCI_EPC_IRQ_LEGACY:
+ dev_err(pci->dev, "EP cannot trigger legacy IRQs\n");
+ return -EINVAL;
+ case PCI_EPC_IRQ_MSI:
+ return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
+ default:
+ dev_err(pci->dev, "UNKNOWN IRQ type\n");
+ }
+
+ return 0;
+}
+
+static struct dw_pcie_ep_ops pcie_ep_ops = {
+ .ep_init = artpec6_pcie_ep_init,
+ .raise_irq = artpec6_pcie_raise_irq,
};
+static int artpec6_add_pcie_ep(struct artpec6_pcie *artpec6_pcie,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct dw_pcie_ep *ep;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct dw_pcie *pci = artpec6_pcie->pci;
+
+ ep = &pci->ep;
+ ep->ops = &pcie_ep_ops;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2");
+ 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 artpec6_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -242,6 +509,18 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
struct resource *dbi_base;
struct resource *phy_base;
int ret;
+ const struct of_device_id *match;
+ const struct artpec_pcie_of_data *data;
+ enum artpec_pcie_variants variant;
+ enum dw_pcie_device_mode mode;
+
+ match = of_match_device(artpec6_pcie_of_match, dev);
+ if (!match)
+ return -EINVAL;
+
+ data = (struct artpec_pcie_of_data *)match->data;
+ variant = (enum artpec_pcie_variants)data->variant;
+ mode = (enum dw_pcie_device_mode)data->mode;
artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL);
if (!artpec6_pcie)
@@ -255,6 +534,8 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
pci->ops = &dw_pcie_ops;
artpec6_pcie->pci = pci;
+ artpec6_pcie->variant = variant;
+ artpec6_pcie->mode = mode;
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
@@ -274,15 +555,73 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, artpec6_pcie);
- ret = artpec6_add_pcie_port(artpec6_pcie, pdev);
- if (ret < 0)
- return ret;
+ switch (artpec6_pcie->mode) {
+ case DW_PCIE_RC_TYPE:
+ if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_HOST))
+ return -ENODEV;
+
+ ret = artpec6_add_pcie_port(artpec6_pcie, pdev);
+ if (ret < 0)
+ return ret;
+ break;
+ case DW_PCIE_EP_TYPE: {
+ u32 val;
+
+ if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_EP))
+ return -ENODEV;
+
+ val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
+ val &= ~PCIECFG_DEVICE_TYPE_MASK;
+ artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
+ ret = artpec6_add_pcie_ep(artpec6_pcie, pdev);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ default:
+ dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode);
+ }
return 0;
}
+static const struct artpec_pcie_of_data artpec6_pcie_rc_of_data = {
+ .variant = ARTPEC6,
+ .mode = DW_PCIE_RC_TYPE,
+};
+
+static const struct artpec_pcie_of_data artpec6_pcie_ep_of_data = {
+ .variant = ARTPEC6,
+ .mode = DW_PCIE_EP_TYPE,
+};
+
+static const struct artpec_pcie_of_data artpec7_pcie_rc_of_data = {
+ .variant = ARTPEC7,
+ .mode = DW_PCIE_RC_TYPE,
+};
+
+static const struct artpec_pcie_of_data artpec7_pcie_ep_of_data = {
+ .variant = ARTPEC7,
+ .mode = DW_PCIE_EP_TYPE,
+};
+
static const struct of_device_id artpec6_pcie_of_match[] = {
- { .compatible = "axis,artpec6-pcie", },
+ {
+ .compatible = "axis,artpec6-pcie",
+ .data = &artpec6_pcie_rc_of_data,
+ },
+ {
+ .compatible = "axis,artpec6-pcie-ep",
+ .data = &artpec6_pcie_ep_of_data,
+ },
+ {
+ .compatible = "axis,artpec7-pcie",
+ .data = &artpec7_pcie_rc_of_data,
+ },
+ {
+ .compatible = "axis,artpec7-pcie-ep",
+ .data = &artpec7_pcie_ep_of_data,
+ },
{},
};
diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c
index d53d5f168363..3a6feeff5f5b 100644
--- a/drivers/pci/dwc/pcie-designware-ep.c
+++ b/drivers/pci/dwc/pcie-designware-ep.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/**
* Synopsys DesignWare PCIe Endpoint controller driver
*
* Copyright (C) 2017 Texas Instruments
* Author: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * 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>
@@ -30,21 +19,24 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
pci_epc_linkup(epc);
}
-static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
+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_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi2(pci, reg, 0x0);
dw_pcie_writel_dbi(pci, reg, 0x0);
+ dw_pcie_dbi_ro_wr_dis(pci);
}
-static int dw_pcie_ep_write_header(struct pci_epc *epc,
+static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
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_dbi_ro_wr_en(pci);
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);
@@ -58,6 +50,7 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc,
dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_ID, hdr->subsys_id);
dw_pcie_writeb_dbi(pci, PCI_INTERRUPT_PIN,
hdr->interrupt_pin);
+ dw_pcie_dbi_ro_wr_dis(pci);
return 0;
}
@@ -70,8 +63,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar,
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));
+ free_win = find_first_zero_bit(ep->ib_window_map, ep->num_ib_windows);
if (free_win >= ep->num_ib_windows) {
dev_err(pci->dev, "no free inbound window\n");
return -EINVAL;
@@ -85,7 +77,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar,
}
ep->bar_to_atu[bar] = free_win;
- set_bit(free_win, &ep->ib_window_map);
+ set_bit(free_win, ep->ib_window_map);
return 0;
}
@@ -96,8 +88,7 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr,
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));
+ free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows);
if (free_win >= ep->num_ob_windows) {
dev_err(pci->dev, "no free outbound window\n");
return -EINVAL;
@@ -106,13 +97,14 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr,
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);
+ 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)
+static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
+ enum pci_barno bar)
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
@@ -121,10 +113,11 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno 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);
+ clear_bit(atu_index, ep->ib_window_map);
}
-static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
+static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
+ enum pci_barno bar,
dma_addr_t bar_phys, size_t size, int flags)
{
int ret;
@@ -142,8 +135,10 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
if (ret)
return ret;
+ dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi2(pci, reg, size - 1);
dw_pcie_writel_dbi(pci, reg, flags);
+ dw_pcie_dbi_ro_wr_dis(pci);
return 0;
}
@@ -163,7 +158,8 @@ static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
return -EINVAL;
}
-static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
+static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
+ phys_addr_t addr)
{
int ret;
u32 atu_index;
@@ -175,10 +171,11 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
return;
dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND);
- clear_bit(atu_index, &ep->ob_window_map);
+ clear_bit(atu_index, ep->ob_window_map);
}
-static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
+static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
+ phys_addr_t addr,
u64 pci_addr, size_t size)
{
int ret;
@@ -194,39 +191,37 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
return 0;
}
-static int dw_pcie_ep_get_msi(struct pci_epc *epc)
+static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
{
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))
+ val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL);
+ if (!(val & MSI_CAP_MSI_EN_MASK))
return -EINVAL;
+ val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT;
return val;
}
-static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int)
+static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, 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);
+ val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL);
+ val &= ~MSI_CAP_MMC_MASK;
+ val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK;
+ dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val);
+ dw_pcie_dbi_ro_wr_dis(pci);
return 0;
}
-static int dw_pcie_ep_raise_irq(struct pci_epc *epc,
+static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
enum pci_epc_irq_type type, u8 interrupt_num)
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
@@ -234,7 +229,7 @@ static int dw_pcie_ep_raise_irq(struct pci_epc *epc,
if (!ep->ops->raise_irq)
return -EINVAL;
- return ep->ops->raise_irq(ep, type, interrupt_num);
+ return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
}
static void dw_pcie_ep_stop(struct pci_epc *epc)
@@ -272,10 +267,48 @@ static const struct pci_epc_ops epc_ops = {
.stop = dw_pcie_ep_stop,
};
+int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
+ u8 interrupt_num)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct pci_epc *epc = ep->epc;
+ u16 msg_ctrl, msg_data;
+ u32 msg_addr_lower, msg_addr_upper;
+ u64 msg_addr;
+ bool has_upper;
+ int ret;
+
+ /* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */
+ msg_ctrl = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL);
+ has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
+ msg_addr_lower = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32);
+ if (has_upper) {
+ msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32);
+ msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64);
+ } else {
+ msg_addr_upper = 0;
+ msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_32);
+ }
+ msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
+ ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
+ epc->mem->page_size);
+ if (ret)
+ return ret;
+
+ writel(msg_data | (interrupt_num - 1), ep->msi_mem);
+
+ dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
+
+ return 0;
+}
+
void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
{
struct pci_epc *epc = ep->epc;
+ pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
+ epc->mem->page_size);
+
pci_epc_mem_exit(epc);
}
@@ -298,12 +331,32 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
dev_err(dev, "unable to read *num-ib-windows* property\n");
return ret;
}
+ if (ep->num_ib_windows > MAX_IATU_IN) {
+ dev_err(dev, "invalid *num-ib-windows*\n");
+ return -EINVAL;
+ }
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;
}
+ if (ep->num_ob_windows > MAX_IATU_OUT) {
+ dev_err(dev, "invalid *num-ob-windows*\n");
+ return -EINVAL;
+ }
+
+ ep->ib_window_map = devm_kzalloc(dev, sizeof(long) *
+ BITS_TO_LONGS(ep->num_ib_windows),
+ GFP_KERNEL);
+ if (!ep->ib_window_map)
+ return -ENOMEM;
+
+ ep->ob_window_map = devm_kzalloc(dev, sizeof(long) *
+ BITS_TO_LONGS(ep->num_ob_windows),
+ GFP_KERNEL);
+ if (!ep->ob_window_map)
+ return -ENOMEM;
addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows,
GFP_KERNEL);
@@ -331,6 +384,13 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
return ret;
}
+ ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
+ epc->mem->page_size);
+ if (!ep->msi_mem) {
+ dev_err(dev, "Failed to reserve memory for MSI\n");
+ return -ENOMEM;
+ }
+
ep->epc = epc;
epc_set_drvdata(epc, ep);
dw_pcie_setup(pci);
diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c
index 81e2157a7cfb..8de2d5c69b1d 100644
--- a/drivers/pci/dwc/pcie-designware-host.c
+++ b/drivers/pci/dwc/pcie-designware-host.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Synopsys DesignWare PCIe host controller driver
*
@@ -5,10 +6,6 @@
* http://www.samsung.com
*
* Author: Jingoo Han <jg1.han@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/irqdomain.h>
@@ -83,10 +80,19 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
void dw_pcie_msi_init(struct pcie_port *pp)
{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct device *dev = pci->dev;
+ struct page *page;
u64 msi_target;
- pp->msi_data = __get_free_pages(GFP_KERNEL, 0);
- msi_target = virt_to_phys((void *)pp->msi_data);
+ page = alloc_page(GFP_KERNEL);
+ pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, pp->msi_data)) {
+ dev_err(dev, "failed to map MSI data\n");
+ __free_page(page);
+ return;
+ }
+ msi_target = (u64)pp->msi_data;
/* program the msi_data */
dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
@@ -187,7 +193,7 @@ static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
if (pp->ops->get_msi_addr)
msi_target = pp->ops->get_msi_addr(pp);
else
- msi_target = virt_to_phys((void *)pp->msi_data);
+ msi_target = (u64)pp->msi_data;
msg.address_lo = (u32)(msi_target & 0xffffffff);
msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff);
diff --git a/drivers/pci/dwc/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c
index 168e2380f493..ebdf28bcd67d 100644
--- a/drivers/pci/dwc/pcie-designware-plat.c
+++ b/drivers/pci/dwc/pcie-designware-plat.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe RC driver for Synopsys DesignWare Core
*
* Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
*
* Authors: Joao Pinto <Joao.Pinto@synopsys.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c
index 88abdddee2ad..1b7282e5b494 100644
--- a/drivers/pci/dwc/pcie-designware.c
+++ b/drivers/pci/dwc/pcie-designware.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Synopsys DesignWare PCIe host controller driver
*
@@ -5,10 +6,6 @@
* http://www.samsung.com
*
* Author: Jingoo Han <jg1.han@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
@@ -149,7 +146,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
u32 retries, val;
if (pci->ops->cpu_addr_fixup)
- cpu_addr = pci->ops->cpu_addr_fixup(cpu_addr);
+ cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr);
if (pci->iatu_unroll_enabled) {
dw_pcie_prog_outbound_atu_unroll(pci, index, type, cpu_addr,
diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
index e5d9d77b778e..11b13864a406 100644
--- a/drivers/pci/dwc/pcie-designware.h
+++ b/drivers/pci/dwc/pcie-designware.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Synopsys DesignWare PCIe host controller driver
*
@@ -5,15 +6,12 @@
* http://www.samsung.com
*
* Author: Jingoo Han <jg1.han@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef _PCIE_DESIGNWARE_H
#define _PCIE_DESIGNWARE_H
+#include <linux/dma-mapping.h>
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/pci.h>
@@ -100,10 +98,14 @@
#define MSI_MESSAGE_CONTROL 0x52
#define MSI_CAP_MMC_SHIFT 1
+#define MSI_CAP_MMC_MASK (7 << MSI_CAP_MMC_SHIFT)
#define MSI_CAP_MME_SHIFT 4
+#define MSI_CAP_MSI_EN_MASK 0x1
#define MSI_CAP_MME_MASK (7 << MSI_CAP_MME_SHIFT)
#define MSI_MESSAGE_ADDR_L32 0x54
#define MSI_MESSAGE_ADDR_U32 0x58
+#define MSI_MESSAGE_DATA_32 0x58
+#define MSI_MESSAGE_DATA_64 0x5C
/*
* Maximum number of MSI IRQs can be 256 per controller. But keep
@@ -113,6 +115,10 @@
#define MAX_MSI_IRQS 32
#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32)
+/* Maximum number of inbound/outbound iATUs */
+#define MAX_IATU_IN 256
+#define MAX_IATU_OUT 256
+
struct pcie_port;
struct dw_pcie;
struct dw_pcie_ep;
@@ -168,7 +174,7 @@ struct pcie_port {
const struct dw_pcie_host_ops *ops;
int msi_irq;
struct irq_domain *irq_domain;
- unsigned long msi_data;
+ dma_addr_t msi_data;
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
};
@@ -180,8 +186,8 @@ enum dw_pcie_as_type {
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);
+ int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
+ enum pci_epc_irq_type type, u8 interrupt_num);
};
struct dw_pcie_ep {
@@ -192,14 +198,16 @@ struct dw_pcie_ep {
size_t page_size;
u8 bar_to_atu[6];
phys_addr_t *outbound_addr;
- unsigned long ib_window_map;
- unsigned long ob_window_map;
+ unsigned long *ib_window_map;
+ unsigned long *ob_window_map;
u32 num_ib_windows;
u32 num_ob_windows;
+ void __iomem *msi_mem;
+ phys_addr_t msi_mem_phys;
};
struct dw_pcie_ops {
- u64 (*cpu_addr_fixup)(u64 cpu_addr);
+ u64 (*cpu_addr_fixup)(struct dw_pcie *pcie, 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,
@@ -334,6 +342,9 @@ static inline int dw_pcie_host_init(struct pcie_port *pp)
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);
+int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
+ u8 interrupt_num);
+void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar);
#else
static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
{
@@ -347,5 +358,15 @@ static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep)
static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
{
}
+
+static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
+ u8 interrupt_num)
+{
+ return 0;
+}
+
+static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
+{
+}
#endif
#endif /* _PCIE_DESIGNWARE_H */
diff --git a/drivers/pci/dwc/pcie-hisi.c b/drivers/pci/dwc/pcie-hisi.c
index a20179169e06..2658aaebb993 100644
--- a/drivers/pci/dwc/pcie-hisi.c
+++ b/drivers/pci/dwc/pcie-hisi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for HiSilicon SoCs
*
@@ -6,10 +7,6 @@
* Authors: Zhou Wang <wangzhou1@hisilicon.com>
* Dacai Zhu <zhudacai@hisilicon.com>
* Gabriele Paoloni <gabriele.paoloni@huawei.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/interrupt.h>
#include <linux/init.h>
diff --git a/drivers/pci/dwc/pcie-histb.c b/drivers/pci/dwc/pcie-histb.c
index 33b01b734d7d..70b5c0b108bf 100644
--- a/drivers/pci/dwc/pcie-histb.c
+++ b/drivers/pci/dwc/pcie-histb.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for HiSilicon STB SoCs
*
@@ -5,10 +6,6 @@
*
* Authors: Ruqiang Ju <juruqiang@hisilicon.com>
* Jianguo Sun <sunjianguo1@huawei.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/clk.h>
diff --git a/drivers/pci/dwc/pcie-kirin.c b/drivers/pci/dwc/pcie-kirin.c
index dc3033cf3c19..13d839bd6160 100644
--- a/drivers/pci/dwc/pcie-kirin.c
+++ b/drivers/pci/dwc/pcie-kirin.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Kirin Phone SoCs
*
@@ -5,10 +6,6 @@
* http://www.huawei.com
*
* Author: Xiaowei Song <songxiaowei@huawei.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <asm/compiler.h>
diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c
index ce7ba5b7552a..6310c66e265c 100644
--- a/drivers/pci/dwc/pcie-qcom.c
+++ b/drivers/pci/dwc/pcie-qcom.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Qualcomm PCIe root complex driver
*
@@ -5,15 +6,6 @@
* Copyright 2015 Linaro Limited.
*
* Author: Stanimir Varbanov <svarbanov@mm-sol.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 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.
*/
#include <linux/clk.h>
@@ -171,7 +163,7 @@ struct qcom_pcie {
union qcom_pcie_resources res;
struct phy *phy;
struct gpio_desc *reset;
- struct qcom_pcie_ops *ops;
+ const struct qcom_pcie_ops *ops;
};
#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
@@ -1234,7 +1226,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
pcie->pci = pci;
- pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev);
+ pcie->ops = of_device_get_match_data(dev);
pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
if (IS_ERR(pcie->reset))
diff --git a/drivers/pci/dwc/pcie-spear13xx.c b/drivers/pci/dwc/pcie-spear13xx.c
index 709189d23b31..ecb58f7b7566 100644
--- a/drivers/pci/dwc/pcie-spear13xx.c
+++ b/drivers/pci/dwc/pcie-spear13xx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for ST Microelectronics SPEAr13xx SoCs
*
@@ -6,10 +7,6 @@
* Copyright (C) 2010-2014 ST Microelectronics
* Pratyush Anand <pratyush.anand@gmail.com>
* Mohit Kumar <mohit.kumar.dhaka@gmail.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>