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, ®_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 *)®_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, ®_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, ®_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