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