16e0832faSShawn Lin // SPDX-License-Identifier: GPL-2.0 26e0832faSShawn Lin /* 36e0832faSShawn Lin * PCIe host controller driver for Marvell Armada-8K SoCs 46e0832faSShawn Lin * 56e0832faSShawn Lin * Armada-8K PCIe Glue Layer Source Code 66e0832faSShawn Lin * 76e0832faSShawn Lin * Copyright (C) 2016 Marvell Technology Group Ltd. 86e0832faSShawn Lin * 96e0832faSShawn Lin * Author: Yehuda Yitshak <yehuday@marvell.com> 106e0832faSShawn Lin * Author: Shadi Ammouri <shadi@marvell.com> 116e0832faSShawn Lin */ 126e0832faSShawn Lin 136e0832faSShawn Lin #include <linux/clk.h> 146e0832faSShawn Lin #include <linux/delay.h> 156e0832faSShawn Lin #include <linux/interrupt.h> 166e0832faSShawn Lin #include <linux/kernel.h> 176e0832faSShawn Lin #include <linux/init.h> 186e0832faSShawn Lin #include <linux/of.h> 196e0832faSShawn Lin #include <linux/pci.h> 206e0832faSShawn Lin #include <linux/phy/phy.h> 216e0832faSShawn Lin #include <linux/platform_device.h> 226e0832faSShawn Lin #include <linux/resource.h> 236e0832faSShawn Lin #include <linux/of_pci.h> 246e0832faSShawn Lin #include <linux/of_irq.h> 256e0832faSShawn Lin 266e0832faSShawn Lin #include "pcie-designware.h" 276e0832faSShawn Lin 286e0832faSShawn Lin struct armada8k_pcie { 296e0832faSShawn Lin struct dw_pcie *pci; 306e0832faSShawn Lin struct clk *clk; 316e0832faSShawn Lin struct clk *clk_reg; 326e0832faSShawn Lin }; 336e0832faSShawn Lin 346e0832faSShawn Lin #define PCIE_VENDOR_REGS_OFFSET 0x8000 356e0832faSShawn Lin 366e0832faSShawn Lin #define PCIE_GLOBAL_CONTROL_REG (PCIE_VENDOR_REGS_OFFSET + 0x0) 376e0832faSShawn Lin #define PCIE_APP_LTSSM_EN BIT(2) 386e0832faSShawn Lin #define PCIE_DEVICE_TYPE_SHIFT 4 396e0832faSShawn Lin #define PCIE_DEVICE_TYPE_MASK 0xF 406e0832faSShawn Lin #define PCIE_DEVICE_TYPE_RC 0x4 /* Root complex */ 416e0832faSShawn Lin 426e0832faSShawn Lin #define PCIE_GLOBAL_STATUS_REG (PCIE_VENDOR_REGS_OFFSET + 0x8) 436e0832faSShawn Lin #define PCIE_GLB_STS_RDLH_LINK_UP BIT(1) 446e0832faSShawn Lin #define PCIE_GLB_STS_PHY_LINK_UP BIT(9) 456e0832faSShawn Lin 466e0832faSShawn Lin #define PCIE_GLOBAL_INT_CAUSE1_REG (PCIE_VENDOR_REGS_OFFSET + 0x1C) 476e0832faSShawn Lin #define PCIE_GLOBAL_INT_MASK1_REG (PCIE_VENDOR_REGS_OFFSET + 0x20) 486e0832faSShawn Lin #define PCIE_INT_A_ASSERT_MASK BIT(9) 496e0832faSShawn Lin #define PCIE_INT_B_ASSERT_MASK BIT(10) 506e0832faSShawn Lin #define PCIE_INT_C_ASSERT_MASK BIT(11) 516e0832faSShawn Lin #define PCIE_INT_D_ASSERT_MASK BIT(12) 526e0832faSShawn Lin 536e0832faSShawn Lin #define PCIE_ARCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x50) 546e0832faSShawn Lin #define PCIE_AWCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x54) 556e0832faSShawn Lin #define PCIE_ARUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x5C) 566e0832faSShawn Lin #define PCIE_AWUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x60) 576e0832faSShawn Lin /* 58*f6b6aefeSBjorn Helgaas * AR/AW Cache defaults: Normal memory, Write-Back, Read / Write 596e0832faSShawn Lin * allocate 606e0832faSShawn Lin */ 616e0832faSShawn Lin #define ARCACHE_DEFAULT_VALUE 0x3511 626e0832faSShawn Lin #define AWCACHE_DEFAULT_VALUE 0x5311 636e0832faSShawn Lin 646e0832faSShawn Lin #define DOMAIN_OUTER_SHAREABLE 0x2 656e0832faSShawn Lin #define AX_USER_DOMAIN_MASK 0x3 666e0832faSShawn Lin #define AX_USER_DOMAIN_SHIFT 4 676e0832faSShawn Lin 686e0832faSShawn Lin #define to_armada8k_pcie(x) dev_get_drvdata((x)->dev) 696e0832faSShawn Lin 706e0832faSShawn Lin static int armada8k_pcie_link_up(struct dw_pcie *pci) 716e0832faSShawn Lin { 726e0832faSShawn Lin u32 reg; 736e0832faSShawn Lin u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP; 746e0832faSShawn Lin 756e0832faSShawn Lin reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_STATUS_REG); 766e0832faSShawn Lin 776e0832faSShawn Lin if ((reg & mask) == mask) 786e0832faSShawn Lin return 1; 796e0832faSShawn Lin 806e0832faSShawn Lin dev_dbg(pci->dev, "No link detected (Global-Status: 0x%08x).\n", reg); 816e0832faSShawn Lin return 0; 826e0832faSShawn Lin } 836e0832faSShawn Lin 846e0832faSShawn Lin static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie) 856e0832faSShawn Lin { 866e0832faSShawn Lin struct dw_pcie *pci = pcie->pci; 876e0832faSShawn Lin u32 reg; 886e0832faSShawn Lin 896e0832faSShawn Lin if (!dw_pcie_link_up(pci)) { 906e0832faSShawn Lin /* Disable LTSSM state machine to enable configuration */ 916e0832faSShawn Lin reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); 926e0832faSShawn Lin reg &= ~(PCIE_APP_LTSSM_EN); 936e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); 946e0832faSShawn Lin } 956e0832faSShawn Lin 966e0832faSShawn Lin /* Set the device to root complex mode */ 976e0832faSShawn Lin reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); 986e0832faSShawn Lin reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT); 996e0832faSShawn Lin reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT; 1006e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); 1016e0832faSShawn Lin 1026e0832faSShawn Lin /* Set the PCIe master AxCache attributes */ 1036e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE); 1046e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE); 1056e0832faSShawn Lin 1066e0832faSShawn Lin /* Set the PCIe master AxDomain attributes */ 1076e0832faSShawn Lin reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG); 1086e0832faSShawn Lin reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); 1096e0832faSShawn Lin reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; 1106e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg); 1116e0832faSShawn Lin 1126e0832faSShawn Lin reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG); 1136e0832faSShawn Lin reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); 1146e0832faSShawn Lin reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; 1156e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg); 1166e0832faSShawn Lin 1176e0832faSShawn Lin /* Enable INT A-D interrupts */ 1186e0832faSShawn Lin reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG); 1196e0832faSShawn Lin reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK | 1206e0832faSShawn Lin PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK; 1216e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg); 1226e0832faSShawn Lin 1236e0832faSShawn Lin if (!dw_pcie_link_up(pci)) { 1246e0832faSShawn Lin /* Configuration done. Start LTSSM */ 1256e0832faSShawn Lin reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG); 1266e0832faSShawn Lin reg |= PCIE_APP_LTSSM_EN; 1276e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg); 1286e0832faSShawn Lin } 1296e0832faSShawn Lin 1306e0832faSShawn Lin /* Wait until the link becomes active again */ 1316e0832faSShawn Lin if (dw_pcie_wait_for_link(pci)) 1326e0832faSShawn Lin dev_err(pci->dev, "Link not up after reconfiguration\n"); 1336e0832faSShawn Lin } 1346e0832faSShawn Lin 1356e0832faSShawn Lin static int armada8k_pcie_host_init(struct pcie_port *pp) 1366e0832faSShawn Lin { 1376e0832faSShawn Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 1386e0832faSShawn Lin struct armada8k_pcie *pcie = to_armada8k_pcie(pci); 1396e0832faSShawn Lin 1406e0832faSShawn Lin dw_pcie_setup_rc(pp); 1416e0832faSShawn Lin armada8k_pcie_establish_link(pcie); 1426e0832faSShawn Lin 1436e0832faSShawn Lin return 0; 1446e0832faSShawn Lin } 1456e0832faSShawn Lin 1466e0832faSShawn Lin static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) 1476e0832faSShawn Lin { 1486e0832faSShawn Lin struct armada8k_pcie *pcie = arg; 1496e0832faSShawn Lin struct dw_pcie *pci = pcie->pci; 1506e0832faSShawn Lin u32 val; 1516e0832faSShawn Lin 1526e0832faSShawn Lin /* 1536e0832faSShawn Lin * Interrupts are directly handled by the device driver of the 1546e0832faSShawn Lin * PCI device. However, they are also latched into the PCIe 1556e0832faSShawn Lin * controller, so we simply discard them. 1566e0832faSShawn Lin */ 1576e0832faSShawn Lin val = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG); 1586e0832faSShawn Lin dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG, val); 1596e0832faSShawn Lin 1606e0832faSShawn Lin return IRQ_HANDLED; 1616e0832faSShawn Lin } 1626e0832faSShawn Lin 1636e0832faSShawn Lin static const struct dw_pcie_host_ops armada8k_pcie_host_ops = { 1646e0832faSShawn Lin .host_init = armada8k_pcie_host_init, 1656e0832faSShawn Lin }; 1666e0832faSShawn Lin 1676e0832faSShawn Lin static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, 1686e0832faSShawn Lin struct platform_device *pdev) 1696e0832faSShawn Lin { 1706e0832faSShawn Lin struct dw_pcie *pci = pcie->pci; 1716e0832faSShawn Lin struct pcie_port *pp = &pci->pp; 1726e0832faSShawn Lin struct device *dev = &pdev->dev; 1736e0832faSShawn Lin int ret; 1746e0832faSShawn Lin 1756e0832faSShawn Lin pp->ops = &armada8k_pcie_host_ops; 1766e0832faSShawn Lin 1776e0832faSShawn Lin pp->irq = platform_get_irq(pdev, 0); 1786e0832faSShawn Lin if (pp->irq < 0) { 1796e0832faSShawn Lin dev_err(dev, "failed to get irq for port\n"); 1806e0832faSShawn Lin return pp->irq; 1816e0832faSShawn Lin } 1826e0832faSShawn Lin 1836e0832faSShawn Lin ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler, 1846e0832faSShawn Lin IRQF_SHARED, "armada8k-pcie", pcie); 1856e0832faSShawn Lin if (ret) { 1866e0832faSShawn Lin dev_err(dev, "failed to request irq %d\n", pp->irq); 1876e0832faSShawn Lin return ret; 1886e0832faSShawn Lin } 1896e0832faSShawn Lin 1906e0832faSShawn Lin ret = dw_pcie_host_init(pp); 1916e0832faSShawn Lin if (ret) { 1926e0832faSShawn Lin dev_err(dev, "failed to initialize host: %d\n", ret); 1936e0832faSShawn Lin return ret; 1946e0832faSShawn Lin } 1956e0832faSShawn Lin 1966e0832faSShawn Lin return 0; 1976e0832faSShawn Lin } 1986e0832faSShawn Lin 1996e0832faSShawn Lin static const struct dw_pcie_ops dw_pcie_ops = { 2006e0832faSShawn Lin .link_up = armada8k_pcie_link_up, 2016e0832faSShawn Lin }; 2026e0832faSShawn Lin 2036e0832faSShawn Lin static int armada8k_pcie_probe(struct platform_device *pdev) 2046e0832faSShawn Lin { 2056e0832faSShawn Lin struct dw_pcie *pci; 2066e0832faSShawn Lin struct armada8k_pcie *pcie; 2076e0832faSShawn Lin struct device *dev = &pdev->dev; 2086e0832faSShawn Lin struct resource *base; 2096e0832faSShawn Lin int ret; 2106e0832faSShawn Lin 2116e0832faSShawn Lin pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); 2126e0832faSShawn Lin if (!pcie) 2136e0832faSShawn Lin return -ENOMEM; 2146e0832faSShawn Lin 2156e0832faSShawn Lin pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); 2166e0832faSShawn Lin if (!pci) 2176e0832faSShawn Lin return -ENOMEM; 2186e0832faSShawn Lin 2196e0832faSShawn Lin pci->dev = dev; 2206e0832faSShawn Lin pci->ops = &dw_pcie_ops; 2216e0832faSShawn Lin 2226e0832faSShawn Lin pcie->pci = pci; 2236e0832faSShawn Lin 2246e0832faSShawn Lin pcie->clk = devm_clk_get(dev, NULL); 2256e0832faSShawn Lin if (IS_ERR(pcie->clk)) 2266e0832faSShawn Lin return PTR_ERR(pcie->clk); 2276e0832faSShawn Lin 2286e0832faSShawn Lin ret = clk_prepare_enable(pcie->clk); 2296e0832faSShawn Lin if (ret) 2306e0832faSShawn Lin return ret; 2316e0832faSShawn Lin 2326e0832faSShawn Lin pcie->clk_reg = devm_clk_get(dev, "reg"); 2336e0832faSShawn Lin if (pcie->clk_reg == ERR_PTR(-EPROBE_DEFER)) { 2346e0832faSShawn Lin ret = -EPROBE_DEFER; 2356e0832faSShawn Lin goto fail; 2366e0832faSShawn Lin } 2376e0832faSShawn Lin if (!IS_ERR(pcie->clk_reg)) { 2386e0832faSShawn Lin ret = clk_prepare_enable(pcie->clk_reg); 2396e0832faSShawn Lin if (ret) 2406e0832faSShawn Lin goto fail_clkreg; 2416e0832faSShawn Lin } 2426e0832faSShawn Lin 2436e0832faSShawn Lin /* Get the dw-pcie unit configuration/control registers base. */ 2446e0832faSShawn Lin base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); 2456e0832faSShawn Lin pci->dbi_base = devm_pci_remap_cfg_resource(dev, base); 2466e0832faSShawn Lin if (IS_ERR(pci->dbi_base)) { 2476e0832faSShawn Lin dev_err(dev, "couldn't remap regs base %p\n", base); 2486e0832faSShawn Lin ret = PTR_ERR(pci->dbi_base); 2496e0832faSShawn Lin goto fail_clkreg; 2506e0832faSShawn Lin } 2516e0832faSShawn Lin 2526e0832faSShawn Lin platform_set_drvdata(pdev, pcie); 2536e0832faSShawn Lin 2546e0832faSShawn Lin ret = armada8k_add_pcie_port(pcie, pdev); 2556e0832faSShawn Lin if (ret) 2566e0832faSShawn Lin goto fail_clkreg; 2576e0832faSShawn Lin 2586e0832faSShawn Lin return 0; 2596e0832faSShawn Lin 2606e0832faSShawn Lin fail_clkreg: 2616e0832faSShawn Lin clk_disable_unprepare(pcie->clk_reg); 2626e0832faSShawn Lin fail: 2636e0832faSShawn Lin clk_disable_unprepare(pcie->clk); 2646e0832faSShawn Lin 2656e0832faSShawn Lin return ret; 2666e0832faSShawn Lin } 2676e0832faSShawn Lin 2686e0832faSShawn Lin static const struct of_device_id armada8k_pcie_of_match[] = { 2696e0832faSShawn Lin { .compatible = "marvell,armada8k-pcie", }, 2706e0832faSShawn Lin {}, 2716e0832faSShawn Lin }; 2726e0832faSShawn Lin 2736e0832faSShawn Lin static struct platform_driver armada8k_pcie_driver = { 2746e0832faSShawn Lin .probe = armada8k_pcie_probe, 2756e0832faSShawn Lin .driver = { 2766e0832faSShawn Lin .name = "armada8k-pcie", 2776e0832faSShawn Lin .of_match_table = of_match_ptr(armada8k_pcie_of_match), 2786e0832faSShawn Lin .suppress_bind_attrs = true, 2796e0832faSShawn Lin }, 2806e0832faSShawn Lin }; 2816e0832faSShawn Lin builtin_platform_driver(armada8k_pcie_driver); 282