1 /*
2  * Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH
3  * Copyright 2023 NXP
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <errno.h>
9 #include <stdint.h>
10 #include <stdbool.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/mdio.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/net/mdio.h>
16 #include <zephyr/net/mii.h>
17 #include <zephyr/net/phy.h>
18 #include <zephyr/sys/util.h>
19 #include "phy_adin2111_priv.h"
20 
21 LOG_MODULE_REGISTER(phy_adin, CONFIG_PHY_LOG_LEVEL);
22 
23 /* PHYs out of reset check retry delay */
24 #define ADIN2111_PHY_AWAIT_DELAY_POLL_US			15U
25 /*
26  * Number of retries for PHYs out of reset check,
27  * rmii variants as ADIN11XX need 70ms maximum after hw reset to be up,
28  * so the increasing the count for that, as default 25ms (sw reset) + 45.
29  */
30 #define ADIN2111_PHY_AWAIT_RETRY_COUNT				3000U
31 
32 /* PHY's software powerdown check retry delay */
33 #define ADIN2111_PHY_SFT_PD_DELAY_POLL_US			15U
34 /* Number of retries for PHY's software powerdown check */
35 #define ADIN2111_PHY_SFT_PD_RETRY_COUNT				200U
36 
37 /* Software reset, CLK_25 disabled time*/
38 #define ADIN1100_PHY_SFT_RESET_MS				25U
39 
40 /* PHYs autonegotiation complete timeout */
41 #define ADIN2111_AN_COMPLETE_AWAIT_TIMEOUT_MS			3000U
42 
43 /* ADIN2111 PHY identifier */
44 #define ADIN2111_PHY_ID						0x0283BCA1U
45 #define ADIN1110_PHY_ID						0x0283BC91U
46 #define ADIN1100_PHY_ID						0x0283BC81U
47 
48 /* System Interrupt Mask Register */
49 #define ADIN2111_PHY_CRSM_IRQ_MASK				0x0020U
50 /* System Interrupt Status Register */
51 #define ADIN2111_PHY_CRSM_IRQ_STATUS				0x0010U
52 /**
53  * Mask of reserved interrupts that indicates a fatal error in the system.
54  *
55  * There is inconsistency between RM and ADI driver example:
56  *   - RM mask 0x6FFF
57  *   - ADI driver example mask 0x2BFF
58  *
59  * The value from the example doesn't include reserved bits 10 and 14.
60  * The tests show that PHY is still functioning when bit 10 is raised.
61  *
62  * Here the value from ADI driver example is used instead of RM.
63  */
64 #define ADIN2111_PHY_CRSM_IRQ_STATUS_FATAL_ERR			0x2BFFU
65 
66 /* PHY Subsystem Interrupt Mask Register */
67 #define ADIN2111_PHY_SUBSYS_IRQ_MASK				0x0021U
68 /* PHY Subsystem Interrupt Status Register */
69 #define ADIN2111_PHY_SUBSYS_IRQ_STATUS				0x0011U
70 /* Link Status Change */
71 #define ADIN2111_PHY_SUBSYS_IRQ_STATUS_LINK_STAT_CHNG_LH	BIT(1)
72 
73 /* Software Power-down Control Register */
74 #define ADIN2111_PHY_CRSM_SFT_PD_CNTRL				0x8812U
75 /* System Status Register */
76 #define ADIN2111_PHY_CRSM_STAT					0x8818U
77 /* Software Power-down Status */
78 #define ADIN2111_CRSM_STAT_CRSM_SFT_PD_RDY			BIT(1)
79 
80 /* LED Control Register */
81 #define ADIN2111_PHY_LED_CNTRL					0x8C82U
82 /* LED 1 Enable */
83 #define ADIN2111_PHY_LED_CNTRL_LED1_EN				BIT(15)
84 /* LED 0 Enable */
85 #define ADIN2111_PHY_LED_CNTRL_LED0_EN				BIT(7)
86 
87 /* MMD bridge regs */
88 #define ADIN1100_MMD_ACCESS_CNTRL				0x0DU
89 #define ADIN1100_MMD_ACCESS					0x0EU
90 
91 struct phy_adin2111_config {
92 	const struct device *mdio;
93 	uint8_t phy_addr;
94 	bool led0_en;
95 	bool led1_en;
96 	bool tx_24v;
97 	bool mii;
98 };
99 
100 struct phy_adin2111_data {
101 	const struct device *dev;
102 	struct phy_link_state state;
103 	struct k_sem sem;
104 	struct k_work_delayable monitor_work;
105 	phy_callback_t cb;
106 	void *cb_data;
107 };
108 
phy_adin2111_c22_read(const struct device * dev,uint16_t reg,uint16_t * val)109 static inline int phy_adin2111_c22_read(const struct device *dev, uint16_t reg,
110 					uint16_t *val)
111 {
112 	const struct phy_adin2111_config *const cfg = dev->config;
113 
114 	return mdio_read(cfg->mdio, cfg->phy_addr, reg, val);
115 }
116 
phy_adin2111_c22_write(const struct device * dev,uint16_t reg,uint16_t val)117 static inline int phy_adin2111_c22_write(const struct device *dev, uint16_t reg,
118 					 uint16_t val)
119 {
120 	const struct phy_adin2111_config *const cfg = dev->config;
121 
122 	return mdio_write(cfg->mdio, cfg->phy_addr, reg, val);
123 }
124 
phy_adin2111_c45_setup_dev_reg(const struct device * dev,uint16_t devad,uint16_t reg)125 static int phy_adin2111_c45_setup_dev_reg(const struct device *dev, uint16_t devad,
126 					  uint16_t reg)
127 {
128 	const struct phy_adin2111_config *cfg = dev->config;
129 	int rval;
130 
131 	rval = mdio_write(cfg->mdio, cfg->phy_addr, ADIN1100_MMD_ACCESS_CNTRL, devad);
132 	if (rval < 0) {
133 		return rval;
134 	}
135 	rval = mdio_write(cfg->mdio, cfg->phy_addr, ADIN1100_MMD_ACCESS, reg);
136 	if (rval < 0) {
137 		return rval;
138 	}
139 
140 	return mdio_write(cfg->mdio, cfg->phy_addr, ADIN1100_MMD_ACCESS_CNTRL, devad | BIT(14));
141 }
142 
phy_adin2111_c45_read(const struct device * dev,uint16_t devad,uint16_t reg,uint16_t * val)143 static int phy_adin2111_c45_read(const struct device *dev, uint16_t devad,
144 				 uint16_t reg, uint16_t *val)
145 {
146 	const struct phy_adin2111_config *cfg = dev->config;
147 	int rval;
148 
149 	if (cfg->mii) {
150 		/* Using C22 -> devad bridge */
151 		rval = phy_adin2111_c45_setup_dev_reg(dev, devad, reg);
152 		if (rval < 0) {
153 			return rval;
154 		}
155 
156 		return mdio_read(cfg->mdio, cfg->phy_addr, ADIN1100_MMD_ACCESS, val);
157 	}
158 
159 	return mdio_read_c45(cfg->mdio, cfg->phy_addr, devad, reg, val);
160 }
161 
phy_adin2111_c45_write(const struct device * dev,uint16_t devad,uint16_t reg,uint16_t val)162 static int phy_adin2111_c45_write(const struct device *dev, uint16_t devad,
163 				  uint16_t reg, uint16_t val)
164 {
165 	const struct phy_adin2111_config *cfg = dev->config;
166 	int rval;
167 
168 	if (cfg->mii) {
169 		/* Using C22 -> devad bridge */
170 		rval = phy_adin2111_c45_setup_dev_reg(dev, devad, reg);
171 		if (rval < 0) {
172 			return rval;
173 		}
174 
175 		return mdio_write(cfg->mdio, cfg->phy_addr, ADIN1100_MMD_ACCESS, val);
176 	}
177 
178 	return mdio_write_c45(cfg->mdio, cfg->phy_addr, devad, reg, val);
179 }
180 
phy_adin2111_reg_read(const struct device * dev,uint16_t reg_addr,uint32_t * data)181 static int phy_adin2111_reg_read(const struct device *dev, uint16_t reg_addr,
182 				 uint32_t *data)
183 {
184 	const struct phy_adin2111_config *cfg = dev->config;
185 	int ret;
186 
187 	mdio_bus_enable(cfg->mdio);
188 
189 	ret = phy_adin2111_c22_read(dev, reg_addr, (uint16_t *) data);
190 
191 	mdio_bus_disable(cfg->mdio);
192 
193 	return ret;
194 }
195 
phy_adin2111_reg_write(const struct device * dev,uint16_t reg_addr,uint32_t data)196 static int phy_adin2111_reg_write(const struct device *dev, uint16_t reg_addr,
197 				  uint32_t data)
198 {
199 	const struct phy_adin2111_config *cfg = dev->config;
200 	int ret;
201 
202 	mdio_bus_enable(cfg->mdio);
203 
204 	ret = phy_adin2111_c22_write(dev, reg_addr, (uint16_t) data);
205 
206 	mdio_bus_disable(cfg->mdio);
207 
208 	return ret;
209 }
210 
phy_adin2111_await_phy(const struct device * dev)211 static int phy_adin2111_await_phy(const struct device *dev)
212 {
213 	int ret;
214 	uint32_t count;
215 	uint16_t val;
216 
217 	/**
218 	 * Port 2 PHY comes out of reset after Port 1 PHY,
219 	 * wait until both are out of reset.
220 	 * Reading Port 2 PHY registers returns 0s until
221 	 * it comes out from reset.
222 	 */
223 	for (count = 0U; count < ADIN2111_PHY_AWAIT_RETRY_COUNT; ++count) {
224 		ret = phy_adin2111_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC1,
225 					    ADIN2111_PHY_CRSM_IRQ_MASK, &val);
226 		if (ret >= 0) {
227 			if (val != 0U) {
228 				break;
229 			}
230 			ret = -ETIMEDOUT;
231 		}
232 		k_sleep(K_USEC(ADIN2111_PHY_AWAIT_DELAY_POLL_US));
233 	}
234 
235 	return ret;
236 }
237 
phy_adin2111_an_state_read(const struct device * dev)238 static int phy_adin2111_an_state_read(const struct device *dev)
239 {
240 	struct phy_adin2111_data *const data = dev->data;
241 	uint16_t bmsr;
242 	int ret;
243 
244 	/* read twice to get actual link status, latch low */
245 	ret = phy_adin2111_c22_read(dev, MII_BMSR, &bmsr);
246 	if (ret < 0) {
247 		return ret;
248 	}
249 	ret = phy_adin2111_c22_read(dev, MII_BMSR, &bmsr);
250 	if (ret < 0) {
251 		return ret;
252 	}
253 
254 	data->state.is_up = !!(bmsr & MII_BMSR_LINK_STATUS);
255 
256 	return 0;
257 }
258 
phy_adin2111_handle_phy_irq(const struct device * dev,struct phy_link_state * state)259 int phy_adin2111_handle_phy_irq(const struct device *dev,
260 				struct phy_link_state *state)
261 {
262 	struct phy_adin2111_data *const data = dev->data;
263 	uint16_t subsys_status;
264 	int ret;
265 
266 	ret = phy_adin2111_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC2,
267 				    ADIN2111_PHY_SUBSYS_IRQ_STATUS,
268 				    &subsys_status);
269 	if (ret < 0) {
270 		return ret;
271 	}
272 
273 	if ((subsys_status & ADIN2111_PHY_SUBSYS_IRQ_STATUS_LINK_STAT_CHNG_LH) == 0U) {
274 		/* nothing to process */
275 		return -EAGAIN;
276 	}
277 
278 	k_sem_take(&data->sem, K_FOREVER);
279 
280 	ret = phy_adin2111_an_state_read(dev);
281 
282 	memcpy(state, &data->state, sizeof(struct phy_link_state));
283 
284 	k_sem_give(&data->sem);
285 
286 	return ret;
287 }
288 
phy_adin2111_sft_pd(const struct device * dev,bool enter)289 static int phy_adin2111_sft_pd(const struct device *dev, bool enter)
290 {
291 	int ret;
292 	uint32_t count;
293 	const uint16_t expected = enter ? ADIN2111_CRSM_STAT_CRSM_SFT_PD_RDY : 0U;
294 	uint16_t val;
295 
296 	ret = phy_adin2111_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC1,
297 				     ADIN2111_PHY_CRSM_SFT_PD_CNTRL,
298 				     enter ? 1U : 0U);
299 	if (ret < 0) {
300 		return ret;
301 	}
302 
303 	for (count = 0U; count < ADIN2111_PHY_SFT_PD_RETRY_COUNT; ++count) {
304 		ret = phy_adin2111_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC1,
305 					    ADIN2111_PHY_CRSM_STAT, &val);
306 		if (ret >= 0) {
307 			if ((val & ADIN2111_CRSM_STAT_CRSM_SFT_PD_RDY) == expected) {
308 				break;
309 			}
310 			ret = -ETIMEDOUT;
311 		}
312 		k_sleep(K_USEC(ADIN2111_PHY_SFT_PD_DELAY_POLL_US));
313 	}
314 
315 	return ret;
316 }
317 
phy_adin2111_id(const struct device * dev,uint32_t * phy_id)318 static int phy_adin2111_id(const struct device *dev, uint32_t *phy_id)
319 {
320 	uint16_t val;
321 
322 	if (phy_adin2111_c22_read(dev, MII_PHYID1R, &val) < 0) {
323 		return -EIO;
324 	}
325 
326 	*phy_id = (val & UINT16_MAX) << 16;
327 
328 	if (phy_adin2111_c22_read(dev, MII_PHYID2R, &val) < 0) {
329 		return -EIO;
330 	}
331 
332 	*phy_id |= (val & UINT16_MAX);
333 
334 	return 0;
335 }
336 
phy_adin2111_get_link_state(const struct device * dev,struct phy_link_state * state)337 static int phy_adin2111_get_link_state(const struct device *dev,
338 				       struct phy_link_state *state)
339 {
340 	struct phy_adin2111_data *const data = dev->data;
341 
342 	k_sem_take(&data->sem, K_FOREVER);
343 
344 	memcpy(state, &data->state, sizeof(struct phy_link_state));
345 
346 	k_sem_give(&data->sem);
347 
348 	return 0;
349 }
350 
phy_adin2111_reset(const struct device * dev)351 static int phy_adin2111_reset(const struct device *dev)
352 {
353 	int ret;
354 
355 	ret = phy_adin2111_c22_write(dev, MII_BMCR, MII_BMCR_RESET);
356 	if (ret < 0) {
357 		return ret;
358 	}
359 
360 	k_msleep(ADIN1100_PHY_SFT_RESET_MS);
361 
362 	return 0;
363 }
364 
invoke_link_cb(const struct device * dev)365 static void invoke_link_cb(const struct device *dev)
366 {
367 	struct phy_adin2111_data *const data = dev->data;
368 	struct phy_link_state state;
369 
370 	if (data->cb == NULL) {
371 		return;
372 	}
373 
374 	data->cb(dev, &state, data->cb_data);
375 }
376 
update_link_state(const struct device * dev)377 static int update_link_state(const struct device *dev)
378 {
379 	struct phy_adin2111_data *const data = dev->data;
380 	const struct phy_adin2111_config *config = dev->config;
381 	struct phy_link_state old_state;
382 	uint16_t bmsr;
383 	int ret;
384 
385 	ret = phy_adin2111_c22_read(dev, MII_BMSR, &bmsr);
386 	if (ret < 0) {
387 		return ret;
388 	}
389 
390 	old_state = data->state;
391 	data->state.is_up = !!(bmsr & MII_BMSR_LINK_STATUS);
392 
393 	if (old_state.speed != data->state.speed || old_state.is_up != data->state.is_up) {
394 
395 		LOG_INF("PHY (%d) Link is %s", config->phy_addr, data->state.is_up ? "up" : "down");
396 
397 		if (data->state.is_up == false) {
398 			return 0;
399 		}
400 
401 		invoke_link_cb(dev);
402 
403 		LOG_INF("PHY (%d) Link speed %s Mb, %s duplex\n", config->phy_addr,
404 			(PHY_LINK_IS_SPEED_100M(data->state.speed) ? "100" : "10"),
405 			PHY_LINK_IS_FULL_DUPLEX(data->state.speed) ? "full" : "half");
406 	}
407 
408 	return 0;
409 }
410 
monitor_work_handler(struct k_work * work)411 static void monitor_work_handler(struct k_work *work)
412 {
413 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
414 	struct phy_adin2111_data *const data =
415 		CONTAINER_OF(dwork, struct phy_adin2111_data, monitor_work);
416 	const struct device *dev = data->dev;
417 	int rc;
418 
419 	k_sem_take(&data->sem, K_FOREVER);
420 
421 	rc = update_link_state(dev);
422 
423 	k_sem_give(&data->sem);
424 
425 	/* Submit delayed work */
426 	k_work_reschedule(&data->monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
427 }
428 
phy_adin2111_init(const struct device * dev)429 static int phy_adin2111_init(const struct device *dev)
430 {
431 	const struct phy_adin2111_config *const cfg = dev->config;
432 	struct phy_adin2111_data *const data = dev->data;
433 	uint32_t phy_id;
434 	uint16_t val;
435 	bool tx_24v_supported = false;
436 	int ret;
437 
438 	data->dev = dev;
439 	data->state.is_up = false;
440 	data->state.speed = LINK_FULL_10BASE;
441 
442 	/*
443 	 * For adin1100 and further mii stuff,
444 	 * reset may not be performed from the mac layer, doing a clean reset here.
445 	 */
446 	if (cfg->mii) {
447 		ret = phy_adin2111_reset(dev);
448 		if (ret < 0) {
449 			return ret;
450 		}
451 	}
452 
453 	ret = phy_adin2111_await_phy(dev);
454 	if (ret < 0) {
455 		LOG_ERR("PHY %u didn't come out of reset, %d",
456 			cfg->phy_addr, ret);
457 		return -ENODEV;
458 	}
459 
460 	ret = phy_adin2111_id(dev, &phy_id);
461 	if (ret < 0) {
462 		LOG_ERR("Failed to read PHY %u ID, %d",
463 			cfg->phy_addr, ret);
464 		return -ENODEV;
465 	}
466 
467 	if (phy_id != ADIN2111_PHY_ID && phy_id != ADIN1110_PHY_ID && phy_id != ADIN1100_PHY_ID) {
468 		LOG_ERR("PHY %u unexpected PHY ID %X", cfg->phy_addr, phy_id);
469 		return -EINVAL;
470 	}
471 
472 	LOG_INF("PHY %u ID %X", cfg->phy_addr, phy_id);
473 
474 	/* enter software powerdown */
475 	ret = phy_adin2111_sft_pd(dev, true);
476 	if (ret < 0) {
477 		return ret;
478 	}
479 
480 	/* disable interrupts */
481 	ret = phy_adin2111_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC1,
482 				     ADIN2111_PHY_CRSM_IRQ_MASK, 0U);
483 	if (ret < 0) {
484 		return ret;
485 	}
486 
487 	/* enable link status change irq */
488 	ret = phy_adin2111_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC2,
489 				     ADIN2111_PHY_SUBSYS_IRQ_MASK,
490 				     ADIN2111_PHY_SUBSYS_IRQ_STATUS_LINK_STAT_CHNG_LH);
491 	if (ret < 0) {
492 		return ret;
493 	}
494 
495 	/* clear PHY IRQ status before enabling ADIN IRQs */
496 	ret = phy_adin2111_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC1,
497 				    ADIN2111_PHY_CRSM_IRQ_STATUS, &val);
498 	if (ret < 0) {
499 		return ret;
500 	}
501 
502 	if (val & ADIN2111_PHY_CRSM_IRQ_STATUS_FATAL_ERR) {
503 		LOG_ERR("PHY %u CRSM reports fatal system error", cfg->phy_addr);
504 		return -ENODEV;
505 	}
506 
507 	ret = phy_adin2111_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC2,
508 				    ADIN2111_PHY_SUBSYS_IRQ_STATUS, &val);
509 	if (ret < 0) {
510 		return ret;
511 	}
512 
513 	if (!cfg->led0_en || !cfg->led1_en) {
514 		ret = phy_adin2111_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC1,
515 					    ADIN2111_PHY_LED_CNTRL, &val);
516 		if (ret < 0) {
517 			return ret;
518 		}
519 		if (!cfg->led0_en) {
520 			val &= ~(ADIN2111_PHY_LED_CNTRL_LED0_EN);
521 		}
522 		if (!cfg->led1_en) {
523 			val &= ~(ADIN2111_PHY_LED_CNTRL_LED1_EN);
524 		}
525 		ret = phy_adin2111_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC1,
526 					     ADIN2111_PHY_LED_CNTRL, val);
527 		if (ret < 0) {
528 			return ret;
529 		}
530 	}
531 
532 	/* check 2.4V support */
533 	ret = phy_adin2111_c45_read(dev, MDIO_MMD_PMAPMD, MDIO_PMA_B10L_STAT, &val);
534 	if (ret < 0) {
535 		return ret;
536 	}
537 
538 	tx_24v_supported = !!(val & MDIO_PMA_B10L_STAT_2V4_ABLE);
539 
540 	LOG_INF("PHY %u 2.4V mode %s", cfg->phy_addr,
541 		tx_24v_supported ? "supported" : "not supported");
542 
543 	if (!cfg->tx_24v & tx_24v_supported) {
544 		LOG_ERR("PHY %u 2.4V mode supported, but not enabled",
545 			cfg->phy_addr);
546 	}
547 
548 	/* config 2.4V auto-negotiation */
549 	ret = phy_adin2111_c45_read(dev, MDIO_MMD_AN, MDIO_AN_T1_ADV_H, &val);
550 	if (ret < 0) {
551 		return ret;
552 	}
553 
554 	if (tx_24v_supported) {
555 		val |= MDIO_AN_T1_ADV_H_10L_TX_HI;
556 	} else {
557 		val &= ~MDIO_AN_T1_ADV_H_10L_TX_HI;
558 	}
559 
560 	if (cfg->tx_24v) {
561 		if (!tx_24v_supported) {
562 			LOG_ERR("PHY %u 2.4V mode enabled, but not supported",
563 				cfg->phy_addr);
564 			return -EINVAL;
565 		}
566 
567 		val |= MDIO_AN_T1_ADV_H_10L_TX_HI_REQ;
568 	} else {
569 		val &= ~MDIO_AN_T1_ADV_H_10L_TX_HI_REQ;
570 	}
571 
572 	ret = phy_adin2111_c45_write(dev, MDIO_MMD_AN, MDIO_AN_T1_ADV_H, val);
573 	if (ret < 0) {
574 		return ret;
575 	}
576 
577 	/* enable auto-negotiation */
578 	ret = phy_adin2111_c45_write(dev, MDIO_MMD_AN, MDIO_AN_T1_CTRL,
579 				     MDIO_AN_T1_CTRL_EN);
580 	if (ret < 0) {
581 		return ret;
582 	}
583 
584 	if (cfg->mii) {
585 		k_work_init_delayable(&data->monitor_work, monitor_work_handler);
586 		monitor_work_handler(&data->monitor_work.work);
587 	}
588 
589 	/**
590 	 * done, PHY is in software powerdown (SFT PD)
591 	 * exit software powerdown, PHY 1 has to exit before PHY 2
592 	 * correct PHY order is expected to be in DTS to guarantee that
593 	 */
594 	return phy_adin2111_sft_pd(dev, false);
595 }
596 
phy_adin2111_link_cb_set(const struct device * dev,phy_callback_t cb,void * user_data)597 static int phy_adin2111_link_cb_set(const struct device *dev, phy_callback_t cb,
598 				    void *user_data)
599 {
600 	struct phy_adin2111_data *const data = dev->data;
601 
602 	data->cb = cb;
603 	data->cb_data = user_data;
604 
605 	/* Invoke the callback to notify the caller of the current
606 	 * link status.
607 	 */
608 	invoke_link_cb(dev);
609 
610 	return 0;
611 }
612 
613 static DEVICE_API(ethphy, phy_adin2111_api) = {
614 	.get_link = phy_adin2111_get_link_state,
615 	.link_cb_set = phy_adin2111_link_cb_set,
616 	.read = phy_adin2111_reg_read,
617 	.write = phy_adin2111_reg_write,
618 };
619 
620 #define ADIN2111_PHY_INITIALIZE(n, model)						\
621 	static const struct phy_adin2111_config phy_adin##model##_config_##n = {	\
622 		.mdio = DEVICE_DT_GET(DT_INST_BUS(n)),					\
623 		.phy_addr = DT_INST_REG_ADDR(n),					\
624 		.led0_en = DT_INST_PROP(n, led0_en),					\
625 		.led1_en = DT_INST_PROP(n, led1_en),					\
626 		.tx_24v = !(DT_INST_PROP(n, disable_tx_mode_24v)),			\
627 		IF_ENABLED(DT_HAS_COMPAT_STATUS_OKAY(adi_adin1100_phy),			\
628 		(.mii = 1))								\
629 	};										\
630 	static struct phy_adin2111_data phy_adin##model##_data_##n = {			\
631 		.sem = Z_SEM_INITIALIZER(phy_adin##model##_data_##n.sem, 1, 1),		\
632 	};										\
633 	DEVICE_DT_INST_DEFINE(n, &phy_adin2111_init, NULL,				\
634 			      &phy_adin##model##_data_##n,				\
635 			      &phy_adin##model##_config_##n,				\
636 			      POST_KERNEL, CONFIG_PHY_INIT_PRIORITY,			\
637 			      &phy_adin2111_api);
638 
639 #define DT_DRV_COMPAT adi_adin2111_phy
640 DT_INST_FOREACH_STATUS_OKAY_VARGS(ADIN2111_PHY_INITIALIZE, 2111)
641 #undef DT_DRV_COMPAT
642 #define DT_DRV_COMPAT adi_adin1100_phy
643 DT_INST_FOREACH_STATUS_OKAY_VARGS(ADIN2111_PHY_INITIALIZE, 1100)
644 #undef DT_DRV_COMPAT
645