]>
Commit | Line | Data |
---|---|---|
bdebb00d MK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * PCIe host bridge driver for Apple system-on-chips. | |
4 | * | |
5 | * The HW is ECAM compliant. | |
6 | * | |
7 | * Initialization requires enabling power and clocks, along with a | |
8 | * number of register pokes. | |
9 | * | |
10 | * Copyright (C) 2021 Alyssa Rosenzweig <[email protected]> | |
11 | * Copyright (C) 2021 Google LLC | |
12 | * Copyright (C) 2021 Corellium LLC | |
13 | * Copyright (C) 2021 Mark Kettenis <[email protected]> | |
14 | * | |
15 | * Author: Alyssa Rosenzweig <[email protected]> | |
16 | * Author: Marc Zyngier <[email protected]> | |
17 | */ | |
18 | ||
19 | #include <common.h> | |
20 | #include <dm.h> | |
21 | #include <dm/device_compat.h> | |
22 | #include <dm/devres.h> | |
23 | #include <mapmem.h> | |
24 | #include <pci.h> | |
25 | #include <asm/io.h> | |
26 | #include <asm-generic/gpio.h> | |
27 | #include <linux/delay.h> | |
28 | #include <linux/iopoll.h> | |
29 | ||
30 | #define CORE_RC_PHYIF_CTL 0x00024 | |
31 | #define CORE_RC_PHYIF_CTL_RUN BIT(0) | |
32 | #define CORE_RC_PHYIF_STAT 0x00028 | |
33 | #define CORE_RC_PHYIF_STAT_REFCLK BIT(4) | |
34 | #define CORE_RC_CTL 0x00050 | |
35 | #define CORE_RC_CTL_RUN BIT(0) | |
36 | #define CORE_RC_STAT 0x00058 | |
37 | #define CORE_RC_STAT_READY BIT(0) | |
38 | #define CORE_FABRIC_STAT 0x04000 | |
39 | #define CORE_FABRIC_STAT_MASK 0x001F001F | |
159f4157 MK |
40 | |
41 | #define CORE_PHY_DEFAULT_BASE(port) (0x84000 + 0x4000 * (port)) | |
42 | ||
43 | #define PHY_LANE_CFG 0x00000 | |
44 | #define PHY_LANE_CFG_REFCLK0REQ BIT(0) | |
45 | #define PHY_LANE_CFG_REFCLK1REQ BIT(1) | |
46 | #define PHY_LANE_CFG_REFCLK0ACK BIT(2) | |
47 | #define PHY_LANE_CFG_REFCLK1ACK BIT(3) | |
48 | #define PHY_LANE_CFG_REFCLKEN (BIT(9) | BIT(10)) | |
49 | #define PHY_LANE_CFG_REFCLKCGEN (BIT(30) | BIT(31)) | |
50 | #define PHY_LANE_CTL 0x00004 | |
51 | #define PHY_LANE_CTL_CFGACC BIT(15) | |
bdebb00d MK |
52 | |
53 | #define PORT_LTSSMCTL 0x00080 | |
54 | #define PORT_LTSSMCTL_START BIT(0) | |
55 | #define PORT_INTSTAT 0x00100 | |
56 | #define PORT_INT_TUNNEL_ERR 31 | |
57 | #define PORT_INT_CPL_TIMEOUT 23 | |
58 | #define PORT_INT_RID2SID_MAPERR 22 | |
59 | #define PORT_INT_CPL_ABORT 21 | |
60 | #define PORT_INT_MSI_BAD_DATA 19 | |
61 | #define PORT_INT_MSI_ERR 18 | |
62 | #define PORT_INT_REQADDR_GT32 17 | |
63 | #define PORT_INT_AF_TIMEOUT 15 | |
64 | #define PORT_INT_LINK_DOWN 14 | |
65 | #define PORT_INT_LINK_UP 12 | |
66 | #define PORT_INT_LINK_BWMGMT 11 | |
67 | #define PORT_INT_AER_MASK (15 << 4) | |
68 | #define PORT_INT_PORT_ERR 4 | |
69 | #define PORT_INT_INTx(i) i | |
70 | #define PORT_INT_INTx_MASK 15 | |
71 | #define PORT_INTMSK 0x00104 | |
72 | #define PORT_INTMSKSET 0x00108 | |
73 | #define PORT_INTMSKCLR 0x0010c | |
74 | #define PORT_MSICFG 0x00124 | |
75 | #define PORT_MSICFG_EN BIT(0) | |
76 | #define PORT_MSICFG_L2MSINUM_SHIFT 4 | |
77 | #define PORT_MSIBASE 0x00128 | |
78 | #define PORT_MSIBASE_1_SHIFT 16 | |
79 | #define PORT_MSIADDR 0x00168 | |
80 | #define PORT_LINKSTS 0x00208 | |
81 | #define PORT_LINKSTS_UP BIT(0) | |
82 | #define PORT_LINKSTS_BUSY BIT(2) | |
83 | #define PORT_LINKCMDSTS 0x00210 | |
84 | #define PORT_OUTS_NPREQS 0x00284 | |
85 | #define PORT_OUTS_NPREQS_REQ BIT(24) | |
86 | #define PORT_OUTS_NPREQS_CPL BIT(16) | |
87 | #define PORT_RXWR_FIFO 0x00288 | |
88 | #define PORT_RXWR_FIFO_HDR GENMASK(15, 10) | |
89 | #define PORT_RXWR_FIFO_DATA GENMASK(9, 0) | |
90 | #define PORT_RXRD_FIFO 0x0028C | |
91 | #define PORT_RXRD_FIFO_REQ GENMASK(6, 0) | |
92 | #define PORT_OUTS_CPLS 0x00290 | |
93 | #define PORT_OUTS_CPLS_SHRD GENMASK(14, 8) | |
94 | #define PORT_OUTS_CPLS_WAIT GENMASK(6, 0) | |
95 | #define PORT_APPCLK 0x00800 | |
96 | #define PORT_APPCLK_EN BIT(0) | |
97 | #define PORT_APPCLK_CGDIS BIT(8) | |
98 | #define PORT_STATUS 0x00804 | |
99 | #define PORT_STATUS_READY BIT(0) | |
100 | #define PORT_REFCLK 0x00810 | |
101 | #define PORT_REFCLK_EN BIT(0) | |
102 | #define PORT_REFCLK_CGDIS BIT(8) | |
103 | #define PORT_PERST 0x00814 | |
104 | #define PORT_PERST_OFF BIT(0) | |
105 | #define PORT_RID2SID(i16) (0x00828 + 4 * (i16)) | |
106 | #define PORT_RID2SID_VALID BIT(31) | |
107 | #define PORT_RID2SID_SID_SHIFT 16 | |
108 | #define PORT_RID2SID_BUS_SHIFT 8 | |
109 | #define PORT_RID2SID_DEV_SHIFT 3 | |
110 | #define PORT_RID2SID_FUNC_SHIFT 0 | |
111 | #define PORT_OUTS_PREQS_HDR 0x00980 | |
112 | #define PORT_OUTS_PREQS_HDR_MASK GENMASK(9, 0) | |
113 | #define PORT_OUTS_PREQS_DATA 0x00984 | |
114 | #define PORT_OUTS_PREQS_DATA_MASK GENMASK(15, 0) | |
115 | #define PORT_TUNCTRL 0x00988 | |
116 | #define PORT_TUNCTRL_PERST_ON BIT(0) | |
117 | #define PORT_TUNCTRL_PERST_ACK_REQ BIT(1) | |
118 | #define PORT_TUNSTAT 0x0098c | |
119 | #define PORT_TUNSTAT_PERST_ON BIT(0) | |
120 | #define PORT_TUNSTAT_PERST_ACK_PEND BIT(1) | |
121 | #define PORT_PREFMEM_ENABLE 0x00994 | |
122 | ||
159f4157 MK |
123 | struct reg_info { |
124 | u32 phy_lane_ctl; | |
125 | u32 port_refclk; | |
126 | u32 port_perst; | |
127 | }; | |
128 | ||
129 | const struct reg_info t8103_hw = { | |
130 | .phy_lane_ctl = PHY_LANE_CTL, | |
131 | .port_refclk = PORT_REFCLK, | |
132 | .port_perst = PORT_PERST, | |
133 | }; | |
134 | ||
135 | #define PORT_T602X_PERST 0x082c | |
136 | ||
137 | const struct reg_info t602x_hw = { | |
138 | .phy_lane_ctl = 0, | |
139 | .port_refclk = 0, | |
140 | .port_perst = PORT_T602X_PERST, | |
141 | }; | |
142 | ||
bdebb00d MK |
143 | struct apple_pcie_priv { |
144 | struct udevice *dev; | |
145 | void __iomem *base; | |
146 | void __iomem *cfg_base; | |
147 | struct list_head ports; | |
159f4157 | 148 | const struct reg_info *hw; |
bdebb00d MK |
149 | }; |
150 | ||
151 | struct apple_pcie_port { | |
152 | struct apple_pcie_priv *pcie; | |
153 | struct gpio_desc reset; | |
154 | ofnode np; | |
155 | void __iomem *base; | |
159f4157 | 156 | void __iomem *phy; |
bdebb00d MK |
157 | struct list_head entry; |
158 | int idx; | |
159 | }; | |
160 | ||
161 | static void rmw_set(u32 set, void __iomem *addr) | |
162 | { | |
163 | writel_relaxed(readl_relaxed(addr) | set, addr); | |
164 | } | |
165 | ||
166 | static void rmw_clear(u32 clr, void __iomem *addr) | |
167 | { | |
168 | writel_relaxed(readl_relaxed(addr) & ~clr, addr); | |
169 | } | |
170 | ||
171 | static int apple_pcie_config_address(const struct udevice *bus, | |
172 | pci_dev_t bdf, uint offset, | |
173 | void **paddress) | |
174 | { | |
175 | struct apple_pcie_priv *pcie = dev_get_priv(bus); | |
176 | void *addr; | |
177 | ||
178 | addr = pcie->cfg_base; | |
179 | addr += PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), | |
180 | PCI_FUNC(bdf), offset); | |
181 | *paddress = addr; | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
186 | static int apple_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, | |
187 | uint offset, ulong *valuep, | |
188 | enum pci_size_t size) | |
189 | { | |
190 | int ret; | |
191 | ||
192 | ret = pci_generic_mmap_read_config(bus, apple_pcie_config_address, | |
193 | bdf, offset, valuep, size); | |
194 | return ret; | |
195 | } | |
196 | ||
197 | static int apple_pcie_write_config(struct udevice *bus, pci_dev_t bdf, | |
198 | uint offset, ulong value, | |
199 | enum pci_size_t size) | |
200 | { | |
201 | return pci_generic_mmap_write_config(bus, apple_pcie_config_address, | |
202 | bdf, offset, value, size); | |
203 | } | |
204 | ||
205 | static const struct dm_pci_ops apple_pcie_ops = { | |
206 | .read_config = apple_pcie_read_config, | |
207 | .write_config = apple_pcie_write_config, | |
208 | }; | |
209 | ||
210 | static int apple_pcie_setup_refclk(struct apple_pcie_priv *pcie, | |
211 | struct apple_pcie_port *port) | |
212 | { | |
213 | u32 stat; | |
214 | int res; | |
215 | ||
159f4157 MK |
216 | if (pcie->hw->phy_lane_ctl) |
217 | rmw_set(PHY_LANE_CTL_CFGACC, port->phy + pcie->hw->phy_lane_ctl); | |
bdebb00d | 218 | |
159f4157 | 219 | rmw_set(PHY_LANE_CFG_REFCLK0REQ, port->phy + PHY_LANE_CFG); |
bdebb00d | 220 | |
159f4157 MK |
221 | res = readl_poll_sleep_timeout(port->phy + PHY_LANE_CFG, |
222 | stat, stat & PHY_LANE_CFG_REFCLK0ACK, | |
bdebb00d MK |
223 | 100, 50000); |
224 | if (res < 0) | |
225 | return res; | |
226 | ||
159f4157 MK |
227 | rmw_set(PHY_LANE_CFG_REFCLK1REQ, port->phy + PHY_LANE_CFG); |
228 | res = readl_poll_sleep_timeout(port->phy + PHY_LANE_CFG, | |
229 | stat, stat & PHY_LANE_CFG_REFCLK1ACK, | |
bdebb00d MK |
230 | 100, 50000); |
231 | ||
232 | if (res < 0) | |
233 | return res; | |
234 | ||
159f4157 MK |
235 | if (pcie->hw->phy_lane_ctl) |
236 | rmw_clear(PHY_LANE_CTL_CFGACC, port->phy + pcie->hw->phy_lane_ctl); | |
237 | ||
238 | rmw_set(PHY_LANE_CFG_REFCLKEN, port->phy + PHY_LANE_CFG); | |
bdebb00d | 239 | |
159f4157 MK |
240 | if (pcie->hw->port_refclk) |
241 | rmw_set(PORT_REFCLK_EN, port->base + pcie->hw->port_refclk); | |
bdebb00d MK |
242 | |
243 | return 0; | |
244 | } | |
245 | ||
246 | static int apple_pcie_setup_port(struct apple_pcie_priv *pcie, ofnode np) | |
247 | { | |
248 | struct apple_pcie_port *port; | |
249 | struct gpio_desc reset; | |
250 | fdt_addr_t addr; | |
251 | u32 stat, idx; | |
252 | int ret; | |
159f4157 | 253 | char name[16]; |
bdebb00d MK |
254 | |
255 | ret = gpio_request_by_name_nodev(np, "reset-gpios", 0, &reset, 0); | |
256 | if (ret) | |
257 | return ret; | |
258 | ||
259 | port = devm_kzalloc(pcie->dev, sizeof(*port), GFP_KERNEL); | |
260 | if (!port) | |
261 | return -ENOMEM; | |
262 | ||
263 | ret = ofnode_read_u32_index(np, "reg", 0, &idx); | |
264 | if (ret) | |
265 | return ret; | |
266 | ||
267 | /* Use the first reg entry to work out the port index */ | |
268 | port->idx = idx >> 11; | |
269 | port->pcie = pcie; | |
270 | port->reset = reset; | |
271 | port->np = np; | |
272 | ||
159f4157 MK |
273 | snprintf(name, sizeof(name), "port%d", port->idx); |
274 | addr = dev_read_addr_name(pcie->dev, name); | |
275 | if (addr == FDT_ADDR_T_NONE) | |
276 | addr = dev_read_addr_index(pcie->dev, port->idx + 2); | |
bdebb00d MK |
277 | if (addr == FDT_ADDR_T_NONE) |
278 | return -EINVAL; | |
279 | port->base = map_sysmem(addr, 0); | |
280 | ||
159f4157 MK |
281 | snprintf(name, sizeof(name), "phy%d", port->idx); |
282 | addr = dev_read_addr_name(pcie->dev, name); | |
283 | if (addr == FDT_ADDR_T_NONE) | |
284 | port->phy = pcie->base + CORE_PHY_DEFAULT_BASE(port->idx); | |
285 | else | |
286 | port->phy = map_sysmem(addr, 0); | |
287 | ||
bdebb00d MK |
288 | rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK); |
289 | ||
290 | /* Assert PERST# before setting up the clock */ | |
291 | dm_gpio_set_value(&reset, 1); | |
292 | ||
293 | ret = apple_pcie_setup_refclk(pcie, port); | |
294 | if (ret < 0) | |
295 | return ret; | |
296 | ||
297 | /* The minimal Tperst-clk value is 100us (PCIe CEM r5.0, 2.9.2) */ | |
298 | udelay(100); | |
299 | ||
300 | /* Deassert PERST# */ | |
159f4157 | 301 | rmw_set(PORT_PERST_OFF, port->base + pcie->hw->port_perst); |
bdebb00d MK |
302 | dm_gpio_set_value(&reset, 0); |
303 | ||
304 | /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ | |
305 | udelay(100 * 1000); | |
306 | ||
307 | ret = readl_poll_sleep_timeout(port->base + PORT_STATUS, stat, | |
308 | stat & PORT_STATUS_READY, 100, 250000); | |
309 | if (ret < 0) { | |
310 | dev_err(pcie->dev, "port %d ready wait timeout\n", port->idx); | |
311 | return ret; | |
312 | } | |
313 | ||
bdebb00d MK |
314 | list_add_tail(&port->entry, &pcie->ports); |
315 | ||
316 | writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL); | |
317 | ||
318 | /* | |
319 | * Deliberately ignore the link not coming up as connected | |
320 | * devices (e.g. the WiFi controller) may not be powerd up. | |
321 | */ | |
322 | readl_poll_sleep_timeout(port->base + PORT_LINKSTS, stat, | |
323 | (stat & PORT_LINKSTS_UP), 100, 100000); | |
324 | ||
159f4157 MK |
325 | if (pcie->hw->port_refclk) |
326 | rmw_clear(PORT_REFCLK_CGDIS, port->base + PORT_REFCLK); | |
327 | else | |
328 | rmw_set(PHY_LANE_CFG_REFCLKCGEN, port->phy + PHY_LANE_CFG); | |
329 | rmw_clear(PORT_APPCLK_CGDIS, port->base + PORT_APPCLK); | |
330 | ||
bdebb00d MK |
331 | return 0; |
332 | } | |
333 | ||
334 | static int apple_pcie_probe(struct udevice *dev) | |
335 | { | |
336 | struct apple_pcie_priv *pcie = dev_get_priv(dev); | |
337 | fdt_addr_t addr; | |
338 | ofnode of_port; | |
339 | int i, ret; | |
340 | ||
159f4157 MK |
341 | pcie->hw = (struct reg_info *)dev_get_driver_data(dev); |
342 | ||
bdebb00d MK |
343 | pcie->dev = dev; |
344 | addr = dev_read_addr_index(dev, 0); | |
345 | if (addr == FDT_ADDR_T_NONE) | |
346 | return -EINVAL; | |
347 | pcie->cfg_base = map_sysmem(addr, 0); | |
348 | ||
349 | addr = dev_read_addr_index(dev, 1); | |
350 | if (addr == FDT_ADDR_T_NONE) | |
351 | return -EINVAL; | |
352 | pcie->base = map_sysmem(addr, 0); | |
353 | ||
354 | INIT_LIST_HEAD(&pcie->ports); | |
355 | ||
356 | for (of_port = ofnode_first_subnode(dev_ofnode(dev)); | |
357 | ofnode_valid(of_port); | |
358 | of_port = ofnode_next_subnode(of_port)) { | |
6ee2c8ad JG |
359 | if (!ofnode_is_enabled(of_port)) |
360 | continue; | |
bdebb00d MK |
361 | ret = apple_pcie_setup_port(pcie, of_port); |
362 | if (ret) { | |
363 | dev_err(pcie->dev, "Port %d setup fail: %d\n", i, ret); | |
364 | return ret; | |
365 | } | |
366 | } | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
371 | static int apple_pcie_remove(struct udevice *dev) | |
372 | { | |
373 | struct apple_pcie_priv *pcie = dev_get_priv(dev); | |
374 | struct apple_pcie_port *port, *tmp; | |
375 | ||
376 | list_for_each_entry_safe(port, tmp, &pcie->ports, entry) { | |
377 | gpio_free_list_nodev(&port->reset, 1); | |
378 | free(port); | |
379 | } | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
384 | static const struct udevice_id apple_pcie_of_match[] = { | |
159f4157 MK |
385 | { .compatible = "apple,t6020-pcie", .data = (ulong)&t602x_hw }, |
386 | { .compatible = "apple,pcie", .data = (ulong)&t8103_hw }, | |
bdebb00d MK |
387 | { /* sentinel */ } |
388 | }; | |
389 | ||
390 | U_BOOT_DRIVER(apple_pcie) = { | |
391 | .name = "apple_pcie", | |
392 | .id = UCLASS_PCI, | |
393 | .of_match = apple_pcie_of_match, | |
394 | .probe = apple_pcie_probe, | |
395 | .remove = apple_pcie_remove, | |
396 | .priv_auto = sizeof(struct apple_pcie_priv), | |
397 | .ops = &apple_pcie_ops, | |
398 | }; |