1 /*
2 * Xilinx Processor System Gigabit Ethernet controller (GEM) driver
3 *
4 * PHY management interface implementation
5 * Models currently supported:
6 * - Marvell Alaska 88E1111 (QEMU simulated PHY)
7 * - Marvell Alaska 88E1510/88E1518/88E1512/88E1514 (Zedboard)
8 * - Texas Instruments TLK105
9 * - Texas Instruments DP83822
10 *
11 * Copyright (c) 2021, Weidmueller Interface GmbH & Co. KG
12 * SPDX-License-Identifier: Apache-2.0
13 */
14
15 #include <zephyr/kernel.h>
16 #include <zephyr/device.h>
17
18 #include "eth_xlnx_gem_priv.h"
19
20 #define LOG_MODULE_NAME phy_xlnx_gem
21 #define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
24
25 /* Basic MDIO read / write functions for PHY access */
26
27 /**
28 * @brief Read PHY data via the MDIO interface
29 * Reads data from a PHY attached to the respective GEM's MDIO interface
30 *
31 * @param base_addr Base address of the GEM's register space
32 * @param phy_addr MDIO address of the PHY to be accessed
33 * @param reg_addr Index of the PHY register to be read
34 * @return 16-bit data word received from the PHY
35 */
phy_xlnx_gem_mdio_read(uint32_t base_addr,uint8_t phy_addr,uint8_t reg_addr)36 static uint16_t phy_xlnx_gem_mdio_read(
37 uint32_t base_addr, uint8_t phy_addr,
38 uint8_t reg_addr)
39 {
40 uint32_t reg_val;
41 uint32_t poll_cnt = 0;
42
43 /*
44 * MDIO read operation as described in Zynq-7000 TRM,
45 * chapter 16.3.4, p. 517.
46 */
47
48 /*
49 * Wait until gem.net_status[phy_mgmt_idle] == 1 before issuing the
50 * current command.
51 */
52 do {
53 if (poll_cnt++ > 0) {
54 k_busy_wait(100);
55 }
56 reg_val = sys_read32(base_addr + ETH_XLNX_GEM_NWSR_OFFSET);
57 } while ((reg_val & ETH_XLNX_GEM_MDIO_IDLE_BIT) == 0 && poll_cnt < 10);
58 if (poll_cnt == 10) {
59 LOG_ERR("GEM@0x%08X read from PHY address %hhu, "
60 "register address %hhu timed out",
61 base_addr, phy_addr, reg_addr);
62 return 0;
63 }
64
65 /* Assemble & write the read command to the gem.phy_maint register */
66
67 /* Set the bits constant for any operation */
68 reg_val = ETH_XLNX_GEM_PHY_MAINT_CONST_BITS;
69 /* Indicate a read operation */
70 reg_val |= ETH_XLNX_GEM_PHY_MAINT_READ_OP_BIT;
71 /* PHY address */
72 reg_val |= (((uint32_t)phy_addr & ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_MASK) <<
73 ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_SHIFT);
74 /* Register address */
75 reg_val |= (((uint32_t)reg_addr & ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_MASK) <<
76 ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_SHIFT);
77
78 sys_write32(reg_val, base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET);
79
80 /*
81 * Wait until gem.net_status[phy_mgmt_idle] == 1 -> current command
82 * completed.
83 */
84 poll_cnt = 0;
85 do {
86 if (poll_cnt++ > 0) {
87 k_busy_wait(100);
88 }
89 reg_val = sys_read32(base_addr + ETH_XLNX_GEM_NWSR_OFFSET);
90 } while ((reg_val & ETH_XLNX_GEM_MDIO_IDLE_BIT) == 0 && poll_cnt < 10);
91 if (poll_cnt == 10) {
92 LOG_ERR("GEM@0x%08X read from PHY address %hhu, "
93 "register address %hhu timed out",
94 base_addr, phy_addr, reg_addr);
95 return 0;
96 }
97
98 /*
99 * Read the data returned by the PHY -> lower 16 bits of the PHY main-
100 * tenance register
101 */
102 reg_val = sys_read32(base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET);
103 return (uint16_t)reg_val;
104 }
105
106 /**
107 * @brief Writes PHY data via the MDIO interface
108 * Writes data to a PHY attached to the respective GEM's MDIO interface
109 *
110 * @param base_addr Base address of the GEM's register space
111 * @param phy_addr MDIO address of the PHY to be accessed
112 * @param reg_addr Index of the PHY register to be written to
113 * @param value 16-bit data word to be written to the target register
114 */
phy_xlnx_gem_mdio_write(uint32_t base_addr,uint8_t phy_addr,uint8_t reg_addr,uint16_t value)115 static void phy_xlnx_gem_mdio_write(
116 uint32_t base_addr, uint8_t phy_addr,
117 uint8_t reg_addr, uint16_t value)
118 {
119 uint32_t reg_val;
120 uint32_t poll_cnt = 0;
121
122 /*
123 * MDIO write operation as described in Zynq-7000 TRM,
124 * chapter 16.3.4, p. 517.
125 */
126
127 /*
128 * Wait until gem.net_status[phy_mgmt_idle] == 1 before issuing the
129 * current command.
130 */
131 do {
132 if (poll_cnt++ > 0) {
133 k_busy_wait(100);
134 }
135 reg_val = sys_read32(base_addr + ETH_XLNX_GEM_NWSR_OFFSET);
136 } while ((reg_val & ETH_XLNX_GEM_MDIO_IDLE_BIT) == 0 && poll_cnt < 10);
137 if (poll_cnt == 10) {
138 LOG_ERR("GEM@0x%08X write to PHY address %hhu, "
139 "register address %hhu timed out",
140 base_addr, phy_addr, reg_addr);
141 return;
142 }
143
144 /* Assemble & write the read command to the gem.phy_maint register */
145
146 /* Set the bits constant for any operation */
147 reg_val = ETH_XLNX_GEM_PHY_MAINT_CONST_BITS;
148 /* Indicate a read operation */
149 reg_val |= ETH_XLNX_GEM_PHY_MAINT_WRITE_OP_BIT;
150 /* PHY address */
151 reg_val |= (((uint32_t)phy_addr & ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_MASK) <<
152 ETH_XLNX_GEM_PHY_MAINT_PHY_ADDRESS_SHIFT);
153 /* Register address */
154 reg_val |= (((uint32_t)reg_addr & ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_MASK) <<
155 ETH_XLNX_GEM_PHY_MAINT_REGISTER_ID_SHIFT);
156 /* 16 bits of data for the destination register */
157 reg_val |= ((uint32_t)value & ETH_XLNX_GEM_PHY_MAINT_DATA_MASK);
158
159 sys_write32(reg_val, base_addr + ETH_XLNX_GEM_PHY_MAINTENANCE_OFFSET);
160
161 /*
162 * Wait until gem.net_status[phy_mgmt_idle] == 1 -> current command
163 * completed.
164 */
165 poll_cnt = 0;
166 do {
167 if (poll_cnt++ > 0) {
168 k_busy_wait(100);
169 }
170 reg_val = sys_read32(base_addr + ETH_XLNX_GEM_NWSR_OFFSET);
171 } while ((reg_val & ETH_XLNX_GEM_MDIO_IDLE_BIT) == 0 && poll_cnt < 10);
172 if (poll_cnt == 10) {
173 LOG_ERR("GEM@0x%08X write to PHY address %hhu, "
174 "register address %hhu timed out",
175 base_addr, phy_addr, reg_addr);
176 }
177 }
178
179 /*
180 * Vendor-specific PHY management functions for:
181 * Marvell Alaska 88E1111 (QEMU simulated PHY)
182 * Marvell Alaska 88E1510/88E1518/88E1512/88E1514 (Zedboard)
183 * Register IDs & procedures are based on the corresponding datasheets:
184 * https://www.marvell.com/content/dam/marvell/en/public-collateral/transceivers/marvell-phys-transceivers-alaska-88e1111-datasheet.pdf
185 * https://www.marvell.com/content/dam/marvell/en/public-collateral/transceivers/marvell-phys-transceivers-alaska-88e151x-datasheet.pdf
186 *
187 * NOTICE: Unless indicated otherwise, page/table source references refer to
188 * the 88E151x datasheet.
189 */
190
191 /**
192 * @brief Marvell Alaska PHY reset function
193 * Reset function for the Marvell Alaska PHY series
194 *
195 * @param dev Pointer to the device data
196 */
phy_xlnx_gem_marvell_alaska_reset(const struct device * dev)197 static void phy_xlnx_gem_marvell_alaska_reset(const struct device *dev)
198 {
199 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
200 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
201 uint16_t phy_data;
202 uint32_t retries = 0;
203
204 /*
205 * Page 0, register address 0 = Copper control register,
206 * bit [15] = PHY reset. Register 0/0 access is R/M/W. Comp.
207 * datasheet chapter 2.6 and table 64 "Copper Control Register".
208 */
209 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
210 PHY_MRVL_COPPER_CONTROL_REGISTER);
211 phy_data |= PHY_MRVL_COPPER_CONTROL_RESET_BIT;
212 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
213 PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data);
214
215 /* Bit [15] reverts to 0 once the reset is complete. */
216 while (((phy_data & PHY_MRVL_COPPER_CONTROL_RESET_BIT) != 0) && (retries++ < 10)) {
217 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
218 PHY_MRVL_COPPER_CONTROL_REGISTER);
219 }
220 if (retries == 10) {
221 LOG_ERR("%s reset PHY address %hhu (Marvell Alaska) timed out",
222 dev->name, dev_data->phy_addr);
223 }
224 }
225
226 /**
227 * @brief Marvell Alaska PHY configuration function
228 * Configuration function for the Marvell Alaska PHY series
229 *
230 * @param dev Pointer to the device data
231 */
phy_xlnx_gem_marvell_alaska_cfg(const struct device * dev)232 static void phy_xlnx_gem_marvell_alaska_cfg(const struct device *dev)
233 {
234 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
235 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
236 uint16_t phy_data;
237 uint16_t phy_data_gbit;
238 uint32_t retries = 0;
239
240 /*
241 * Page 0, register address 0 = Copper control register,
242 * bit [12] = auto-negotiation enable bit is to be cleared
243 * for now, afterwards, trigger a PHY reset.
244 * Register 0/0 access is R/M/W. Comp. datasheet chapter 2.6
245 * and table 64 "Copper Control Register".
246 */
247 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
248 PHY_MRVL_COPPER_CONTROL_REGISTER);
249 phy_data &= ~PHY_MRVL_COPPER_CONTROL_AUTONEG_ENABLE_BIT;
250 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
251 PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data);
252 phy_xlnx_gem_marvell_alaska_reset(dev);
253
254 if ((dev_data->phy_id & PHY_MRVL_PHY_ID_MODEL_MASK) ==
255 PHY_MRVL_PHY_ID_MODEL_88E151X) {
256 /*
257 * 88E151x only: configure the system interface and media type
258 * (i.e. "RGMII to Copper", 0x0). On the 88E1111, this setting
259 * is configured using I/O pins on the device.
260 * TODO: Make this value configurable via KConfig or DT?
261 * Page 18, register address 20 = General Control Register 1,
262 * bits [2..0] = mode configuration
263 * Comp. datasheet table 129 "General Control Register 1"
264 * NOTICE: a change of this value requires a subsequent software
265 * reset command via the same register's bit [15].
266 */
267 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
268 PHY_MRVL_COPPER_PAGE_SWITCH_REGISTER,
269 PHY_MRVL_GENERAL_CONTROL_1_PAGE);
270
271 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
272 PHY_MRVL_GENERAL_CONTROL_1_REGISTER);
273 phy_data &= ~(PHY_MRVL_MODE_CONFIG_MASK << PHY_MRVL_MODE_CONFIG_SHIFT);
274 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
275 PHY_MRVL_GENERAL_CONTROL_1_REGISTER, phy_data);
276
277 /*
278 * [15] Mode Software Reset bit, affecting pages 6 and 18
279 * Reset is performed immediately, bit [15] is self-clearing.
280 * This reset bit is not to be confused with the actual PHY
281 * reset in register 0/0!
282 */
283 phy_data |= PHY_MRVL_GENERAL_CONTROL_1_RESET_BIT;
284 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
285 PHY_MRVL_GENERAL_CONTROL_1_REGISTER, phy_data);
286
287 /* Bit [15] reverts to 0 once the reset is complete. */
288 while (((phy_data & PHY_MRVL_GENERAL_CONTROL_1_RESET_BIT) != 0) &&
289 (retries++ < 10)) {
290 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr,
291 dev_data->phy_addr,
292 PHY_MRVL_GENERAL_CONTROL_1_REGISTER);
293 }
294 if (retries == 10) {
295 LOG_ERR("%s configure PHY address %hhu (Marvell Alaska) timed out",
296 dev->name, dev_data->phy_addr);
297 return;
298 }
299
300 /* Revert to register page 0 */
301 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
302 PHY_MRVL_COPPER_PAGE_SWITCH_REGISTER,
303 PHY_MRVL_BASE_REGISTERS_PAGE);
304 }
305
306 /*
307 * Configure MDIX
308 * TODO: Make this value configurable via KConfig or DT?
309 * 88E151x: Page 0, register address 16 = Copper specific control register 1,
310 * 88E1111: Page any, register address 16 = PHY specific control register,
311 * bits [6..5] = MDIO crossover mode. Comp. datasheet table 76.
312 * NOTICE: a change of this value requires a subsequent software
313 * reset command via Copper Control Register's bit [15].
314 */
315
316 /* [6..5] 11 = Enable auto cross over detection */
317 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
318 PHY_MRVL_COPPER_CONTROL_1_REGISTER);
319 phy_data &= ~(PHY_MRVL_MDIX_CONFIG_MASK << PHY_MRVL_MDIX_CONFIG_SHIFT);
320 phy_data |= (PHY_MRVL_MDIX_AUTO_CROSSOVER_ENABLE << PHY_MRVL_MDIX_CONFIG_SHIFT);
321 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
322 PHY_MRVL_COPPER_CONTROL_1_REGISTER, phy_data);
323
324 /*
325 * Configure the Copper Specific Interrupt Enable Register
326 * (88E151x) / Interrupt Enable Register (88E1111).
327 * The interrupt status register provides a convenient way to
328 * detect relevant state changes, also, PHY management could
329 * eventually be changed from polling to interrupt-driven.
330 * There's just one big catch: at least on the Zedboard, the
331 * PHY interrupt line isn't wired up, therefore, the GEM can
332 * never trigger a PHY interrupt. Still, the PHY interrupts
333 * are configured & enabled in order to obtain all relevant
334 * status data from a single source.
335 *
336 * -> all bits contained herein will be retained during the
337 * upcoming software reset operation.
338 * Page 0, register address 18 = (Copper Specific) Interrupt
339 * Enable Register,
340 * bit [14] = Speed changed interrupt enable,
341 * bit [13] = Duplex changed interrupt enable,
342 * bit [11] = Auto-negotiation completed interrupt enable,
343 * bit [10] = Link status changed interrupt enable.
344 * Comp. datasheet table 78
345 */
346 phy_data = PHY_MRVL_COPPER_SPEED_CHANGED_INT_BIT |
347 PHY_MRVL_COPPER_DUPLEX_CHANGED_INT_BIT |
348 PHY_MRVL_COPPER_AUTONEG_COMPLETED_INT_BIT |
349 PHY_MRVL_COPPER_LINK_STATUS_CHANGED_INT_BIT;
350 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
351 PHY_MRVL_COPPER_INT_ENABLE_REGISTER, phy_data);
352
353 /* Trigger a PHY Reset, affecting pages 0, 2, 3, 5, 7. */
354 phy_xlnx_gem_marvell_alaska_reset(dev);
355
356 /*
357 * Clear the interrupt status register before advertising the
358 * supported link speed(s).
359 */
360 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
361 PHY_MRVL_COPPER_INT_STATUS_REGISTER);
362
363 /*
364 * Set which link speeds and duplex modes shall be advertised during
365 * auto-negotiation, then re-enable auto-negotiation. PHY link speed
366 * advertisement configuration as described in Zynq-7000 TRM, chapter
367 * 16.3.4, p. 517.
368 */
369
370 /*
371 * Advertise the link speed from the device configuration & perform
372 * auto-negotiation. This process involves:
373 *
374 * Page 0, register address 4 =
375 * Copper Auto-Negotiation Advertisement Register,
376 * Page 0, register address 0 =
377 * Copper Control Register, bit [15] = Reset -> apply all changes
378 * made regarding advertisement,
379 * Page 0, register address 9 =
380 * 1000BASE-T Control Register (if link speed = 1GBit/s),
381 * Page 0, register address 1 =
382 * Copper Status Register, bit [5] = Copper Auto-Negotiation
383 * Complete.
384 *
385 * Comp. datasheet tables 68 & 73.
386 */
387
388 /*
389 * 88E151x only:
390 * Register 4, bits [4..0] = Selector field, 00001 = 802.3. Those bits
391 * are reserved in other Marvell PHYs.
392 */
393 if ((dev_data->phy_id & PHY_MRVL_PHY_ID_MODEL_MASK) ==
394 PHY_MRVL_PHY_ID_MODEL_88E151X) {
395 phy_data = PHY_MRVL_ADV_SELECTOR_802_3;
396 } else {
397 phy_data = 0x0000;
398 }
399
400 /*
401 * Clear the 1 GBit/s FDX/HDX advertisement bits from reg. 9's current
402 * contents in case we're going to advertise anything below 1 GBit/s
403 * as maximum / nominal link speed.
404 */
405 phy_data_gbit = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
406 PHY_MRVL_1000BASET_CONTROL_REGISTER);
407 phy_data_gbit &= ~PHY_MRVL_ADV_1000BASET_FDX_BIT;
408 phy_data_gbit &= ~PHY_MRVL_ADV_1000BASET_HDX_BIT;
409
410 if (dev_conf->enable_fdx) {
411 if (dev_conf->max_link_speed == LINK_1GBIT) {
412 /* Advertise 1 GBit/s, full duplex */
413 phy_data_gbit |= PHY_MRVL_ADV_1000BASET_FDX_BIT;
414 if (dev_conf->phy_advertise_lower) {
415 /* + 100 MBit/s, full duplex */
416 phy_data |= PHY_MRVL_ADV_100BASET_FDX_BIT;
417 /* + 10 MBit/s, full duplex */
418 phy_data |= PHY_MRVL_ADV_10BASET_FDX_BIT;
419 }
420 } else if (dev_conf->max_link_speed == LINK_100MBIT) {
421 /* Advertise 100 MBit/s, full duplex */
422 phy_data |= PHY_MRVL_ADV_100BASET_FDX_BIT;
423 if (dev_conf->phy_advertise_lower) {
424 /* + 10 MBit/s, full duplex */
425 phy_data |= PHY_MRVL_ADV_10BASET_FDX_BIT;
426 }
427 } else if (dev_conf->max_link_speed == LINK_10MBIT) {
428 /* Advertise 10 MBit/s, full duplex */
429 phy_data |= PHY_MRVL_ADV_10BASET_FDX_BIT;
430 }
431 } else {
432 if (dev_conf->max_link_speed == LINK_1GBIT) {
433 /* Advertise 1 GBit/s, half duplex */
434 phy_data_gbit = PHY_MRVL_ADV_1000BASET_HDX_BIT;
435 if (dev_conf->phy_advertise_lower) {
436 /* + 100 MBit/s, half duplex */
437 phy_data |= PHY_MRVL_ADV_100BASET_HDX_BIT;
438 /* + 10 MBit/s, half duplex */
439 phy_data |= PHY_MRVL_ADV_10BASET_HDX_BIT;
440 }
441 } else if (dev_conf->max_link_speed == LINK_100MBIT) {
442 /* Advertise 100 MBit/s, half duplex */
443 phy_data |= PHY_MRVL_ADV_100BASET_HDX_BIT;
444 if (dev_conf->phy_advertise_lower) {
445 /* + 10 MBit/s, half duplex */
446 phy_data |= PHY_MRVL_ADV_10BASET_HDX_BIT;
447 }
448 } else if (dev_conf->max_link_speed == LINK_10MBIT) {
449 /* Advertise 10 MBit/s, half duplex */
450 phy_data |= PHY_MRVL_ADV_10BASET_HDX_BIT;
451 }
452 }
453
454 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
455 PHY_MRVL_1000BASET_CONTROL_REGISTER, phy_data_gbit);
456 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
457 PHY_MRVL_COPPER_AUTONEG_ADV_REGISTER, phy_data);
458
459 /*
460 * Trigger a PHY reset, affecting pages 0, 2, 3, 5, 7.
461 * Afterwards, set the auto-negotiation enable bit [12] in the
462 * Copper Control Register.
463 */
464 phy_xlnx_gem_marvell_alaska_reset(dev);
465 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
466 PHY_MRVL_COPPER_CONTROL_REGISTER);
467 phy_data |= PHY_MRVL_COPPER_CONTROL_AUTONEG_ENABLE_BIT;
468 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
469 PHY_MRVL_COPPER_CONTROL_REGISTER, phy_data);
470
471 /*
472 * Set the link speed to 'link down' for now, once auto-negotiation
473 * is complete, the result will be handled by the system work queue.
474 */
475 dev_data->eff_link_speed = LINK_DOWN;
476 }
477
478 /**
479 * @brief Marvell Alaska PHY status change polling function
480 * Status change polling function for the Marvell Alaska PHY series
481 *
482 * @param dev Pointer to the device data
483 * @return A set of bits indicating whether one or more of the following
484 * events has occurred: auto-negotiation completed, link state
485 * changed, link speed changed.
486 */
phy_xlnx_gem_marvell_alaska_poll_sc(const struct device * dev)487 static uint16_t phy_xlnx_gem_marvell_alaska_poll_sc(const struct device *dev)
488 {
489 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
490 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
491 uint16_t phy_data;
492 uint16_t phy_status = 0;
493
494 /*
495 * PHY status change detection is implemented by reading the
496 * interrupt status register.
497 * Page 0, register address 19 = Copper Interrupt Status Register
498 * bit [14] = Speed changed interrupt,
499 * bit [13] = Duplex changed interrupt,
500 * bit [11] = Auto-negotiation completed interrupt,
501 * bit [10] = Link status changed interrupt.
502 * Comp. datasheet table 79
503 */
504 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
505 PHY_MRVL_COPPER_INT_STATUS_REGISTER);
506
507 if ((phy_data & PHY_MRVL_COPPER_AUTONEG_COMPLETED_INT_BIT) != 0) {
508 phy_status |= PHY_XLNX_GEM_EVENT_AUTONEG_COMPLETE;
509 }
510 if (((phy_data & PHY_MRVL_COPPER_DUPLEX_CHANGED_INT_BIT) != 0) ||
511 ((phy_data & PHY_MRVL_COPPER_LINK_STATUS_CHANGED_INT_BIT) != 0)) {
512 phy_status |= PHY_XLNX_GEM_EVENT_LINK_STATE_CHANGED;
513 }
514 if ((phy_data & PHY_MRVL_COPPER_SPEED_CHANGED_INT_BIT) != 0) {
515 phy_status |= PHY_XLNX_GEM_EVENT_LINK_SPEED_CHANGED;
516 }
517
518 /*
519 * Clear the status register, preserve reserved bit [3] as indicated
520 * by the datasheet
521 */
522 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
523 PHY_MRVL_COPPER_INT_STATUS_REGISTER, (phy_data & 0x8));
524
525 return phy_status;
526 }
527
528 /**
529 * @brief Marvell Alaska PHY link status polling function
530 * Link status polling function for the Marvell Alaska PHY series
531 *
532 * @param dev Pointer to the device data
533 * @return 1 if the PHY indicates link up, 0 if the link is down
534 */
phy_xlnx_gem_marvell_alaska_poll_lsts(const struct device * dev)535 static uint8_t phy_xlnx_gem_marvell_alaska_poll_lsts(const struct device *dev)
536 {
537 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
538 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
539 uint16_t phy_data;
540
541 /*
542 * Current link status is obtained from:
543 * Page 0, register address 1 = Copper Status Register
544 * bit [2] = Copper Link Status
545 */
546 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
547 PHY_MRVL_COPPER_STATUS_REGISTER);
548
549 return ((phy_data >> PHY_MRVL_COPPER_LINK_STATUS_BIT_SHIFT) & 0x0001);
550 }
551
552 /**
553 * @brief Marvell Alaska PHY link speed polling function
554 * Link speed polling function for the Marvell Alaska PHY series
555 *
556 * @param dev Pointer to the device data
557 * @return Enum containing the current link speed reported by the PHY
558 */
phy_xlnx_gem_marvell_alaska_poll_lspd(const struct device * dev)559 static enum eth_xlnx_link_speed phy_xlnx_gem_marvell_alaska_poll_lspd(
560 const struct device *dev)
561 {
562 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
563 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
564 enum eth_xlnx_link_speed link_speed;
565 uint16_t phy_data;
566
567 /*
568 * Current link speed is obtained from:
569 * Page 0, register address 17 = Copper Specific Status Register 1
570 * bits [15 .. 14] = Speed.
571 */
572 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
573 PHY_MRVL_COPPER_STATUS_1_REGISTER);
574 phy_data >>= PHY_MRVL_LINK_SPEED_SHIFT;
575 phy_data &= PHY_MRVL_LINK_SPEED_MASK;
576
577 /*
578 * Link speed bit masks: comp. datasheet, table 77 @ description
579 * of the 'Speed' bits.
580 */
581 switch (phy_data) {
582 case PHY_MRVL_LINK_SPEED_10MBIT:
583 link_speed = LINK_10MBIT;
584 break;
585 case PHY_MRVL_LINK_SPEED_100MBIT:
586 link_speed = LINK_100MBIT;
587 break;
588 case PHY_MRVL_LINK_SPEED_1GBIT:
589 link_speed = LINK_1GBIT;
590 break;
591 default:
592 link_speed = LINK_DOWN;
593 break;
594 };
595
596 return link_speed;
597 }
598
599 /*
600 * Vendor-specific PHY management functions for:
601 * Texas Instruments TLK105
602 * Texas Instruments DP83822
603 * with the DP83822 being the successor to the deprecated TLK105.
604 * Register IDs & procedures are based on the corresponding datasheets:
605 * https://www.ti.com/lit/gpn/tlk105
606 * https://www.ti.com/lit/gpn/dp83822i
607 */
608
609 /**
610 * @brief TI TLK105 & DP83822 PHY reset function
611 * Reset function for the TI TLK105 & DP83822 PHYs
612 *
613 * @param dev Pointer to the device data
614 */
phy_xlnx_gem_ti_dp83822_reset(const struct device * dev)615 static void phy_xlnx_gem_ti_dp83822_reset(const struct device *dev)
616 {
617 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
618 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
619 uint16_t phy_data;
620 uint32_t retries = 0;
621
622 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
623 PHY_TI_BASIC_MODE_CONTROL_REGISTER);
624 phy_data |= PHY_TI_BASIC_MODE_CONTROL_RESET_BIT;
625 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
626 PHY_TI_BASIC_MODE_CONTROL_REGISTER, phy_data);
627
628 while (((phy_data & PHY_TI_BASIC_MODE_CONTROL_RESET_BIT) != 0) && (retries++ < 10)) {
629 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
630 PHY_TI_BASIC_MODE_CONTROL_REGISTER);
631 }
632 if (retries == 10) {
633 LOG_ERR("%s reset PHY address %hhu (TI TLK105/DP83822) timed out",
634 dev->name, dev_data->phy_addr);
635 }
636 }
637
638 /**
639 * @brief TI TLK105 & DP83822 PHY configuration function
640 * Configuration function for the TI TLK105 & DP83822 PHYs
641 *
642 * @param dev Pointer to the device data
643 */
phy_xlnx_gem_ti_dp83822_cfg(const struct device * dev)644 static void phy_xlnx_gem_ti_dp83822_cfg(const struct device *dev)
645 {
646 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
647 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
648 uint16_t phy_data = PHY_TI_ADV_SELECTOR_802_3;
649
650 /* Configure link advertisement */
651 if (dev_conf->enable_fdx) {
652 if (dev_conf->max_link_speed == LINK_100MBIT) {
653 /* Advertise 100BASE-TX, full duplex */
654 phy_data |= PHY_TI_ADV_100BASET_FDX_BIT;
655 if (dev_conf->phy_advertise_lower) {
656 /* + 10BASE-TX, full duplex */
657 phy_data |= PHY_TI_ADV_10BASET_FDX_BIT;
658 }
659 } else if (dev_conf->max_link_speed == LINK_10MBIT) {
660 /* Advertise 10BASE-TX, full duplex */
661 phy_data |= PHY_TI_ADV_10BASET_FDX_BIT;
662 }
663 } else {
664 if (dev_conf->max_link_speed == LINK_100MBIT) {
665 /* Advertise 100BASE-TX, half duplex */
666 phy_data |= PHY_TI_ADV_100BASET_HDX_BIT;
667 if (dev_conf->phy_advertise_lower) {
668 /* + 10BASE-TX, half duplex */
669 phy_data |= PHY_TI_ADV_10BASET_HDX_BIT;
670 }
671 } else if (dev_conf->max_link_speed == LINK_10MBIT) {
672 /* Advertise 10BASE-TX, half duplex */
673 phy_data |= PHY_TI_ADV_10BASET_HDX_BIT;
674 }
675 }
676 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
677 PHY_TI_AUTONEG_ADV_REGISTER, phy_data);
678
679 /* Enable auto-negotiation */
680 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
681 PHY_TI_BASIC_MODE_CONTROL_REGISTER);
682 phy_data |= PHY_TI_BASIC_MODE_CONTROL_AUTONEG_ENABLE_BIT;
683 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
684 PHY_TI_BASIC_MODE_CONTROL_REGISTER, phy_data);
685
686 /* Robust Auto MDIX */
687 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
688 PHY_TI_CONTROL_REGISTER_1);
689 phy_data |= PHY_TI_CR1_ROBUST_AUTO_MDIX_BIT;
690 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
691 PHY_TI_CONTROL_REGISTER_1, phy_data);
692
693 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
694 PHY_TI_PHY_CONTROL_REGISTER);
695 /* Auto MDIX enable */
696 phy_data |= PHY_TI_PHY_CONTROL_AUTO_MDIX_ENABLE_BIT;
697 /* Link LED shall only indicate link up or down, no RX/TX activity */
698 phy_data |= PHY_TI_PHY_CONTROL_LED_CONFIG_LINK_ONLY_BIT;
699 /* Force MDIX disable */
700 phy_data &= ~PHY_TI_PHY_CONTROL_FORCE_MDIX_BIT;
701 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
702 PHY_TI_PHY_CONTROL_REGISTER, phy_data);
703
704 /* Set blink rate to 5 Hz */
705 phy_data = (PHY_TI_LED_CONTROL_BLINK_RATE_5HZ <<
706 PHY_TI_LED_CONTROL_BLINK_RATE_SHIFT);
707 phy_xlnx_gem_mdio_write(dev_conf->base_addr, dev_data->phy_addr,
708 PHY_TI_LED_CONTROL_REGISTER, phy_data);
709
710 /*
711 * Set the link speed to 'link down' for now, once auto-negotiation
712 * is complete, the result will be handled by the system work queue.
713 */
714 dev_data->eff_link_speed = LINK_DOWN;
715 }
716
717 /**
718 * @brief TI TLK105 & DP83822 PHY status change polling function
719 * Status change polling function for the TI TLK105 & DP83822 PHYs
720 *
721 * @param dev Pointer to the device data
722 * @return A set of bits indicating whether one or more of the following
723 * events has occurred: auto-negotiation completed, link state
724 * changed, link speed changed.
725 */
phy_xlnx_gem_ti_dp83822_poll_sc(const struct device * dev)726 static uint16_t phy_xlnx_gem_ti_dp83822_poll_sc(const struct device *dev)
727 {
728 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
729 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
730 uint16_t phy_data;
731 uint16_t phy_status = 0;
732
733 /*
734 * The relevant status bits are obtained from the MII Interrupt
735 * Status Register 1. The upper byte of the register's data word
736 * contains the status bits which are set regardless of whether
737 * the corresponding interrupt enable bits are set in the lower
738 * byte or not (comp. TLK105 documentation, chapter 8.1.16).
739 */
740 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
741 PHY_TI_MII_INTERRUPT_STATUS_REGISTER_1);
742
743 if ((phy_data & PHY_TI_AUTONEG_COMPLETED_INT_BIT) != 0) {
744 phy_status |= PHY_XLNX_GEM_EVENT_AUTONEG_COMPLETE;
745 }
746 if ((phy_data & PHY_TI_DUPLEX_CHANGED_INT_BIT) != 0) {
747 phy_status |= PHY_XLNX_GEM_EVENT_LINK_STATE_CHANGED;
748 }
749 if ((phy_data & PHY_TI_LINK_STATUS_CHANGED_INT_BIT) != 0) {
750 phy_status |= PHY_XLNX_GEM_EVENT_LINK_STATE_CHANGED;
751 }
752 if ((phy_data & PHY_TI_SPEED_CHANGED_INT_BIT) != 0) {
753 phy_status |= PHY_XLNX_GEM_EVENT_LINK_SPEED_CHANGED;
754 }
755
756 return phy_status;
757 }
758
759 /**
760 * @brief TI TLK105 & DP83822 PHY link status polling function
761 * Link status polling function for the TI TLK105 & DP83822 PHYs
762 *
763 * @param dev Pointer to the device data
764 * @return 1 if the PHY indicates link up, 0 if the link is down
765 */
phy_xlnx_gem_ti_dp83822_poll_lsts(const struct device * dev)766 static uint8_t phy_xlnx_gem_ti_dp83822_poll_lsts(const struct device *dev)
767 {
768 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
769 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
770 uint16_t phy_data;
771
772 /*
773 * Double read of the BMSR is intentional - the relevant bit is latched
774 * low so that after a link down -> link up transition, the first read
775 * of the BMSR will still return the latched link down status rather
776 * than the current status.
777 */
778 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
779 PHY_TI_BASIC_MODE_STATUS_REGISTER);
780 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
781 PHY_TI_BASIC_MODE_STATUS_REGISTER);
782
783 return ((phy_data & PHY_TI_BASIC_MODE_STATUS_LINK_STATUS_BIT) != 0);
784 }
785
786 /**
787 * @brief TI TLK105 & DP83822 PHY link speed polling function
788 * Link speed polling function for the TI TLK105 & DP83822 PHYs
789 *
790 * @param dev Pointer to the device data
791 * @return Enum containing the current link speed reported by the PHY
792 */
phy_xlnx_gem_ti_dp83822_poll_lspd(const struct device * dev)793 static enum eth_xlnx_link_speed phy_xlnx_gem_ti_dp83822_poll_lspd(
794 const struct device *dev)
795 {
796 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
797 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
798 enum eth_xlnx_link_speed link_speed;
799 uint16_t phy_data;
800
801 phy_data = phy_xlnx_gem_mdio_read(dev_conf->base_addr, dev_data->phy_addr,
802 PHY_TI_PHY_STATUS_REGISTER);
803
804 /* PHYSCR[0] is the link established indication bit */
805 if ((phy_data & PHY_TI_PHY_STATUS_LINK_BIT) != 0) {
806 /* PHYSCR[1] is the speed status bit: 0 = 100 Mbps, 1 = 10 Mbps. */
807 if ((phy_data & PHY_TI_PHY_STATUS_SPEED_BIT) != 0) {
808 link_speed = LINK_10MBIT;
809 } else {
810 link_speed = LINK_100MBIT;
811 }
812 } else {
813 link_speed = LINK_DOWN;
814 }
815
816 return link_speed;
817 }
818
819 /**
820 * @brief Marvell Alaska PHY function pointer table
821 * Function pointer table for the Marvell Alaska PHY series
822 * specific management functions
823 */
824 static struct phy_xlnx_gem_api phy_xlnx_gem_marvell_alaska_api = {
825 .phy_reset_func = phy_xlnx_gem_marvell_alaska_reset,
826 .phy_configure_func = phy_xlnx_gem_marvell_alaska_cfg,
827 .phy_poll_status_change_func = phy_xlnx_gem_marvell_alaska_poll_sc,
828 .phy_poll_link_status_func = phy_xlnx_gem_marvell_alaska_poll_lsts,
829 .phy_poll_link_speed_func = phy_xlnx_gem_marvell_alaska_poll_lspd
830 };
831
832 /**
833 * @brief Texas Instruments TLK105 & DP83822 PHY function pointer table
834 * Function pointer table for the Texas Instruments TLK105 / DP83822 PHY
835 * series specific management functions
836 */
837 static struct phy_xlnx_gem_api phy_xlnx_gem_ti_dp83822_api = {
838 .phy_reset_func = phy_xlnx_gem_ti_dp83822_reset,
839 .phy_configure_func = phy_xlnx_gem_ti_dp83822_cfg,
840 .phy_poll_status_change_func = phy_xlnx_gem_ti_dp83822_poll_sc,
841 .phy_poll_link_status_func = phy_xlnx_gem_ti_dp83822_poll_lsts,
842 .phy_poll_link_speed_func = phy_xlnx_gem_ti_dp83822_poll_lspd
843 };
844
845 /*
846 * All vendor-specific API structs & code are located above
847 * -> assemble the top-level list of supported devices the
848 * upcoming function phy_xlnx_gem_detect will work with.
849 */
850
851 /**
852 * @brief Top-level table of supported PHYs
853 * Top-level table of PHYs supported by the GEM driver. Contains 1..n
854 * supported PHY specifications, consisting of the PHY ID plus a mask
855 * for masking out variable parts of the PHY ID such as hardware revisions,
856 * as well as a textual description of the PHY model and a pointer to
857 * the corresponding PHY management function pointer table.
858 */
859 static struct phy_xlnx_gem_supported_dev phy_xlnx_gem_supported_devs[] = {
860 {
861 .phy_id = PHY_MRVL_PHY_ID_MODEL_88E1111,
862 .phy_id_mask = PHY_MRVL_PHY_ID_MODEL_MASK,
863 .api = &phy_xlnx_gem_marvell_alaska_api,
864 .identifier = "Marvell Alaska 88E1111"
865 },
866 {
867 .phy_id = PHY_MRVL_PHY_ID_MODEL_88E151X,
868 .phy_id_mask = PHY_MRVL_PHY_ID_MODEL_MASK,
869 .api = &phy_xlnx_gem_marvell_alaska_api,
870 .identifier = "Marvell Alaska 88E151x"
871 },
872 {
873 .phy_id = PHY_TI_PHY_ID_MODEL_DP83822,
874 .phy_id_mask = PHY_TI_PHY_ID_MODEL_MASK,
875 .api = &phy_xlnx_gem_ti_dp83822_api,
876 .identifier = "Texas Instruments DP83822"
877 },
878 {
879 .phy_id = PHY_TI_PHY_ID_MODEL_TLK105,
880 .phy_id_mask = PHY_TI_PHY_ID_MODEL_MASK,
881 .api = &phy_xlnx_gem_ti_dp83822_api,
882 .identifier = "Texas Instruments TLK105"
883 }
884 };
885
886 /**
887 * @brief Top-level PHY detection function
888 * Top-level PHY detection function called by the GEM driver if PHY management
889 * is enabled for the current GEM device instance. This function is generic
890 * and does not require any knowledge regarding PHY vendors, models etc.
891 *
892 * @param dev Pointer to the device data
893 * @retval -ENOTSUP if PHY management is disabled for the current GEM
894 * device instance
895 * @retval -EIO if no (supported) PHY was detected
896 * @retval 0 if a supported PHY has been detected
897 */
phy_xlnx_gem_detect(const struct device * dev)898 int phy_xlnx_gem_detect(const struct device *dev)
899 {
900 const struct eth_xlnx_gem_dev_cfg *dev_conf = dev->config;
901 struct eth_xlnx_gem_dev_data *dev_data = dev->data;
902
903 uint8_t phy_curr_addr;
904 uint8_t phy_first_addr = dev_conf->phy_mdio_addr_fix;
905 uint8_t phy_last_addr = (dev_conf->phy_mdio_addr_fix != 0) ?
906 dev_conf->phy_mdio_addr_fix : 31;
907 uint32_t phy_id;
908 uint16_t phy_data;
909 uint32_t list_iter;
910
911 /*
912 * Clear the PHY address & ID in the device data struct -> may be
913 * pre-initialized with a non-zero address meaning auto detection
914 * is disabled. If eventually a supported PHY is found, a non-
915 * zero address will be written back to the data struct.
916 */
917 dev_data->phy_addr = 0;
918 dev_data->phy_id = 0;
919 dev_data->phy_access_api = NULL;
920
921 if (!dev_conf->init_phy) {
922 return -ENOTSUP;
923 }
924
925 /*
926 * PHY detection as described in Zynq-7000 TRM, chapter 16.3.4,
927 * p. 517
928 */
929 for (phy_curr_addr = phy_first_addr;
930 phy_curr_addr <= phy_last_addr;
931 phy_curr_addr++) {
932 /* Read the upper & lower PHY ID 16-bit words */
933 phy_data = phy_xlnx_gem_mdio_read(
934 dev_conf->base_addr, phy_curr_addr,
935 PHY_IDENTIFIER_1_REGISTER);
936 phy_id = (((uint32_t)phy_data << 16) & 0xFFFF0000);
937 phy_data = phy_xlnx_gem_mdio_read(
938 dev_conf->base_addr, phy_curr_addr,
939 PHY_IDENTIFIER_2_REGISTER);
940 phy_id |= ((uint32_t)phy_data & 0x0000FFFF);
941
942 if (phy_id != 0x00000000 && phy_id != 0xFFFFFFFF) {
943 LOG_DBG("%s detected PHY at address %hhu: "
944 "ID 0x%08X",
945 dev->name,
946 phy_curr_addr, phy_id);
947
948 /*
949 * Iterate the list of all supported PHYs -> if the
950 * current PHY is supported, store all related data
951 * in the device's run-time data struct.
952 */
953 for (list_iter = 0; list_iter < ARRAY_SIZE(phy_xlnx_gem_supported_devs);
954 list_iter++) {
955 if (phy_xlnx_gem_supported_devs[list_iter].phy_id ==
956 (phy_xlnx_gem_supported_devs[list_iter].phy_id_mask
957 & phy_id)) {
958 LOG_DBG("%s identified supported PHY: %s",
959 dev->name,
960 phy_xlnx_gem_supported_devs[list_iter].identifier);
961
962 /*
963 * Store the numeric values of the PHY ID and address
964 * as well as the corresponding set of function pointers
965 * in the device's run-time data struct.
966 */
967 dev_data->phy_addr = phy_curr_addr;
968 dev_data->phy_id = phy_id;
969 dev_data->phy_access_api =
970 phy_xlnx_gem_supported_devs[list_iter].api;
971
972 return 0;
973 }
974 }
975 }
976 }
977
978 LOG_ERR("%s PHY detection failed", dev->name);
979 return -EIO;
980 }
981