blob: 51aca7359ff32b73b99e2061e9595338141e7897 [file] [log] [blame]
Mason Huo08059f02023-07-25 17:46:48 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * StarFive PLDA PCIe host controller driver
4 *
5 * Copyright (C) 2023 StarFive Technology Co., Ltd.
6 * Author: Mason Huo <mason.huo@starfivetech.com>
7 *
8 */
9
Mason Huo08059f02023-07-25 17:46:48 +080010#include <clk.h>
11#include <dm.h>
12#include <pci.h>
13#include <pci_ids.h>
14#include <power-domain.h>
15#include <regmap.h>
16#include <reset.h>
17#include <syscon.h>
18#include <asm/global_data.h>
19#include <asm/io.h>
20#include <asm-generic/gpio.h>
21#include <dm/device_compat.h>
22#include <dm/pinctrl.h>
23#include <linux/delay.h>
24#include <linux/iopoll.h>
25#include "pcie_plda_common.h"
26
27/* system control */
Hal Fengf61d4a72024-12-08 17:19:34 +080028#define STG_SYSCON_PCIE0_BASE 0x48
29#define STG_SYSCON_PCIE1_BASE 0x1f8
30
31#define STG_SYSCON_AR_OFFSET 0x78
Mason Huo08059f02023-07-25 17:46:48 +080032#define STG_SYSCON_AXI4_SLVL_ARFUNC_MASK GENMASK(22, 8)
33#define STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT 8
Hal Fengf61d4a72024-12-08 17:19:34 +080034#define STG_SYSCON_AW_OFFSET 0x7c
Mason Huo08059f02023-07-25 17:46:48 +080035#define STG_SYSCON_AXI4_SLVL_AWFUNC_MASK GENMASK(14, 0)
36#define STG_SYSCON_CLKREQ_MASK BIT(22)
37#define STG_SYSCON_CKREF_SRC_SHIFT 18
38#define STG_SYSCON_CKREF_SRC_MASK GENMASK(19, 18)
Hal Fengf61d4a72024-12-08 17:19:34 +080039#define STG_SYSCON_RP_NEP_OFFSET 0xe8
40#define STG_SYSCON_K_RP_NEP_MASK BIT(8)
Mason Huo08059f02023-07-25 17:46:48 +080041
42DECLARE_GLOBAL_DATA_PTR;
43
44struct starfive_pcie {
45 struct pcie_plda plda;
46 struct clk_bulk clks;
47 struct reset_ctl_bulk rsts;
48 struct gpio_desc reset_gpio;
49 struct regmap *regmap;
Hal Fengf61d4a72024-12-08 17:19:34 +080050 unsigned int stg_pcie_base;
Mason Huo08059f02023-07-25 17:46:48 +080051};
52
53static int starfive_pcie_atr_init(struct starfive_pcie *priv)
54{
55 struct udevice *ctlr = pci_get_controller(priv->plda.dev);
56 struct pci_controller *hose = dev_get_uclass_priv(ctlr);
57 int i, ret;
58
59 /*
60 * As the two host bridges in JH7110 soc have the same default
61 * address translation table, this cause the second root port can't
62 * access it's host bridge config space correctly.
63 * To workaround, config the ATR of host bridge config space by SW.
64 */
65
66 ret = plda_pcie_set_atr_entry(&priv->plda,
67 (phys_addr_t)priv->plda.cfg_base, 0,
68 priv->plda.cfg_size,
69 XR3PCI_ATR_TRSLID_PCIE_CONFIG);
70 if (ret)
71 return ret;
72
73 for (i = 0; i < hose->region_count; i++) {
74 if (hose->regions[i].flags == PCI_REGION_SYS_MEMORY)
75 continue;
76
77 /* Only support identity mappings. */
78 if (hose->regions[i].bus_start !=
79 hose->regions[i].phys_start)
80 return -EINVAL;
81
82 ret = plda_pcie_set_atr_entry(&priv->plda,
83 hose->regions[i].phys_start,
84 hose->regions[i].bus_start,
85 hose->regions[i].size,
86 XR3PCI_ATR_TRSLID_PCIE_MEMORY);
87 if (ret)
88 return ret;
89 }
90
91 return 0;
92}
93
94static int starfive_pcie_get_syscon(struct udevice *dev)
95{
96 struct starfive_pcie *priv = dev_get_priv(dev);
97 struct udevice *syscon;
98 struct ofnode_phandle_args syscfg_phandle;
Mason Huo08059f02023-07-25 17:46:48 +080099 int ret;
100
101 /* get corresponding syscon phandle */
102 ret = dev_read_phandle_with_args(dev, "starfive,stg-syscon", NULL, 0, 0,
103 &syscfg_phandle);
104
105 if (ret < 0) {
106 dev_err(dev, "Can't get syscfg phandle: %d\n", ret);
107 return ret;
108 }
109
110 ret = uclass_get_device_by_ofnode(UCLASS_SYSCON, syscfg_phandle.node,
111 &syscon);
112 if (ret) {
113 dev_err(dev, "Unable to find syscon device (%d)\n", ret);
114 return ret;
115 }
116
117 priv->regmap = syscon_get_regmap(syscon);
118 if (!priv->regmap) {
119 dev_err(dev, "Unable to find regmap\n");
120 return -ENODEV;
121 }
122
Mason Huo08059f02023-07-25 17:46:48 +0800123 return 0;
124}
125
126static int starfive_pcie_parse_dt(struct udevice *dev)
127{
128 struct starfive_pcie *priv = dev_get_priv(dev);
129 int ret;
Hal Fengf61d4a72024-12-08 17:19:34 +0800130 u32 domain_nr;
Mason Huo08059f02023-07-25 17:46:48 +0800131
Hal Fengf61d4a72024-12-08 17:19:34 +0800132 priv->plda.reg_base = (void *)dev_read_addr_name(dev, "apb");
Mason Huo08059f02023-07-25 17:46:48 +0800133 if (priv->plda.reg_base == (void __iomem *)FDT_ADDR_T_NONE) {
134 dev_err(dev, "Missing required reg address range\n");
135 return -EINVAL;
136 }
137
138 priv->plda.cfg_base =
139 (void *)dev_read_addr_size_name(dev,
Hal Fengf61d4a72024-12-08 17:19:34 +0800140 "cfg",
Mason Huo08059f02023-07-25 17:46:48 +0800141 &priv->plda.cfg_size);
142 if (priv->plda.cfg_base == (void __iomem *)FDT_ADDR_T_NONE) {
143 dev_err(dev, "Missing required config address range");
144 return -EINVAL;
145 }
146
147 ret = starfive_pcie_get_syscon(dev);
148 if (ret) {
149 dev_err(dev, "Can't get syscon: %d\n", ret);
150 return ret;
151 }
152
153 ret = reset_get_bulk(dev, &priv->rsts);
154 if (ret) {
155 dev_err(dev, "Can't get reset: %d\n", ret);
156 return ret;
157 }
158
159 ret = clk_get_bulk(dev, &priv->clks);
160 if (ret) {
161 dev_err(dev, "Can't get clock: %d\n", ret);
162 return ret;
163 }
164
Hal Fengf61d4a72024-12-08 17:19:34 +0800165 ret = dev_read_u32(dev, "linux,pci-domain", &domain_nr);
166 if (ret) {
167 dev_err(dev, "Can't get pci domain: %d\n", ret);
168 return ret;
169 }
170
171 if (domain_nr == 0)
172 priv->stg_pcie_base = STG_SYSCON_PCIE0_BASE;
173 else
174 priv->stg_pcie_base = STG_SYSCON_PCIE1_BASE;
175
176 ret = gpio_request_by_name(dev, "perst-gpios", 0, &priv->reset_gpio,
Mason Huo08059f02023-07-25 17:46:48 +0800177 GPIOD_IS_OUT);
178 if (ret) {
179 dev_err(dev, "Can't get reset-gpio: %d\n", ret);
180 return ret;
181 }
182
183 if (!dm_gpio_is_valid(&priv->reset_gpio)) {
184 dev_err(dev, "reset-gpio is not valid\n");
185 return -EINVAL;
186 }
187 return 0;
188}
189
190static int starfive_pcie_init_port(struct udevice *dev)
191{
192 int ret, i;
193 struct starfive_pcie *priv = dev_get_priv(dev);
194 struct pcie_plda *plda = &priv->plda;
195
196 ret = clk_enable_bulk(&priv->clks);
197 if (ret) {
198 dev_err(dev, "Failed to enable clks (ret=%d)\n", ret);
199 return ret;
200 }
201
202 ret = reset_deassert_bulk(&priv->rsts);
203 if (ret) {
204 dev_err(dev, "Failed to deassert resets (ret=%d)\n", ret);
205 goto err_deassert_clk;
206 }
207
208 dm_gpio_set_value(&priv->reset_gpio, 1);
209 /* Disable physical functions except #0 */
210 for (i = 1; i < PLDA_FUNC_NUM; i++) {
211 regmap_update_bits(priv->regmap,
Hal Fengf61d4a72024-12-08 17:19:34 +0800212 priv->stg_pcie_base + STG_SYSCON_AR_OFFSET,
Mason Huo08059f02023-07-25 17:46:48 +0800213 STG_SYSCON_AXI4_SLVL_ARFUNC_MASK,
214 (i << PLDA_PHY_FUNC_SHIFT) <<
215 STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT);
216 regmap_update_bits(priv->regmap,
Hal Fengf61d4a72024-12-08 17:19:34 +0800217 priv->stg_pcie_base + STG_SYSCON_AW_OFFSET,
Mason Huo08059f02023-07-25 17:46:48 +0800218 STG_SYSCON_AXI4_SLVL_AWFUNC_MASK,
219 i << PLDA_PHY_FUNC_SHIFT);
220
221 plda_pcie_disable_func(plda);
222 }
223
224 /* Disable physical functions */
225 regmap_update_bits(priv->regmap,
Hal Fengf61d4a72024-12-08 17:19:34 +0800226 priv->stg_pcie_base + STG_SYSCON_AR_OFFSET,
Mason Huo08059f02023-07-25 17:46:48 +0800227 STG_SYSCON_AXI4_SLVL_ARFUNC_MASK,
228 0);
229 regmap_update_bits(priv->regmap,
Hal Fengf61d4a72024-12-08 17:19:34 +0800230 priv->stg_pcie_base + STG_SYSCON_AW_OFFSET,
Mason Huo08059f02023-07-25 17:46:48 +0800231 STG_SYSCON_AXI4_SLVL_AWFUNC_MASK,
232 0);
233
234 plda_pcie_enable_root_port(plda);
235
236 /* PCIe PCI Standard Configuration Identification Settings. */
237 plda_pcie_set_standard_class(plda);
238
239 /*
240 * The LTR message forwarding of PCIe Message Reception was set by core
241 * as default, but the forward id & addr are also need to be reset.
242 * If we do not disable LTR message forwarding here, or set a legal
243 * forwarding address, the kernel will get stuck after this driver probe.
244 * To workaround, disable the LTR message forwarding support on
245 * PCIe Message Reception.
246 */
247 plda_pcie_disable_ltr(plda);
248
249 /* Prefetchable memory window 64-bit addressing support */
250 plda_pcie_set_pref_win_64bit(plda);
251 starfive_pcie_atr_init(priv);
252
253 dm_gpio_set_value(&priv->reset_gpio, 0);
254 /* Ensure that PERST in default at least 300 ms */
255 mdelay(300);
256
257 return 0;
258
259err_deassert_clk:
260 clk_disable_bulk(&priv->clks);
261 return ret;
262}
263
264static int starfive_pcie_probe(struct udevice *dev)
265{
266 struct starfive_pcie *priv = dev_get_priv(dev);
267 int ret;
268
269 priv->plda.atr_table_num = 0;
270 priv->plda.dev = dev;
271
272 ret = starfive_pcie_parse_dt(dev);
273 if (ret)
274 return ret;
275
276 regmap_update_bits(priv->regmap,
Hal Fengf61d4a72024-12-08 17:19:34 +0800277 priv->stg_pcie_base + STG_SYSCON_RP_NEP_OFFSET,
Mason Huo08059f02023-07-25 17:46:48 +0800278 STG_SYSCON_K_RP_NEP_MASK,
279 STG_SYSCON_K_RP_NEP_MASK);
280
281 regmap_update_bits(priv->regmap,
Hal Fengf61d4a72024-12-08 17:19:34 +0800282 priv->stg_pcie_base + STG_SYSCON_AW_OFFSET,
Mason Huo08059f02023-07-25 17:46:48 +0800283 STG_SYSCON_CKREF_SRC_MASK,
284 2 << STG_SYSCON_CKREF_SRC_SHIFT);
285
286 regmap_update_bits(priv->regmap,
Hal Fengf61d4a72024-12-08 17:19:34 +0800287 priv->stg_pcie_base + STG_SYSCON_AW_OFFSET,
Mason Huo08059f02023-07-25 17:46:48 +0800288 STG_SYSCON_CLKREQ_MASK,
289 STG_SYSCON_CLKREQ_MASK);
290
291 ret = starfive_pcie_init_port(dev);
292 if (ret)
293 return ret;
294
295 dev_err(dev, "Starfive PCIe bus probed.\n");
296
297 return 0;
298}
299
300static const struct dm_pci_ops starfive_pcie_ops = {
301 .read_config = plda_pcie_config_read,
302 .write_config = plda_pcie_config_write,
303};
304
305static const struct udevice_id starfive_pcie_ids[] = {
306 { .compatible = "starfive,jh7110-pcie" },
307 { }
308};
309
310U_BOOT_DRIVER(starfive_pcie_drv) = {
311 .name = "starfive_7110_pcie",
312 .id = UCLASS_PCI,
313 .of_match = starfive_pcie_ids,
314 .ops = &starfive_pcie_ops,
315 .probe = starfive_pcie_probe,
316 .priv_auto = sizeof(struct starfive_pcie),
317};