1*df5d8fb6SRandolph Lin // SPDX-License-Identifier: GPL-2.0 2*df5d8fb6SRandolph Lin /* 3*df5d8fb6SRandolph Lin * Driver for the PCIe Controller in QiLai from Andes 4*df5d8fb6SRandolph Lin * 5*df5d8fb6SRandolph Lin * Copyright (C) 2026 Andes Technology Corporation 6*df5d8fb6SRandolph Lin */ 7*df5d8fb6SRandolph Lin 8*df5d8fb6SRandolph Lin #include <linux/bitfield.h> 9*df5d8fb6SRandolph Lin #include <linux/bits.h> 10*df5d8fb6SRandolph Lin #include <linux/kernel.h> 11*df5d8fb6SRandolph Lin #include <linux/module.h> 12*df5d8fb6SRandolph Lin #include <linux/pci.h> 13*df5d8fb6SRandolph Lin #include <linux/platform_device.h> 14*df5d8fb6SRandolph Lin #include <linux/pm_runtime.h> 15*df5d8fb6SRandolph Lin #include <linux/types.h> 16*df5d8fb6SRandolph Lin 17*df5d8fb6SRandolph Lin #include "pcie-designware.h" 18*df5d8fb6SRandolph Lin 19*df5d8fb6SRandolph Lin #define PCIE_INTR_CONTROL1 0x15c 20*df5d8fb6SRandolph Lin #define PCIE_MSI_CTRL_INT_EN BIT(28) 21*df5d8fb6SRandolph Lin 22*df5d8fb6SRandolph Lin #define PCIE_LOGIC_COHERENCY_CONTROL3 0x8e8 23*df5d8fb6SRandolph Lin 24*df5d8fb6SRandolph Lin /* 25*df5d8fb6SRandolph Lin * Refer to Table A4-5 (Memory type encoding) in the 26*df5d8fb6SRandolph Lin * AMBA AXI and ACE Protocol Specification. 27*df5d8fb6SRandolph Lin * 28*df5d8fb6SRandolph Lin * The selected value corresponds to the Memory type field: 29*df5d8fb6SRandolph Lin * "Write-back, Read and Write-allocate". 30*df5d8fb6SRandolph Lin * 31*df5d8fb6SRandolph Lin * The last three rows in the table A4-5 in 32*df5d8fb6SRandolph Lin * AMBA AXI and ACE Protocol Specification: 33*df5d8fb6SRandolph Lin * ARCACHE AWCACHE Memory type 34*df5d8fb6SRandolph Lin * ------------------------------------------------------------------ 35*df5d8fb6SRandolph Lin * 1111 (0111) 0111 Write-back Read-allocate 36*df5d8fb6SRandolph Lin * 1011 1111 (1011) Write-back Write-allocate 37*df5d8fb6SRandolph Lin * 1111 1111 Write-back Read and Write-allocate (selected) 38*df5d8fb6SRandolph Lin */ 39*df5d8fb6SRandolph Lin #define IOCP_ARCACHE 0b1111 40*df5d8fb6SRandolph Lin #define IOCP_AWCACHE 0b1111 41*df5d8fb6SRandolph Lin 42*df5d8fb6SRandolph Lin #define PCIE_CFG_MSTR_ARCACHE_MODE GENMASK(6, 3) 43*df5d8fb6SRandolph Lin #define PCIE_CFG_MSTR_AWCACHE_MODE GENMASK(14, 11) 44*df5d8fb6SRandolph Lin #define PCIE_CFG_MSTR_ARCACHE_VALUE GENMASK(22, 19) 45*df5d8fb6SRandolph Lin #define PCIE_CFG_MSTR_AWCACHE_VALUE GENMASK(30, 27) 46*df5d8fb6SRandolph Lin 47*df5d8fb6SRandolph Lin #define PCIE_GEN_CONTROL2 0x54 48*df5d8fb6SRandolph Lin #define PCIE_CFG_LTSSM_EN BIT(0) 49*df5d8fb6SRandolph Lin 50*df5d8fb6SRandolph Lin #define PCIE_REGS_PCIE_SII_PM_STATE 0xc0 51*df5d8fb6SRandolph Lin #define SMLH_LINK_UP BIT(6) 52*df5d8fb6SRandolph Lin #define RDLH_LINK_UP BIT(7) 53*df5d8fb6SRandolph Lin 54*df5d8fb6SRandolph Lin struct qilai_pcie { 55*df5d8fb6SRandolph Lin struct dw_pcie pci; 56*df5d8fb6SRandolph Lin void __iomem *apb_base; 57*df5d8fb6SRandolph Lin }; 58*df5d8fb6SRandolph Lin 59*df5d8fb6SRandolph Lin #define to_qilai_pcie(_pci) container_of(_pci, struct qilai_pcie, pci) 60*df5d8fb6SRandolph Lin 61*df5d8fb6SRandolph Lin static bool qilai_pcie_link_up(struct dw_pcie *pci) 62*df5d8fb6SRandolph Lin { 63*df5d8fb6SRandolph Lin struct qilai_pcie *pcie = to_qilai_pcie(pci); 64*df5d8fb6SRandolph Lin u32 val; 65*df5d8fb6SRandolph Lin 66*df5d8fb6SRandolph Lin val = readl(pcie->apb_base + PCIE_REGS_PCIE_SII_PM_STATE); 67*df5d8fb6SRandolph Lin 68*df5d8fb6SRandolph Lin return FIELD_GET(SMLH_LINK_UP, val) && FIELD_GET(RDLH_LINK_UP, val); 69*df5d8fb6SRandolph Lin } 70*df5d8fb6SRandolph Lin 71*df5d8fb6SRandolph Lin static int qilai_pcie_start_link(struct dw_pcie *pci) 72*df5d8fb6SRandolph Lin { 73*df5d8fb6SRandolph Lin struct qilai_pcie *pcie = to_qilai_pcie(pci); 74*df5d8fb6SRandolph Lin u32 val; 75*df5d8fb6SRandolph Lin 76*df5d8fb6SRandolph Lin val = readl(pcie->apb_base + PCIE_GEN_CONTROL2); 77*df5d8fb6SRandolph Lin val |= PCIE_CFG_LTSSM_EN; 78*df5d8fb6SRandolph Lin writel(val, pcie->apb_base + PCIE_GEN_CONTROL2); 79*df5d8fb6SRandolph Lin 80*df5d8fb6SRandolph Lin return 0; 81*df5d8fb6SRandolph Lin } 82*df5d8fb6SRandolph Lin 83*df5d8fb6SRandolph Lin static const struct dw_pcie_ops qilai_pcie_ops = { 84*df5d8fb6SRandolph Lin .link_up = qilai_pcie_link_up, 85*df5d8fb6SRandolph Lin .start_link = qilai_pcie_start_link, 86*df5d8fb6SRandolph Lin }; 87*df5d8fb6SRandolph Lin 88*df5d8fb6SRandolph Lin /* 89*df5d8fb6SRandolph Lin * Set up the QiLai PCIe IOCP (IO Coherence Port) Read/Write Behaviors to the 90*df5d8fb6SRandolph Lin * Write-Back, Read and Write Allocate mode. 91*df5d8fb6SRandolph Lin * 92*df5d8fb6SRandolph Lin * The IOCP HW target is SoC last-level cache (L2 Cache), which serves as the 93*df5d8fb6SRandolph Lin * system cache. The IOCP HW helps maintain cache monitoring, ensuring that 94*df5d8fb6SRandolph Lin * the device can snoop data from/to the cache. 95*df5d8fb6SRandolph Lin */ 96*df5d8fb6SRandolph Lin static void qilai_pcie_iocp_cache_setup(struct dw_pcie_rp *pp) 97*df5d8fb6SRandolph Lin { 98*df5d8fb6SRandolph Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 99*df5d8fb6SRandolph Lin u32 val; 100*df5d8fb6SRandolph Lin 101*df5d8fb6SRandolph Lin dw_pcie_dbi_ro_wr_en(pci); 102*df5d8fb6SRandolph Lin 103*df5d8fb6SRandolph Lin val = dw_pcie_readl_dbi(pci, PCIE_LOGIC_COHERENCY_CONTROL3); 104*df5d8fb6SRandolph Lin FIELD_MODIFY(PCIE_CFG_MSTR_ARCACHE_MODE, &val, IOCP_ARCACHE); 105*df5d8fb6SRandolph Lin FIELD_MODIFY(PCIE_CFG_MSTR_AWCACHE_MODE, &val, IOCP_AWCACHE); 106*df5d8fb6SRandolph Lin FIELD_MODIFY(PCIE_CFG_MSTR_ARCACHE_VALUE, &val, IOCP_ARCACHE); 107*df5d8fb6SRandolph Lin FIELD_MODIFY(PCIE_CFG_MSTR_AWCACHE_VALUE, &val, IOCP_AWCACHE); 108*df5d8fb6SRandolph Lin dw_pcie_writel_dbi(pci, PCIE_LOGIC_COHERENCY_CONTROL3, val); 109*df5d8fb6SRandolph Lin 110*df5d8fb6SRandolph Lin dw_pcie_dbi_ro_wr_dis(pci); 111*df5d8fb6SRandolph Lin } 112*df5d8fb6SRandolph Lin 113*df5d8fb6SRandolph Lin static void qilai_pcie_enable_msi(struct qilai_pcie *pcie) 114*df5d8fb6SRandolph Lin { 115*df5d8fb6SRandolph Lin u32 val; 116*df5d8fb6SRandolph Lin 117*df5d8fb6SRandolph Lin val = readl(pcie->apb_base + PCIE_INTR_CONTROL1); 118*df5d8fb6SRandolph Lin val |= PCIE_MSI_CTRL_INT_EN; 119*df5d8fb6SRandolph Lin writel(val, pcie->apb_base + PCIE_INTR_CONTROL1); 120*df5d8fb6SRandolph Lin } 121*df5d8fb6SRandolph Lin 122*df5d8fb6SRandolph Lin static int qilai_pcie_host_init(struct dw_pcie_rp *pp) 123*df5d8fb6SRandolph Lin { 124*df5d8fb6SRandolph Lin struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 125*df5d8fb6SRandolph Lin struct qilai_pcie *pcie = to_qilai_pcie(pci); 126*df5d8fb6SRandolph Lin 127*df5d8fb6SRandolph Lin qilai_pcie_enable_msi(pcie); 128*df5d8fb6SRandolph Lin 129*df5d8fb6SRandolph Lin return 0; 130*df5d8fb6SRandolph Lin } 131*df5d8fb6SRandolph Lin 132*df5d8fb6SRandolph Lin static void qilai_pcie_host_post_init(struct dw_pcie_rp *pp) 133*df5d8fb6SRandolph Lin { 134*df5d8fb6SRandolph Lin qilai_pcie_iocp_cache_setup(pp); 135*df5d8fb6SRandolph Lin } 136*df5d8fb6SRandolph Lin 137*df5d8fb6SRandolph Lin static const struct dw_pcie_host_ops qilai_pcie_host_ops = { 138*df5d8fb6SRandolph Lin .init = qilai_pcie_host_init, 139*df5d8fb6SRandolph Lin .post_init = qilai_pcie_host_post_init, 140*df5d8fb6SRandolph Lin }; 141*df5d8fb6SRandolph Lin 142*df5d8fb6SRandolph Lin static int qilai_pcie_probe(struct platform_device *pdev) 143*df5d8fb6SRandolph Lin { 144*df5d8fb6SRandolph Lin struct qilai_pcie *pcie; 145*df5d8fb6SRandolph Lin struct dw_pcie *pci; 146*df5d8fb6SRandolph Lin struct device *dev = &pdev->dev; 147*df5d8fb6SRandolph Lin int ret; 148*df5d8fb6SRandolph Lin 149*df5d8fb6SRandolph Lin pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); 150*df5d8fb6SRandolph Lin if (!pcie) 151*df5d8fb6SRandolph Lin return -ENOMEM; 152*df5d8fb6SRandolph Lin 153*df5d8fb6SRandolph Lin platform_set_drvdata(pdev, pcie); 154*df5d8fb6SRandolph Lin 155*df5d8fb6SRandolph Lin pci = &pcie->pci; 156*df5d8fb6SRandolph Lin pcie->pci.dev = dev; 157*df5d8fb6SRandolph Lin pcie->pci.ops = &qilai_pcie_ops; 158*df5d8fb6SRandolph Lin pcie->pci.pp.ops = &qilai_pcie_host_ops; 159*df5d8fb6SRandolph Lin pci->use_parent_dt_ranges = true; 160*df5d8fb6SRandolph Lin 161*df5d8fb6SRandolph Lin dw_pcie_cap_set(&pcie->pci, REQ_RES); 162*df5d8fb6SRandolph Lin 163*df5d8fb6SRandolph Lin pcie->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb"); 164*df5d8fb6SRandolph Lin if (IS_ERR(pcie->apb_base)) 165*df5d8fb6SRandolph Lin return PTR_ERR(pcie->apb_base); 166*df5d8fb6SRandolph Lin 167*df5d8fb6SRandolph Lin pm_runtime_set_active(dev); 168*df5d8fb6SRandolph Lin pm_runtime_no_callbacks(dev); 169*df5d8fb6SRandolph Lin devm_pm_runtime_enable(dev); 170*df5d8fb6SRandolph Lin 171*df5d8fb6SRandolph Lin ret = dw_pcie_host_init(&pcie->pci.pp); 172*df5d8fb6SRandolph Lin if (ret) 173*df5d8fb6SRandolph Lin return dev_err_probe(dev, ret, "Failed to initialize PCIe host\n"); 174*df5d8fb6SRandolph Lin 175*df5d8fb6SRandolph Lin return 0; 176*df5d8fb6SRandolph Lin } 177*df5d8fb6SRandolph Lin 178*df5d8fb6SRandolph Lin static const struct of_device_id qilai_pcie_of_match[] = { 179*df5d8fb6SRandolph Lin { .compatible = "andestech,qilai-pcie" }, 180*df5d8fb6SRandolph Lin {}, 181*df5d8fb6SRandolph Lin }; 182*df5d8fb6SRandolph Lin MODULE_DEVICE_TABLE(of, qilai_pcie_of_match); 183*df5d8fb6SRandolph Lin 184*df5d8fb6SRandolph Lin static struct platform_driver qilai_pcie_driver = { 185*df5d8fb6SRandolph Lin .probe = qilai_pcie_probe, 186*df5d8fb6SRandolph Lin .driver = { 187*df5d8fb6SRandolph Lin .name = "qilai-pcie", 188*df5d8fb6SRandolph Lin .of_match_table = qilai_pcie_of_match, 189*df5d8fb6SRandolph Lin .probe_type = PROBE_PREFER_ASYNCHRONOUS, 190*df5d8fb6SRandolph Lin }, 191*df5d8fb6SRandolph Lin }; 192*df5d8fb6SRandolph Lin 193*df5d8fb6SRandolph Lin builtin_platform_driver(qilai_pcie_driver); 194*df5d8fb6SRandolph Lin 195*df5d8fb6SRandolph Lin MODULE_AUTHOR("Randolph Lin <randolph@andestech.com>"); 196*df5d8fb6SRandolph Lin MODULE_DESCRIPTION("Andes QiLai PCIe driver"); 197*df5d8fb6SRandolph Lin MODULE_LICENSE("GPL"); 198