Commit | Line | Data |
---|---|---|
49d8fe07 PO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2023-2024 DENX Software Engineering GmbH | |
4 | * Philip Oberfichtner <pro@denx.de> | |
5 | * | |
6 | * Based on linux v6.6.39, especially drivers/net/ethernet/stmicro/stmmac | |
7 | */ | |
8 | ||
9 | #include <asm/io.h> | |
10 | #include <dm.h> | |
11 | #include <dm/device_compat.h> | |
12 | #include <linux/bitfield.h> | |
13 | #include <linux/delay.h> | |
14 | #include <miiphy.h> | |
15 | #include <net.h> | |
16 | #include <pci.h> | |
17 | ||
18 | #include "dwc_eth_qos.h" | |
19 | #include "dwc_eth_qos_intel.h" | |
20 | ||
21 | static struct pci_device_id intel_pci_ids[] = { | |
22 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_RGMII1G) }, | |
23 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_SGMII1) }, | |
24 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_SGMII2G5) }, | |
25 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_PSE0_RGMII1G) }, | |
26 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_PSE0_SGMII1G) }, | |
27 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_PSE0_SGMII2G5) }, | |
28 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_PSE1_RGMII1G) }, | |
29 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_PSE1_SGMII1G) }, | |
30 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EHL_PSE1_SGMII2G5) }, | |
31 | {} | |
32 | }; | |
33 | ||
34 | static int pci_config(struct udevice *dev) | |
35 | { | |
36 | u32 val; | |
37 | ||
38 | /* Try to enable I/O accesses and bus-mastering */ | |
39 | dm_pci_read_config32(dev, PCI_COMMAND, &val); | |
40 | val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | |
41 | dm_pci_write_config32(dev, PCI_COMMAND, val); | |
42 | ||
43 | /* Make sure it worked */ | |
44 | dm_pci_read_config32(dev, PCI_COMMAND, &val); | |
45 | if (!(val & PCI_COMMAND_MEMORY)) { | |
46 | dev_err(dev, "%s: Can't enable I/O memory\n", __func__); | |
47 | return -ENOSPC; | |
48 | } | |
49 | ||
50 | if (!(val & PCI_COMMAND_MASTER)) { | |
51 | dev_err(dev, "%s: Can't enable bus-mastering\n", __func__); | |
52 | return -EPERM; | |
53 | } | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
58 | static void limit_fifo_size(struct udevice *dev) | |
59 | { | |
60 | /* | |
61 | * As described in Intel Erratum EHL22, Document Number: 636674-2.1, | |
62 | * the PSE GbE Controllers advertise a wrong RX and TX fifo size. | |
63 | * Software should limit this value to 64KB. | |
64 | */ | |
65 | struct eqos_priv *eqos = dev_get_priv(dev); | |
66 | ||
67 | eqos->tx_fifo_sz = 0x8000; | |
68 | eqos->rx_fifo_sz = 0x8000; | |
69 | } | |
70 | ||
71 | static int serdes_status_poll(struct udevice *dev, | |
72 | unsigned char phyaddr, unsigned char phyreg, | |
73 | unsigned short mask, unsigned short val) | |
74 | { | |
75 | struct eqos_priv *eqos = dev_get_priv(dev); | |
76 | unsigned int retries = 10; | |
77 | unsigned short val_rd; | |
78 | ||
79 | do { | |
80 | miiphy_read(eqos->mii->name, phyaddr, phyreg, &val_rd); | |
81 | if ((val_rd & mask) == (val & mask)) | |
82 | return 0; | |
83 | udelay(POLL_DELAY_US); | |
84 | } while (--retries); | |
85 | ||
86 | return -ETIMEDOUT; | |
87 | } | |
88 | ||
89 | /* Returns -ve if MAC is unknown and 0 on success */ | |
90 | static int mac_check_pse(const struct udevice *dev, bool *is_pse) | |
91 | { | |
92 | struct pci_child_plat *plat = dev_get_parent_plat(dev); | |
93 | ||
94 | if (!plat || plat->vendor != PCI_VENDOR_ID_INTEL) | |
95 | return -ENXIO; | |
96 | ||
97 | switch (plat->device) { | |
98 | case PCI_DEVICE_ID_INTEL_EHL_PSE0_RGMII1G: | |
99 | case PCI_DEVICE_ID_INTEL_EHL_PSE1_RGMII1G: | |
100 | case PCI_DEVICE_ID_INTEL_EHL_PSE0_SGMII1G: | |
101 | case PCI_DEVICE_ID_INTEL_EHL_PSE1_SGMII1G: | |
102 | case PCI_DEVICE_ID_INTEL_EHL_PSE0_SGMII2G5: | |
103 | case PCI_DEVICE_ID_INTEL_EHL_PSE1_SGMII2G5: | |
104 | *is_pse = 1; | |
105 | return 0; | |
106 | ||
107 | case PCI_DEVICE_ID_INTEL_EHL_RGMII1G: | |
108 | case PCI_DEVICE_ID_INTEL_EHL_SGMII1: | |
109 | case PCI_DEVICE_ID_INTEL_EHL_SGMII2G5: | |
110 | *is_pse = 0; | |
111 | return 0; | |
112 | }; | |
113 | ||
114 | return -ENXIO; | |
115 | } | |
116 | ||
117 | /* Check if we're in 2G5 mode */ | |
118 | static bool serdes_link_mode_2500(struct udevice *dev) | |
119 | { | |
120 | const unsigned char phyad = INTEL_MGBE_ADHOC_ADDR; | |
121 | struct eqos_priv *eqos = dev_get_priv(dev); | |
122 | unsigned short data; | |
123 | ||
124 | miiphy_read(eqos->mii->name, phyad, SERDES_GCR, &data); | |
125 | if (FIELD_GET(SERDES_LINK_MODE_MASK, data) == SERDES_LINK_MODE_2G5) | |
126 | return true; | |
127 | ||
128 | return false; | |
129 | } | |
130 | ||
131 | static int serdes_powerup(struct udevice *dev) | |
132 | { | |
133 | /* Based on linux/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c */ | |
134 | ||
135 | const unsigned char phyad = INTEL_MGBE_ADHOC_ADDR; | |
136 | struct eqos_priv *eqos = dev_get_priv(dev); | |
137 | unsigned short data; | |
138 | int ret; | |
139 | bool is_pse; | |
140 | ||
141 | /* Set the serdes rate and the PCLK rate */ | |
142 | miiphy_read(eqos->mii->name, phyad, SERDES_GCR0, &data); | |
143 | ||
144 | data &= ~SERDES_RATE_MASK; | |
145 | data &= ~SERDES_PCLK_MASK; | |
146 | ||
147 | if (serdes_link_mode_2500(dev)) | |
148 | data |= SERDES_RATE_PCIE_GEN2 << SERDES_RATE_PCIE_SHIFT | | |
149 | SERDES_PCLK_37p5MHZ << SERDES_PCLK_SHIFT; | |
150 | else | |
151 | data |= SERDES_RATE_PCIE_GEN1 << SERDES_RATE_PCIE_SHIFT | | |
152 | SERDES_PCLK_70MHZ << SERDES_PCLK_SHIFT; | |
153 | ||
154 | miiphy_write(eqos->mii->name, phyad, SERDES_GCR0, data); | |
155 | ||
156 | /* assert clk_req */ | |
157 | miiphy_read(eqos->mii->name, phyad, SERDES_GCR0, &data); | |
158 | data |= SERDES_PLL_CLK; | |
159 | miiphy_write(eqos->mii->name, phyad, SERDES_GCR0, data); | |
160 | ||
161 | /* check for clk_ack assertion */ | |
162 | ret = serdes_status_poll(dev, phyad, SERDES_GSR0, | |
163 | SERDES_PLL_CLK, SERDES_PLL_CLK); | |
164 | ||
165 | if (ret) { | |
166 | dev_err(dev, "Serdes PLL clk request timeout\n"); | |
167 | return ret; | |
168 | } | |
169 | ||
170 | /* assert lane reset*/ | |
171 | miiphy_read(eqos->mii->name, phyad, SERDES_GCR0, &data); | |
172 | data |= SERDES_RST; | |
173 | miiphy_write(eqos->mii->name, phyad, SERDES_GCR0, data); | |
174 | ||
175 | /* check for assert lane reset reflection */ | |
176 | ret = serdes_status_poll(dev, phyad, SERDES_GSR0, | |
177 | SERDES_RST, SERDES_RST); | |
178 | ||
179 | if (ret) { | |
180 | dev_err(dev, "Serdes assert lane reset timeout\n"); | |
181 | return ret; | |
182 | } | |
183 | ||
184 | /* move power state to P0 */ | |
185 | miiphy_read(eqos->mii->name, phyad, SERDES_GCR0, &data); | |
186 | data &= ~SERDES_PWR_ST_MASK; | |
187 | data |= SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT; | |
188 | miiphy_write(eqos->mii->name, phyad, SERDES_GCR0, data); | |
189 | ||
190 | /* Check for P0 state */ | |
191 | ret = serdes_status_poll(dev, phyad, SERDES_GSR0, | |
192 | SERDES_PWR_ST_MASK, | |
193 | SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT); | |
194 | ||
195 | if (ret) { | |
196 | dev_err(dev, "Serdes power state P0 timeout.\n"); | |
197 | return ret; | |
198 | } | |
199 | ||
200 | /* PSE only - ungate SGMII PHY Rx Clock*/ | |
201 | ret = mac_check_pse(dev, &is_pse); | |
202 | if (ret) { | |
203 | dev_err(dev, "Failed to determine MAC type.\n"); | |
204 | return ret; | |
205 | } | |
206 | ||
207 | if (is_pse) { | |
208 | miiphy_read(eqos->mii->name, phyad, SERDES_GCR0, &data); | |
209 | data |= SERDES_PHY_RX_CLK; | |
210 | miiphy_write(eqos->mii->name, phyad, SERDES_GCR0, data); | |
211 | } | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
216 | static int xpcs_access(struct udevice *dev, int reg, int v) | |
217 | { | |
218 | /* | |
219 | * Common read/write helper function | |
220 | * | |
221 | * It may seem a bit odd at a first glance that we use bus->read() | |
222 | * directly insetad of one of the wrapper functions. But: | |
223 | * | |
224 | * (1) phy_read() can't be used because we do not access an acutal PHY, | |
225 | * but a MAC-internal submodule. | |
226 | * | |
227 | * (2) miiphy_read() can't be used because it assumes MDIO_DEVAD_NONE. | |
228 | */ | |
229 | ||
230 | int port = INTEL_MGBE_XPCS_ADDR; | |
231 | int devad = 0x1f; | |
232 | u16 val; | |
233 | struct eqos_priv *eqos; | |
234 | struct mii_dev *bus; | |
235 | ||
236 | eqos = dev_get_priv(dev); | |
237 | bus = eqos->mii; | |
238 | ||
239 | if (v < 0) | |
240 | return bus->read(bus, port, devad, reg); | |
241 | ||
242 | val = v; | |
243 | return bus->write(bus, port, devad, reg, val); | |
244 | } | |
245 | ||
246 | static int xpcs_read(struct udevice *dev, int reg) | |
247 | { | |
248 | return xpcs_access(dev, reg, -1); | |
249 | } | |
250 | ||
251 | static int xpcs_write(struct udevice *dev, int reg, u16 val) | |
252 | { | |
253 | return xpcs_access(dev, reg, val); | |
254 | } | |
255 | ||
256 | static int xpcs_clr_bits(struct udevice *dev, int reg, u16 bits) | |
257 | { | |
258 | int ret; | |
259 | ||
260 | ret = xpcs_read(dev, reg); | |
261 | if (ret < 0) | |
262 | return ret; | |
263 | ||
264 | ret &= ~bits; | |
265 | ||
266 | return xpcs_write(dev, reg, ret); | |
267 | } | |
268 | ||
269 | static int xpcs_set_bits(struct udevice *dev, int reg, u16 bits) | |
270 | { | |
271 | int ret; | |
272 | ||
273 | ret = xpcs_read(dev, reg); | |
274 | if (ret < 0) | |
275 | return ret; | |
276 | ||
277 | ret |= bits; | |
278 | ||
279 | return xpcs_write(dev, reg, ret); | |
280 | } | |
281 | ||
282 | static int xpcs_init(struct udevice *dev) | |
283 | { | |
284 | /* Based on linux/drivers/net/pcs/pcs-xpcs.c */ | |
285 | struct eqos_priv *eqos = dev_get_priv(dev); | |
286 | phy_interface_t interface = eqos->config->interface(dev); | |
287 | ||
288 | if (interface != PHY_INTERFACE_MODE_SGMII) | |
289 | return 0; | |
290 | ||
291 | if (xpcs_clr_bits(dev, VR_MII_MMD_CTRL, XPCS_AN_CL37_EN) || | |
292 | xpcs_set_bits(dev, VR_MII_AN_CTRL, XPCS_MODE_SGMII) || | |
293 | xpcs_set_bits(dev, VR_MII_DIG_CTRL1, XPCS_MAC_AUTO_SW) || | |
294 | xpcs_set_bits(dev, VR_MII_MMD_CTRL, XPCS_AN_CL37_EN)) | |
295 | return -EIO; | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
300 | static int eqos_probe_ressources_intel(struct udevice *dev) | |
301 | { | |
302 | int ret; | |
303 | ||
304 | ret = eqos_get_base_addr_pci(dev); | |
305 | if (ret) { | |
306 | dev_err(dev, "eqos_get_base_addr_pci failed: %d\n", ret); | |
307 | return ret; | |
308 | } | |
309 | ||
310 | limit_fifo_size(dev); | |
311 | ||
312 | ret = pci_config(dev); | |
313 | if (ret) { | |
314 | dev_err(dev, "pci_config failed: %d\n", ret); | |
315 | return ret; | |
316 | } | |
317 | ||
318 | return 0; | |
319 | } | |
320 | ||
321 | struct eqos_config eqos_intel_config; | |
322 | ||
323 | /* | |
324 | * overwrite __weak function from eqos_intel.c | |
325 | * | |
326 | * For PCI devices the devcie tree is optional. Choose driver data based on PCI | |
327 | * IDs instead. | |
328 | */ | |
329 | void *eqos_get_driver_data(struct udevice *dev) | |
330 | { | |
331 | const struct pci_device_id *id; | |
332 | const struct pci_child_plat *plat; | |
333 | ||
334 | plat = dev_get_parent_plat(dev); | |
335 | ||
336 | if (!plat) | |
337 | return NULL; | |
338 | ||
339 | /* last intel_pci_ids element is zero initialized */ | |
340 | for (id = intel_pci_ids; id->vendor != 0; id++) { | |
341 | if (id->vendor == plat->vendor && id->device == plat->device) | |
342 | return &eqos_intel_config; | |
343 | } | |
344 | ||
345 | return NULL; | |
346 | } | |
347 | ||
348 | static int eqos_start_resets_intel(struct udevice *dev) | |
349 | { | |
350 | int ret; | |
351 | ||
352 | ret = xpcs_init(dev); | |
353 | if (ret) { | |
354 | dev_err(dev, "xpcs init failed.\n"); | |
355 | return ret; | |
356 | } | |
357 | ||
358 | ret = serdes_powerup(dev); | |
359 | if (ret) { | |
360 | dev_err(dev, "Failed to power up serdes.\n"); | |
361 | return ret; | |
362 | } | |
363 | ||
364 | return 0; | |
365 | } | |
366 | ||
367 | static ulong eqos_get_tick_clk_rate_intel(struct udevice *dev) | |
368 | { | |
369 | return 0; | |
370 | } | |
371 | ||
372 | static int eqos_get_enetaddr_intel(struct udevice *dev) | |
373 | { | |
374 | /* Assume MAC address is programmed by previous boot stage */ | |
375 | struct eth_pdata *plat = dev_get_plat(dev); | |
376 | struct eqos_priv *eqos = dev_get_priv(dev); | |
377 | u8 *lo = (u8 *)&eqos->mac_regs->address0_low; | |
378 | u8 *hi = (u8 *)&eqos->mac_regs->address0_high; | |
379 | ||
380 | plat->enetaddr[0] = lo[0]; | |
381 | plat->enetaddr[1] = lo[1]; | |
382 | plat->enetaddr[2] = lo[2]; | |
383 | plat->enetaddr[3] = lo[3]; | |
384 | plat->enetaddr[4] = hi[0]; | |
385 | plat->enetaddr[5] = hi[1]; | |
386 | ||
387 | return 0; | |
388 | } | |
389 | ||
390 | static phy_interface_t eqos_get_interface_intel(const struct udevice *dev) | |
391 | { | |
392 | struct pci_child_plat *plat = dev_get_parent_plat(dev); | |
393 | ||
394 | if (!plat || plat->vendor != PCI_VENDOR_ID_INTEL) | |
395 | return PHY_INTERFACE_MODE_NA; | |
396 | ||
397 | switch (plat->device) { | |
398 | /* The GbE Host Controller has no RGMII interface */ | |
399 | case PCI_DEVICE_ID_INTEL_EHL_RGMII1G: | |
400 | return PHY_INTERFACE_MODE_NA; | |
401 | ||
402 | case PCI_DEVICE_ID_INTEL_EHL_PSE0_RGMII1G: | |
403 | case PCI_DEVICE_ID_INTEL_EHL_PSE1_RGMII1G: | |
404 | return PHY_INTERFACE_MODE_RGMII; | |
405 | ||
406 | /* Host SGMII and Host SGMII2G5 share the same device id */ | |
407 | case PCI_DEVICE_ID_INTEL_EHL_SGMII1: | |
408 | case PCI_DEVICE_ID_INTEL_EHL_SGMII2G5: | |
409 | case PCI_DEVICE_ID_INTEL_EHL_PSE0_SGMII2G5: | |
410 | case PCI_DEVICE_ID_INTEL_EHL_PSE0_SGMII1G: | |
411 | case PCI_DEVICE_ID_INTEL_EHL_PSE1_SGMII1G: | |
412 | case PCI_DEVICE_ID_INTEL_EHL_PSE1_SGMII2G5: | |
413 | return PHY_INTERFACE_MODE_SGMII; | |
414 | }; | |
415 | ||
416 | return PHY_INTERFACE_MODE_NA; | |
417 | } | |
418 | ||
419 | static struct eqos_ops eqos_intel_ops = { | |
420 | .eqos_inval_desc = eqos_inval_desc_generic, | |
421 | .eqos_flush_desc = eqos_flush_desc_generic, | |
422 | .eqos_inval_buffer = eqos_inval_buffer_generic, | |
423 | .eqos_flush_buffer = eqos_flush_buffer_generic, | |
424 | .eqos_probe_resources = eqos_probe_ressources_intel, | |
425 | .eqos_remove_resources = eqos_null_ops, | |
426 | .eqos_stop_resets = eqos_null_ops, | |
427 | .eqos_start_resets = eqos_start_resets_intel, | |
428 | .eqos_stop_clks = eqos_null_ops, | |
429 | .eqos_start_clks = eqos_null_ops, | |
430 | .eqos_calibrate_pads = eqos_null_ops, | |
431 | .eqos_disable_calibration = eqos_null_ops, | |
432 | .eqos_set_tx_clk_speed = eqos_null_ops, | |
433 | .eqos_get_enetaddr = eqos_get_enetaddr_intel, | |
434 | .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_intel, | |
435 | }; | |
436 | ||
437 | struct eqos_config eqos_intel_config = { | |
438 | .reg_access_always_ok = false, | |
439 | .mdio_wait = 10, | |
440 | .swr_wait = 50, | |
441 | .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB, | |
442 | .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300, | |
443 | .axi_bus_width = EQOS_AXI_WIDTH_64, | |
444 | .interface = eqos_get_interface_intel, | |
445 | .ops = &eqos_intel_ops | |
446 | }; | |
447 | ||
448 | extern U_BOOT_DRIVER(eth_eqos); | |
449 | U_BOOT_PCI_DEVICE(eth_eqos, intel_pci_ids); |