From: raza <raza@xxxxxxxxxxx> pcie-armada8k driver is utilized to serve also AC5. Driver assumes interrupt mask registers are located in the same address in both CPUs. This assumption is incorrect - fix it for AC5. Co-developed-by: Yuval Shaia <yshaia@xxxxxxxxxxx> Signed-off-by: Yuval Shaia <yshaia@xxxxxxxxxxx> Signed-off-by: raza <raza@xxxxxxxxxxx> Signed-off-by: Vadym Kochan <vadym.kochan@xxxxxxxxxxx> --- drivers/pci/controller/dwc/pcie-armada8k.c | 116 +++++++++++++++++---- 1 file changed, 93 insertions(+), 23 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index dc469ef8e99b..b025d23bb058 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -27,12 +27,18 @@ #define ARMADA8K_PCIE_MAX_LANES PCIE_LNK_X4 +enum mvpcie_type { + MVPCIE_TYPE_A8K, + MVPCIE_TYPE_AC5 +}; + struct armada8k_pcie { struct dw_pcie *pci; struct clk *clk; struct clk *clk_reg; struct phy *phy[ARMADA8K_PCIE_MAX_LANES]; unsigned int phy_count; + enum mvpcie_type pcie_type; }; #define PCIE_VENDOR_REGS_OFFSET 0x8000 @@ -49,10 +55,15 @@ struct armada8k_pcie { #define PCIE_GLOBAL_INT_CAUSE1_REG (PCIE_VENDOR_REGS_OFFSET + 0x1C) #define PCIE_GLOBAL_INT_MASK1_REG (PCIE_VENDOR_REGS_OFFSET + 0x20) +#define PCIE_GLOBAL_INT_MASK2_REG (PCIE_VENDOR_REGS_OFFSET + 0x28) #define PCIE_INT_A_ASSERT_MASK BIT(9) #define PCIE_INT_B_ASSERT_MASK BIT(10) #define PCIE_INT_C_ASSERT_MASK BIT(11) #define PCIE_INT_D_ASSERT_MASK BIT(12) +#define PCIE_INT_A_ASSERT_MASK_AC5 BIT(12) +#define PCIE_INT_B_ASSERT_MASK_AC5 BIT(13) +#define PCIE_INT_C_ASSERT_MASK_AC5 BIT(14) +#define PCIE_INT_D_ASSERT_MASK_AC5 BIT(15) #define PCIE_ARCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x50) #define PCIE_AWCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x54) @@ -170,6 +181,7 @@ static int armada8k_pcie_host_init(struct dw_pcie_rp *pp) { u32 reg; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct armada8k_pcie *pcie = to_armada8k_pcie(pci); if (!dw_pcie_link_up(pci)) { /* Disable LTSSM state machine to enable configuration */ @@ -178,32 +190,41 @@ static int armada8k_pcie_host_init(struct dw_pcie_rp *pp) dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); } - /* Set the device to root complex mode */ - reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); - reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT); - reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT; - dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); + if (pcie->pcie_type == MVPCIE_TYPE_A8K) { + /* Set the device to root complex mode */ + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); + reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT); + reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT; + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); - /* Set the PCIe master AxCache attributes */ - dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE); - dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE); + /* Set the PCIe master AxCache attributes */ + dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE); + dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE); - /* Set the PCIe master AxDomain attributes */ - reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG); - reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); - reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; - dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg); + /* Set the PCIe master AxDomain attributes */ + reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG); + reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); + reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; + dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg); - reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG); - reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); - reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; - dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg); + reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG); + reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); + reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; + dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg); + } /* Enable INT A-D interrupts */ - reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG); - reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK | - PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK; - dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg); + if (pcie->pcie_type == MVPCIE_TYPE_AC5) { + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK2_REG); + reg |= PCIE_INT_A_ASSERT_MASK_AC5 | PCIE_INT_B_ASSERT_MASK_AC5 | + PCIE_INT_C_ASSERT_MASK_AC5 | PCIE_INT_D_ASSERT_MASK_AC5; + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK2_REG, reg); + } else { + reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG); + reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK | + PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK; + dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg); + } return 0; } @@ -259,6 +280,45 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, return 0; } +static u32 ac5_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size) +{ + u32 val; + + /* Handle AC5 ATU access */ + if ((reg & ~0xfffff) == 0x300000) { + reg &= 0xfffff; + reg = 0xc000 | (0x200 * (reg >> 9)) | (reg & 0xff); + } else if ((reg & 0xfffff000) == PCIE_VENDOR_REGS_OFFSET) + reg += 0x8000; /* PCIE_VENDOR_REGS_OFFSET in ac5 is 0x10000 */ + dw_pcie_read(base + reg, size, &val); + + return val; +} + +static void ac5_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, + u32 reg, size_t size, u32 val) +{ + /* Handle AC5 ATU access */ + if ((reg & ~0xfffff) == 0x300000) { + reg &= 0xfffff; + reg = 0xc000 | (0x200 * (reg >> 9)) | (reg & 0xff); + } else if ((reg & 0xfffff000) == PCIE_VENDOR_REGS_OFFSET) + reg += 0x8000; /* PCIE_VENDOR_REGS_OFFSET in ac5 is 0x10000 */ + + dw_pcie_write(base + reg, size, val); +} + +static const struct dw_pcie_ops armada8k_dw_pcie_ops = { + .link_up = armada8k_pcie_link_up, +}; + +static const struct dw_pcie_ops ac5_dw_pcie_ops = { + .link_up = armada8k_pcie_link_up, + .read_dbi = ac5_pcie_read_dbi, + .write_dbi = ac5_pcie_write_dbi, +}; + static const struct dw_pcie_ops dw_pcie_ops = { .link_up = armada8k_pcie_link_up, .start_link = armada8k_pcie_start_link, @@ -269,6 +329,7 @@ static int armada8k_pcie_probe(struct platform_device *pdev) struct dw_pcie *pci; struct armada8k_pcie *pcie; struct device *dev = &pdev->dev; + struct device_node *dn = pdev->dev.of_node; struct resource *base; int ret; @@ -281,8 +342,16 @@ static int armada8k_pcie_probe(struct platform_device *pdev) return -ENOMEM; pci->dev = dev; - pci->ops = &dw_pcie_ops; - + if (of_device_is_compatible(dn, "marvell,armada8k-pcie")) { + pci->ops = &armada8k_dw_pcie_ops; + pcie->pcie_type = MVPCIE_TYPE_A8K; + } else if (of_device_is_compatible(dn, "marvell,ac5-pcie")) { + pci->ops = &ac5_dw_pcie_ops; + pcie->pcie_type = MVPCIE_TYPE_AC5; + } else { + dev_err(dev, "couldn't find compatible ops\n"); + return -EOPNOTSUPP; + } pcie->pci = pci; pcie->clk = devm_clk_get(dev, NULL); @@ -336,6 +405,7 @@ static int armada8k_pcie_probe(struct platform_device *pdev) static const struct of_device_id armada8k_pcie_of_match[] = { { .compatible = "marvell,armada8k-pcie", }, + { .compatible = "marvell,ac5-pcie", }, {}, }; -- 2.17.1