1 /*
2  * Copyright 2024 Bernhard Kraemer
3  *
4  * Inspiration from phy_realtek_rtl8211f.c, which is:
5  * Copyright 2023-2024 NXP
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #define DT_DRV_COMPAT ti_dp83825
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/net/phy.h>
14 #include <zephyr/net/mii.h>
15 #include <zephyr/drivers/mdio.h>
16 #include <string.h>
17 #include <zephyr/sys/util_macro.h>
18 #include <zephyr/drivers/gpio.h>
19 
20 #define LOG_MODULE_NAME phy_ti_dp83825
21 #define LOG_LEVEL       CONFIG_PHY_LOG_LEVEL
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
24 
25 #define PHY_TI_DP83825_PHYSCR_REG     0x11
26 #define PHY_TI_DP83825_PHYSCR_REG_IE  BIT(1)
27 #define PHY_TI_DP83825_PHYSCR_REG_IOE BIT(0)
28 
29 #define PHY_TI_DP83825_MISR_REG      0x12
30 #define PHY_TI_DP83825_MISR_REG_LSCE BIT(5)
31 
32 #define PHY_TI_DP83825_RCSR_REG         0x17
33 #define PHY_TI_DP83825_RCSR_REF_CLK_SEL BIT(7)
34 
35 #define PHY_TI_DP83825_POR_DELAY 50
36 
37 enum dp83825_interface {
38 	DP83825_RMII,
39 	DP83825_RMII_25MHZ
40 };
41 
42 struct ti_dp83825_config {
43 	uint8_t addr;
44 	const struct device *mdio_dev;
45 	enum dp83825_interface phy_iface;
46 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
47 	const struct gpio_dt_spec reset_gpio;
48 #endif
49 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
50 	const struct gpio_dt_spec interrupt_gpio;
51 #endif
52 };
53 
54 struct ti_dp83825_data {
55 	const struct device *dev;
56 	struct phy_link_state state;
57 	phy_callback_t cb;
58 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
59 	struct gpio_callback gpio_callback;
60 #endif
61 	void *cb_data;
62 	struct k_mutex mutex;
63 	struct k_work_delayable phy_monitor_work;
64 };
65 
phy_ti_dp83825_read(const struct device * dev,uint16_t reg_addr,uint32_t * data)66 static int phy_ti_dp83825_read(const struct device *dev, uint16_t reg_addr, uint32_t *data)
67 {
68 	const struct ti_dp83825_config *config = dev->config;
69 	int ret;
70 
71 	/* Make sure excessive bits 16-31 are reset */
72 	*data = 0U;
73 
74 	ret = mdio_read(config->mdio_dev, config->addr, reg_addr, (uint16_t *)data);
75 	if (ret) {
76 		return ret;
77 	}
78 
79 	return 0;
80 }
81 
phy_ti_dp83825_write(const struct device * dev,uint16_t reg_addr,uint32_t data)82 static int phy_ti_dp83825_write(const struct device *dev, uint16_t reg_addr, uint32_t data)
83 {
84 	const struct ti_dp83825_config *config = dev->config;
85 	int ret;
86 
87 	ret = mdio_write(config->mdio_dev, config->addr, reg_addr, (uint16_t)data);
88 	if (ret) {
89 		return ret;
90 	}
91 
92 	return 0;
93 }
94 
95 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
phy_ti_dp83825_clear_interrupt(struct ti_dp83825_data * data)96 static int phy_ti_dp83825_clear_interrupt(struct ti_dp83825_data *data)
97 {
98 	const struct device *dev = data->dev;
99 	const struct ti_dp83825_config *config = dev->config;
100 	uint32_t reg_val;
101 	int ret;
102 
103 	/* Lock mutex */
104 	ret = k_mutex_lock(&data->mutex, K_FOREVER);
105 	if (ret) {
106 		LOG_ERR("PHY mutex lock error");
107 		return ret;
108 	}
109 
110 	/* Read/clear PHY interrupt status register */
111 	ret = phy_ti_dp83825_read(dev, PHY_TI_DP83825_MISR_REG, &reg_val);
112 	if (ret) {
113 		LOG_ERR("Error reading phy (%d) interrupt status register", config->addr);
114 	}
115 
116 	/* Unlock mutex */
117 	(void)k_mutex_unlock(&data->mutex);
118 
119 	return ret;
120 }
121 
phy_ti_dp83825_interrupt_handler(const struct device * port,struct gpio_callback * cb,gpio_port_pins_t pins)122 static void phy_ti_dp83825_interrupt_handler(const struct device *port, struct gpio_callback *cb,
123 					     gpio_port_pins_t pins)
124 {
125 	struct ti_dp83825_data *data = CONTAINER_OF(cb, struct ti_dp83825_data, gpio_callback);
126 	int ret;
127 
128 	ret = k_work_reschedule(&data->phy_monitor_work, K_NO_WAIT);
129 	if (ret < 0) {
130 		LOG_ERR("Failed to schedule phy_monitor_work from ISR");
131 	}
132 }
133 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
134 
phy_ti_dp83825_autonegotiate(const struct device * dev)135 static int phy_ti_dp83825_autonegotiate(const struct device *dev)
136 {
137 	const struct ti_dp83825_config *config = dev->config;
138 	int ret;
139 	uint32_t bmcr = 0;
140 
141 	/* Read control register to write back with autonegotiation bit */
142 	ret = phy_ti_dp83825_read(dev, MII_BMCR, &bmcr);
143 	if (ret) {
144 		LOG_ERR("Error reading phy (%d) basic control register", config->addr);
145 		return ret;
146 	}
147 
148 	/* (re)start autonegotiation */
149 	LOG_DBG("PHY (%d) is entering autonegotiation sequence", config->addr);
150 	bmcr |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART;
151 	bmcr &= ~MII_BMCR_ISOLATE;
152 
153 	ret = phy_ti_dp83825_write(dev, MII_BMCR, bmcr);
154 	if (ret) {
155 		LOG_ERR("Error writing phy (%d) basic control register", config->addr);
156 		return ret;
157 	}
158 
159 	return 0;
160 }
161 
phy_ti_dp83825_get_link(const struct device * dev,struct phy_link_state * state)162 static int phy_ti_dp83825_get_link(const struct device *dev, struct phy_link_state *state)
163 {
164 	const struct ti_dp83825_config *config = dev->config;
165 	struct ti_dp83825_data *data = dev->data;
166 	int ret;
167 	uint32_t bmsr = 0;
168 	uint32_t anar = 0;
169 	uint32_t anlpar = 0;
170 	uint32_t mutual_capabilities;
171 	struct phy_link_state old_state = data->state;
172 
173 	/* Lock mutex */
174 	ret = k_mutex_lock(&data->mutex, K_FOREVER);
175 	if (ret) {
176 		LOG_ERR("PHY mutex lock error");
177 		return ret;
178 	}
179 
180 	/* Read link state */
181 	ret = phy_ti_dp83825_read(dev, MII_BMSR, &bmsr);
182 	if (ret) {
183 		LOG_ERR("Error reading phy (%d) basic status register", config->addr);
184 		k_mutex_unlock(&data->mutex);
185 		return ret;
186 	}
187 	state->is_up = bmsr & MII_BMSR_LINK_STATUS;
188 
189 	if (!state->is_up) {
190 		k_mutex_unlock(&data->mutex);
191 		goto result;
192 	}
193 
194 	/* Read currently configured advertising options */
195 	ret = phy_ti_dp83825_read(dev, MII_ANAR, &anar);
196 	if (ret) {
197 		LOG_ERR("Error reading phy (%d) advertising register", config->addr);
198 		k_mutex_unlock(&data->mutex);
199 		return ret;
200 	}
201 
202 	/* Read link partner capability */
203 	ret = phy_ti_dp83825_read(dev, MII_ANLPAR, &anlpar);
204 	if (ret) {
205 		LOG_ERR("Error reading phy (%d) link partner register", config->addr);
206 		k_mutex_unlock(&data->mutex);
207 		return ret;
208 	}
209 
210 	/* Unlock mutex */
211 	k_mutex_unlock(&data->mutex);
212 
213 	mutual_capabilities = anar & anlpar;
214 
215 	if (mutual_capabilities & MII_ADVERTISE_100_FULL) {
216 		state->speed = LINK_FULL_100BASE_T;
217 	} else if (mutual_capabilities & MII_ADVERTISE_100_HALF) {
218 		state->speed = LINK_HALF_100BASE_T;
219 	} else if (mutual_capabilities & MII_ADVERTISE_10_FULL) {
220 		state->speed = LINK_FULL_10BASE_T;
221 	} else if (mutual_capabilities & MII_ADVERTISE_10_HALF) {
222 		state->speed = LINK_HALF_10BASE_T;
223 	} else {
224 		return -EIO;
225 	}
226 
227 result:
228 	if (memcmp(&old_state, state, sizeof(struct phy_link_state)) != 0) {
229 		LOG_DBG("PHY %d is %s", config->addr, state->is_up ? "up" : "down");
230 		if (state->is_up) {
231 			LOG_INF("PHY (%d) Link speed %s Mb, %s duplex\n", config->addr,
232 				(PHY_LINK_IS_SPEED_100M(state->speed) ? "100" : "10"),
233 				PHY_LINK_IS_FULL_DUPLEX(state->speed) ? "full" : "half");
234 		}
235 	}
236 
237 	return ret;
238 }
239 
240 /*
241  * Configuration set statically (DT) that should never change
242  * This function is needed in case the PHY is reset then the next call
243  * to configure the phy will ensure this configuration will be redone
244  */
phy_ti_dp83825_static_cfg(const struct device * dev)245 static int phy_ti_dp83825_static_cfg(const struct device *dev)
246 {
247 	const struct ti_dp83825_config *config = dev->config;
248 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
249 	struct ti_dp83825_data *data = dev->data;
250 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
251 	uint32_t reg_val = 0;
252 	int ret = 0;
253 
254 	/* Select correct reference clock mode depending on interface setup */
255 	ret = phy_ti_dp83825_read(dev, PHY_TI_DP83825_RCSR_REG, (uint32_t *)&reg_val);
256 	if (ret) {
257 		return ret;
258 	}
259 
260 	if (config->phy_iface == DP83825_RMII) {
261 		reg_val |= PHY_TI_DP83825_RCSR_REF_CLK_SEL;
262 	} else {
263 		reg_val &= ~PHY_TI_DP83825_RCSR_REF_CLK_SEL;
264 	}
265 
266 	ret = phy_ti_dp83825_write(dev, PHY_TI_DP83825_RCSR_REG, (uint32_t)reg_val);
267 	if (ret) {
268 		return ret;
269 	}
270 
271 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
272 	/* Read PHYSCR register to write back */
273 	ret = phy_ti_dp83825_read(dev, PHY_TI_DP83825_PHYSCR_REG, &reg_val);
274 	if (ret) {
275 		return ret;
276 	}
277 
278 	/* Config INTR/PWRDN pin as Interrupt output, enable event interrupts */
279 	reg_val |= PHY_TI_DP83825_PHYSCR_REG_IOE | PHY_TI_DP83825_PHYSCR_REG_IE;
280 
281 	/* Write settings to physcr register */
282 	ret = phy_ti_dp83825_write(dev, PHY_TI_DP83825_PHYSCR_REG, reg_val);
283 	if (ret) {
284 		return ret;
285 	}
286 
287 	/* Clear interrupt */
288 	ret = phy_ti_dp83825_clear_interrupt(data);
289 	if (ret) {
290 		return ret;
291 	}
292 
293 	/* Read MISR register to write back */
294 	ret = phy_ti_dp83825_read(dev, PHY_TI_DP83825_MISR_REG, &reg_val);
295 	if (ret) {
296 		return ret;
297 	}
298 
299 	/* Enable link state changed interrupt*/
300 	reg_val |= PHY_TI_DP83825_MISR_REG_LSCE;
301 
302 	/* Write settings to misr register */
303 	ret = phy_ti_dp83825_write(dev, PHY_TI_DP83825_MISR_REG, reg_val);
304 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
305 
306 	return ret;
307 }
308 
phy_ti_dp83825_reset(const struct device * dev)309 static int phy_ti_dp83825_reset(const struct device *dev)
310 {
311 	const struct ti_dp83825_config *config = dev->config;
312 	struct ti_dp83825_data *data = dev->data;
313 	int ret;
314 
315 	/* Lock mutex */
316 	ret = k_mutex_lock(&data->mutex, K_FOREVER);
317 	if (ret) {
318 		LOG_ERR("PHY mutex lock error");
319 		return ret;
320 	}
321 
322 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
323 	if (!config->reset_gpio.port) {
324 		goto skip_reset_gpio;
325 	}
326 
327 	/* Start reset (logically ACTIVE, physically LOW) */
328 	ret = gpio_pin_set_dt(&config->reset_gpio, 1);
329 	if (ret) {
330 		goto done;
331 	}
332 
333 	/* Reset pulse (minimum specified width is T1=25us) */
334 	k_busy_wait(USEC_PER_MSEC * 1);
335 
336 	/* Reset over (logically INACTIVE, physically HIGH) */
337 	ret = gpio_pin_set_dt(&config->reset_gpio, 0);
338 
339 	/* POR release time (minimum specified is T4=50ms) */
340 	k_busy_wait(USEC_PER_MSEC * PHY_TI_DP83825_POR_DELAY);
341 
342 	goto done;
343 skip_reset_gpio:
344 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios) */
345 	ret = phy_ti_dp83825_write(dev, MII_BMCR, MII_BMCR_RESET);
346 	if (ret) {
347 		goto done;
348 	}
349 	/* POR release time (minimum specified is T4=50ms) */
350 	k_busy_wait(USEC_PER_MSEC * PHY_TI_DP83825_POR_DELAY);
351 
352 done:
353 	/* Unlock mutex */
354 	k_mutex_unlock(&data->mutex);
355 
356 	LOG_DBG("PHY (%d) reset completed", config->addr);
357 
358 	return ret;
359 }
360 
phy_ti_dp83825_cfg_link(const struct device * dev,enum phy_link_speed speeds)361 static int phy_ti_dp83825_cfg_link(const struct device *dev, enum phy_link_speed speeds)
362 {
363 	const struct ti_dp83825_config *config = dev->config;
364 	struct ti_dp83825_data *data = dev->data;
365 	int ret;
366 	uint32_t anar;
367 
368 	/* Lock mutex */
369 	ret = k_mutex_lock(&data->mutex, K_FOREVER);
370 	if (ret) {
371 		LOG_ERR("PHY mutex lock error");
372 		goto done;
373 	}
374 
375 	/* We are going to reconfigure the phy, don't need to monitor until done */
376 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
377 	if (!config->interrupt_gpio.port) {
378 		k_work_cancel_delayable(&data->phy_monitor_work);
379 	}
380 #else
381 	k_work_cancel_delayable(&data->phy_monitor_work);
382 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
383 
384 	/* Reset PHY */
385 	ret = phy_ti_dp83825_reset(dev);
386 	if (ret) {
387 		goto done;
388 	}
389 
390 	/* DT configurations */
391 	ret = phy_ti_dp83825_static_cfg(dev);
392 	if (ret) {
393 		goto done;
394 	}
395 
396 	/* Read ANAR register to write back */
397 	ret = phy_ti_dp83825_read(dev, MII_ANAR, &anar);
398 	if (ret) {
399 		LOG_ERR("Error reading phy (%d) advertising register", config->addr);
400 		goto done;
401 	}
402 
403 	/* Setup advertising register */
404 	if (speeds & LINK_FULL_100BASE_T) {
405 		anar |= MII_ADVERTISE_100_FULL;
406 	} else {
407 		anar &= ~MII_ADVERTISE_100_FULL;
408 	}
409 
410 	if (speeds & LINK_HALF_100BASE_T) {
411 		anar |= MII_ADVERTISE_100_HALF;
412 	} else {
413 		anar &= ~MII_ADVERTISE_100_HALF;
414 	}
415 
416 	if (speeds & LINK_FULL_10BASE_T) {
417 		anar |= MII_ADVERTISE_10_FULL;
418 	} else {
419 		anar &= ~MII_ADVERTISE_10_FULL;
420 	}
421 
422 	if (speeds & LINK_HALF_10BASE_T) {
423 		anar |= MII_ADVERTISE_10_HALF;
424 	} else {
425 		anar &= ~MII_ADVERTISE_10_HALF;
426 	}
427 
428 	/* Write capabilities to advertising register */
429 	ret = phy_ti_dp83825_write(dev, MII_ANAR, anar);
430 	if (ret) {
431 		LOG_ERR("Error writing phy (%d) advertising register", config->addr);
432 		goto done;
433 	}
434 
435 	/* (re)do autonegotiation */
436 	ret = phy_ti_dp83825_autonegotiate(dev);
437 	if (ret && (ret != -ENETDOWN)) {
438 		LOG_ERR("Error in autonegotiation");
439 		goto done;
440 	}
441 
442 done:
443 	/* Unlock mutex */
444 	k_mutex_unlock(&data->mutex);
445 
446 	/* Start monitoring */
447 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
448 	if (!config->interrupt_gpio.port) {
449 		k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
450 	}
451 #else
452 	k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
453 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
454 
455 	return ret;
456 }
457 
phy_ti_dp83825_link_cb_set(const struct device * dev,phy_callback_t cb,void * user_data)458 static int phy_ti_dp83825_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data)
459 {
460 	struct ti_dp83825_data *data = dev->data;
461 
462 	data->cb = cb;
463 	data->cb_data = user_data;
464 
465 	phy_ti_dp83825_get_link(dev, &data->state);
466 
467 	data->cb(dev, &data->state, data->cb_data);
468 
469 	return 0;
470 }
471 
phy_ti_dp83825_monitor_work_handler(struct k_work * work)472 static void phy_ti_dp83825_monitor_work_handler(struct k_work *work)
473 {
474 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
475 	struct ti_dp83825_data *data =
476 		CONTAINER_OF(dwork, struct ti_dp83825_data, phy_monitor_work);
477 	const struct device *dev = data->dev;
478 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
479 	const struct ti_dp83825_config *config = dev->config;
480 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
481 	struct phy_link_state state = {};
482 	int ret;
483 
484 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
485 	if (config->interrupt_gpio.port) {
486 		ret = phy_ti_dp83825_clear_interrupt(data);
487 		if (ret) {
488 			return;
489 		}
490 	}
491 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
492 
493 	ret = phy_ti_dp83825_get_link(dev, &state);
494 
495 	if (ret == 0 && memcmp(&state, &data->state, sizeof(struct phy_link_state)) != 0) {
496 		memcpy(&data->state, &state, sizeof(struct phy_link_state));
497 		if (data->cb) {
498 			data->cb(dev, &data->state, data->cb_data);
499 		}
500 	}
501 
502 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
503 	if (!config->interrupt_gpio.port) {
504 		k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
505 	}
506 #else
507 	k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
508 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
509 }
510 
phy_ti_dp83825_init(const struct device * dev)511 static int phy_ti_dp83825_init(const struct device *dev)
512 {
513 	const struct ti_dp83825_config *config = dev->config;
514 	struct ti_dp83825_data *data = dev->data;
515 	int ret;
516 
517 	data->dev = dev;
518 
519 	ret = k_mutex_init(&data->mutex);
520 	if (ret) {
521 		return ret;
522 	}
523 
524 	mdio_bus_enable(config->mdio_dev);
525 
526 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
527 	if (config->reset_gpio.port) {
528 		ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
529 		if (ret) {
530 			return ret;
531 		}
532 	}
533 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios) */
534 
535 	k_work_init_delayable(&data->phy_monitor_work, phy_ti_dp83825_monitor_work_handler);
536 
537 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
538 	if (!config->interrupt_gpio.port) {
539 		phy_ti_dp83825_monitor_work_handler(&data->phy_monitor_work.work);
540 		goto skip_int_gpio;
541 	}
542 
543 	/* Configure interrupt pin */
544 	ret = gpio_pin_configure_dt(&config->interrupt_gpio, GPIO_INPUT);
545 	if (ret) {
546 		return ret;
547 	}
548 
549 	gpio_init_callback(&data->gpio_callback, phy_ti_dp83825_interrupt_handler,
550 			   BIT(config->interrupt_gpio.pin));
551 	ret = gpio_add_callback_dt(&config->interrupt_gpio, &data->gpio_callback);
552 	if (ret) {
553 		return ret;
554 	}
555 
556 	ret = gpio_pin_interrupt_configure_dt(&config->interrupt_gpio, GPIO_INT_EDGE_TO_ACTIVE);
557 	if (ret) {
558 		return ret;
559 	}
560 
561 skip_int_gpio:
562 #else
563 	phy_ti_dp83825_monitor_work_handler(&data->phy_monitor_work.work);
564 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
565 
566 	return 0;
567 }
568 
569 static DEVICE_API(ethphy, ti_dp83825_phy_api) = {
570 	.get_link = phy_ti_dp83825_get_link,
571 	.cfg_link = phy_ti_dp83825_cfg_link,
572 	.link_cb_set = phy_ti_dp83825_link_cb_set,
573 	.read = phy_ti_dp83825_read,
574 	.write = phy_ti_dp83825_write,
575 };
576 
577 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
578 #define RESET_GPIO(n) .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}),
579 #else
580 #define RESET_GPIO(n)
581 #endif /* reset gpio */
582 
583 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
584 #define INTERRUPT_GPIO(n) .interrupt_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}),
585 #else
586 #define INTERRUPT_GPIO(n)
587 #endif /* interrupt gpio */
588 
589 #define TI_DP83825_INIT(n)                                                                         \
590 	static const struct ti_dp83825_config ti_dp83825_##n##_config = {                          \
591 		.addr = DT_INST_REG_ADDR(n),                                                       \
592 		.mdio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)),                                      \
593 		.phy_iface = DT_INST_ENUM_IDX(n, ti_interface_type),                               \
594 		RESET_GPIO(n) INTERRUPT_GPIO(n)};                                                  \
595                                                                                                    \
596 	static struct ti_dp83825_data ti_dp83825_##n##_data;                                       \
597                                                                                                    \
598 	DEVICE_DT_INST_DEFINE(n, &phy_ti_dp83825_init, NULL, &ti_dp83825_##n##_data,               \
599 			      &ti_dp83825_##n##_config, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY,     \
600 			      &ti_dp83825_phy_api);
601 
602 DT_INST_FOREACH_STATUS_OKAY(TI_DP83825_INIT)
603