xref: /linux/drivers/pci/controller/dwc/pcie-andes-qilai.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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