1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 * Copyright (c) 2024 sensry.io
4 */
5
6 #define DT_DRV_COMPAT microchip_vsc8541
7
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_REGISTER(microchip_vsc8541, CONFIG_PHY_LOG_LEVEL);
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/net/phy.h>
13 #include <zephyr/net/mii.h>
14 #include <zephyr/drivers/mdio.h>
15 #include <string.h>
16 #include <zephyr/sys/util_macro.h>
17 #include <zephyr/drivers/gpio.h>
18
19 /* phy page selectors */
20 #define PHY_PAGE_0 0x00 /* main registers space active */
21 #define PHY_PAGE_1 0x01 /* reg 16 - 30 will be redirected to ext. register space 1 */
22 #define PHY_PAGE_2 0x02 /* reg 16 - 30 will be redirected to ext. register space 2 */
23 #define PHY_PAGE_3 0x03 /* reg 0 - 30 will be redirected to gpio register space */
24
25 /* virtual register, treat higher byte as page selector and lower byte is register */
26 #define PHY_REG(page, reg) ((page << 8) | (reg << 0))
27
28 /* Generic Register */
29 #define PHY_REG_PAGE0_BMCR PHY_REG(PHY_PAGE_0, 0x00)
30 #define PHY_REG_PAGE0_BMSR PHY_REG(PHY_PAGE_0, 0x01)
31 #define PHY_REG_PAGE0_ID1 PHY_REG(PHY_PAGE_0, 0x02)
32 #define PHY_REG_PAGE0_ID2 PHY_REG(PHY_PAGE_0, 0x03)
33 #define PHY_REG_PAGE0_ADV PHY_REG(PHY_PAGE_0, 0x04)
34 #define PHY_REG_LPA 0x05
35 #define PHY_REG_EXP 0x06
36 #define PHY_REG_PAGE0_CTRL1000 PHY_REG(PHY_PAGE_0, 0x09)
37 #define PHY_REG_PAGE0_STAT1000 PHY_REG(0, 0x0A)
38 #define PHY_REG_MMD_CTRL 0x0D
39 #define PHY_REG_MMD_DATA 0x0E
40 #define PHY_REG_STAT1000_EXT1 0x0F
41 #define PHY_REG_PAGE0_STAT100 PHY_REG(PHY_PAGE_0, 0x10)
42 #define PHY_REG_PAGE0_STAT1000_EXT2 PHY_REG(PHY_PAGE_0, 0x11)
43 #define PHY_REG_AUX_CTRL 0x12
44 #define PHY_REG_PAGE0_ERROR_COUNTER_1 PHY_REG(0, 0x13)
45 #define PHY_REG_PAGE0_ERROR_COUNTER_2 PHY_REG(0, 0x14)
46 #define PHY_REG_PAGE0_EXT_CTRL_STAT PHY_REG(PHY_PAGE_0, 0x16)
47 #define PHY_REG_PAGE0_EXT_CONTROL_1 PHY_REG(PHY_PAGE_0, 0x17)
48 #define PHY_REG_LED_MODE 0x1d
49
50 #define PHY_REG_PAGE_SELECTOR 0x1F
51
52 /* Extended Register */
53 #define PHY_REG_PAGE1_EXT_MODE_CTRL PHY_REG(PHY_PAGE_1, 0x13)
54 #define PHY_REG_PAGE2_RGMII_CONTROL PHY_REG(PHY_PAGE_2, 0x14)
55 #define PHY_REG_PAGE2_MAC_IF_CONTROL PHY_REG(PHY_PAGE_2, 0x1b)
56
57 /* selected bits in registers */
58 #define BMCR_RESET (1 << 15)
59 #define BMCR_LOOPBACK (1 << 14)
60 #define BMCR_ANENABLE (1 << 12)
61 #define BMCR_ANRESTART (1 << 9)
62 #define BMCR_FULLDPLX (1 << 8)
63 #define BMCR_SPEED10 ((0 << 13) | (0 << 6))
64 #define BMCR_SPEED100 ((1 << 13) | (0 << 6))
65 #define BMCR_SPEED1000 ((0 << 13) | (1 << 6))
66
67 #define BMCR_SPEEDMASK ((1 << 13) | (1 << 6))
68
69 #define BMSR_LSTATUS (1 << 2)
70
71 enum vsc8541_interface {
72 VSC8541_MII,
73 VSC8541_RMII,
74 VSC8541_GMII,
75 VSC8541_RGMII,
76 };
77
78 /* Thread stack size */
79 #define STACK_SIZE 512
80
81 /* Thread priority */
82 #define THREAD_PRIORITY 7
83
84 struct mc_vsc8541_config {
85 uint8_t addr;
86 const struct device *mdio_dev;
87 enum vsc8541_interface microchip_interface_type;
88 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
89 const struct gpio_dt_spec reset_gpio;
90 #endif
91 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
92 const struct gpio_dt_spec interrupt_gpio;
93 #endif
94 };
95
96 struct mc_vsc8541_data {
97 const struct device *dev;
98
99 struct phy_link_state state;
100 int active_page;
101
102 struct k_mutex mutex;
103
104 phy_callback_t cb;
105 void *cb_data;
106
107 struct k_thread link_monitor_thread;
108 uint8_t link_monitor_thread_stack[STACK_SIZE];
109 };
110
111 static int phy_mc_vsc8541_read(const struct device *dev, uint16_t reg_addr, uint32_t *data);
112 static int phy_mc_vsc8541_write(const struct device *dev, uint16_t reg_addr, uint32_t data);
113 static void phy_mc_vsc8541_link_monitor(void *arg1, void *arg2, void *arg3);
114
115 #if CONFIG_PHY_VERIFY_DEVICE_IDENTIFICATION
116 /**
117 * @brief Reads the phy manufacture id and compares to designed, known model version
118 */
phy_mc_vsc8541_verify_phy_id(const struct device * dev)119 static int phy_mc_vsc8541_verify_phy_id(const struct device *dev)
120 {
121 uint16_t phy_id_1;
122 uint16_t phy_id_2;
123
124 if (0 != phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_ID1, (uint32_t *)&phy_id_1)) {
125 return -EINVAL;
126 }
127
128 if (0 != phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_ID2, (uint32_t *)&phy_id_2)) {
129 return -EINVAL;
130 }
131
132 if (phy_id_1 == 0x0007) {
133 if (phy_id_2 == 0x0771) {
134 LOG_INF("model vsc8541-01 rev b");
135 return 0;
136 }
137 if (phy_id_2 == 0x0772) {
138 LOG_INF("model vsc8541-02/-05 rev c");
139 return 0;
140 }
141 }
142
143 LOG_INF("phy id is %#.4x - %#.4x", phy_id_1, phy_id_2);
144 return -EINVAL;
145 }
146 #endif
147
148 /**
149 * @brief Low level reset procedure that toggles the reset gpio
150 */
phy_mc_vsc8541_reset(const struct device * dev)151 static int phy_mc_vsc8541_reset(const struct device *dev)
152 {
153
154 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
155 const struct mc_vsc8541_config *cfg = dev->config;
156
157 if (!cfg->reset_gpio.port) {
158 LOG_WRN("missing reset port definition");
159 return -EINVAL;
160 }
161
162 /* configure the reset pin */
163 int ret = gpio_pin_configure_dt(&cfg->reset_gpio, GPIO_OUTPUT_ACTIVE);
164
165 if (ret) {
166 return ret;
167 }
168
169 for (uint32_t i = 0; i < 2; i++) {
170 /* Start reset */
171 ret = gpio_pin_set_dt(&cfg->reset_gpio, 0);
172 if (ret) {
173 LOG_WRN("failed to set reset gpio");
174 return -EINVAL;
175 }
176
177 /* Wait as specified by datasheet */
178 k_sleep(K_MSEC(200));
179
180 /* Reset over */
181 gpio_pin_set_dt(&cfg->reset_gpio, 1);
182
183 /* After de-asserting reset, must wait before using the config interface */
184 k_sleep(K_MSEC(200));
185 }
186 #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios) */
187
188 /* According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1,
189 * a PHY reset may take up to 0.5 s.
190 */
191 k_sleep(K_MSEC(500));
192
193 #if CONFIG_PHY_VERIFY_DEVICE_IDENTIFICATION
194 /* confirm phy organizationally unique identifier, if enabled */
195 if (0 != phy_mc_vsc8541_verify_phy_id(dev)) {
196 LOG_ERR("failed to verify phy id");
197 return -EINVAL;
198 }
199 #endif
200
201 /* set RGMII mode (must be executed BEFORE software reset -- see datasheet) */
202 if (cfg->microchip_interface_type == VSC8541_RGMII) {
203 ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_EXT_CONTROL_1,
204 (0x0 << 13) | (0x2 << 11));
205 if (ret) {
206 return ret;
207 }
208 }
209
210 /* software reset */
211 ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_BMCR, MII_BMCR_RESET);
212 if (ret) {
213 return ret;
214 }
215
216 /* wait for phy finished software reset */
217 uint32_t reg = 0;
218
219 do {
220 phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMCR, ®);
221 } while (reg & BMCR_RESET);
222
223 /* forced MDI-X */
224 ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE1_EXT_MODE_CTRL, (3 << 2));
225 if (ret) {
226 return ret;
227 }
228
229 /* configure the RGMII clk delay */
230 /* this is highly hardware dependent and may vary between pcb designs */
231 reg = 0x0;
232 /* RX_CLK delay */
233 reg |= (0x5 << 4);
234 /* TX_CLK delay */
235 reg |= (0x5 << 0);
236 ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE2_RGMII_CONTROL, reg);
237 if (ret) {
238 return ret;
239 }
240
241 /* we use limited advertising, to force gigabit speed */
242 /* initial version of this driver supports only 1GB/s */
243
244 /* 1000MBit/s + AUTO */
245 ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_ADV, (1 << 8) | (1 << 6) | 0x01);
246 if (ret) {
247 return ret;
248 }
249
250 ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_CTRL1000, (1 << 12) | (1 << 11) | (1 << 9));
251 if (ret) {
252 return ret;
253 }
254
255 /* start auto negotiation */
256 ret = phy_mc_vsc8541_write(dev, PHY_REG_PAGE0_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
257 if (ret) {
258 return ret;
259 }
260
261 return ret;
262 }
263
264 /**
265 * @brief Update the phy state with current speed given by readings of the phy
266 *
267 * @param dev device structure of the phy
268 * @param state being updated (we only update speed here)
269 */
phy_mc_vsc8541_get_speed(const struct device * dev,struct phy_link_state * state)270 static int phy_mc_vsc8541_get_speed(const struct device *dev, struct phy_link_state *state)
271 {
272 int ret;
273 uint32_t status;
274 uint32_t link10_status;
275 uint32_t link100_status;
276 uint32_t link1000_status;
277
278 ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMSR, &status);
279 if (ret) {
280 return ret;
281 }
282
283 ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_EXT_CTRL_STAT, &link10_status);
284 if (ret) {
285 return ret;
286 }
287
288 ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_STAT100, &link100_status);
289 if (ret) {
290 return ret;
291 }
292
293 ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_STAT1000_EXT2, &link1000_status);
294 if (ret) {
295 return ret;
296 }
297
298 if ((status & (1 << 2)) == 0) {
299 /* no link */
300 state->speed = LINK_HALF_10BASE_T;
301 }
302
303 if ((status & (1 << 5)) == 0) {
304 /* auto negotiation not yet complete */
305 state->speed = LINK_HALF_10BASE_T;
306 }
307
308 if ((link1000_status & (1 << 12))) {
309 state->speed = LINK_FULL_1000BASE_T;
310 }
311 if (link100_status & (1 << 12)) {
312 state->speed = LINK_FULL_100BASE_T;
313 }
314 if (link10_status & (1 << 6)) {
315 state->speed = LINK_FULL_10BASE_T;
316 }
317
318 return 0;
319 }
320
321 /**
322 * @brief Initializes the phy and starts the link monitor
323 *
324 */
phy_mc_vsc8541_init(const struct device * dev)325 static int phy_mc_vsc8541_init(const struct device *dev)
326 {
327 struct mc_vsc8541_data *data = dev->data;
328
329 data->cb = NULL;
330 data->cb_data = NULL;
331 data->state.is_up = false;
332 data->state.speed = LINK_HALF_10BASE_T;
333 data->active_page = -1;
334
335 /* Reset PHY */
336 int ret = phy_mc_vsc8541_reset(dev);
337
338 if (ret) {
339 LOG_ERR("initialize failed");
340 return ret;
341 }
342
343 /* setup thread to watch link state */
344 k_thread_create(&data->link_monitor_thread,
345 (k_thread_stack_t *)data->link_monitor_thread_stack, STACK_SIZE,
346 phy_mc_vsc8541_link_monitor, (void *)dev, NULL, NULL, THREAD_PRIORITY, 0,
347 K_NO_WAIT);
348
349 k_thread_name_set(&data->link_monitor_thread, "phy-link-mon");
350
351 return 0;
352 }
353
354 /**
355 * @brief Update the link status with readings from phy
356 *
357 * @param dev device structure to phy
358 * @param state which is being updated
359 */
phy_mc_vsc8541_get_link(const struct device * dev,struct phy_link_state * state)360 static int phy_mc_vsc8541_get_link(const struct device *dev, struct phy_link_state *state)
361 {
362 int ret;
363 uint32_t reg_sr;
364 uint32_t reg_cr;
365
366 ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMSR, ®_sr);
367 if (ret) {
368 return ret;
369 }
370
371 ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMCR, ®_cr);
372 if (ret) {
373 return ret;
374 }
375
376 uint32_t hasLink = reg_sr & (1 << 2) ? 1 : 0;
377
378 uint32_t auto_negotiation_finished;
379
380 if (reg_cr & (BMCR_ANENABLE)) {
381 /* auto negotiation active; update status */
382 auto_negotiation_finished = reg_sr & (1 << 5) ? 1 : 0;
383 } else {
384 auto_negotiation_finished = 1;
385 }
386
387 if (hasLink & auto_negotiation_finished) {
388 state->is_up = 1;
389 ret = phy_mc_vsc8541_get_speed(dev, state);
390 if (ret) {
391 return ret;
392 }
393 } else {
394 state->is_up = 0;
395 state->speed = LINK_HALF_10BASE_T;
396 }
397
398 return 0;
399 }
400
401 /**
402 * @brief Reconfigure the link speed; currently unused
403 *
404 */
phy_mc_vsc8541_cfg_link(const struct device * dev,enum phy_link_speed speeds)405 static int phy_mc_vsc8541_cfg_link(const struct device *dev, enum phy_link_speed speeds)
406 {
407 /* the initial version does not support reconfiguration */
408 return -ENOTSUP;
409 }
410
411 /**
412 * @brief Set callback which is used to announce link status changes
413 *
414 * @param dev device structure to phy device
415 * @param cb
416 * @param user_data
417 */
phy_mc_vsc8541_link_cb_set(const struct device * dev,phy_callback_t cb,void * user_data)418 static int phy_mc_vsc8541_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data)
419 {
420 struct mc_vsc8541_data *data = dev->data;
421
422 data->cb = cb;
423 data->cb_data = user_data;
424
425 phy_mc_vsc8541_get_link(dev, &data->state);
426
427 data->cb(dev, &data->state, data->cb_data);
428
429 return 0;
430 }
431
432 /**
433 * @brief Monitor thread to check the link state and announce if changed
434 *
435 * @param arg1 provides a pointer to device structure
436 * @param arg2 not used
437 * @param arg3 not used
438 */
phy_mc_vsc8541_link_monitor(void * arg1,void * arg2,void * arg3)439 void phy_mc_vsc8541_link_monitor(void *arg1, void *arg2, void *arg3)
440 {
441 const struct device *dev = arg1;
442 struct mc_vsc8541_data *data = dev->data;
443 const struct mc_vsc8541_config *cfg = dev->config;
444
445 struct phy_link_state new_state;
446
447 while (1) {
448 k_sleep(K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
449 phy_mc_vsc8541_get_link(dev, &new_state);
450
451 if ((new_state.is_up != data->state.is_up) ||
452 (new_state.speed != data->state.speed)) {
453 /* state changed */
454 data->state.is_up = new_state.is_up;
455 data->state.speed = new_state.speed;
456
457 if (data->cb) {
458 /* announce new state */
459 data->cb(dev, &data->state, data->cb_data);
460 }
461 }
462 }
463 }
464
465 /**
466 * @brief Reading of phy register content at given address via mdio interface
467 *
468 * - high byte of register address defines page
469 * - low byte of register address defines the address within selected page
470 * - to speed up, we store the last used page and only swap page if needed
471 *
472 */
phy_mc_vsc8541_read(const struct device * dev,uint16_t reg_addr,uint32_t * data)473 static int phy_mc_vsc8541_read(const struct device *dev, uint16_t reg_addr, uint32_t *data)
474 {
475 const struct mc_vsc8541_config *cfg = dev->config;
476 struct mc_vsc8541_data *dev_data = dev->data;
477 int ret;
478
479 *data = 0U;
480
481 /* decode page */
482 uint32_t page = reg_addr >> 8;
483
484 /* mask out lower byte */
485 reg_addr &= 0x00ff;
486
487 /* select page, given by register upper byte */
488 if (dev_data->active_page != page) {
489 ret = mdio_write(cfg->mdio_dev, cfg->addr, PHY_REG_PAGE_SELECTOR, (uint16_t)page);
490 if (ret) {
491 return ret;
492 }
493 dev_data->active_page = (int)page;
494 }
495
496 /* select register, given by register lower byte */
497 ret = mdio_read(cfg->mdio_dev, cfg->addr, reg_addr, (uint16_t *)data);
498 if (ret) {
499 return ret;
500 }
501
502 return 0;
503 }
504
505 /**
506 * @brief Writing of new value to phy register at given address via mdio interface
507 *
508 * - high byte of register address defines page
509 * - low byte of register address defines the address within selected page
510 * - to speed up, we store the last used page and only swap page if needed
511 *
512 */
phy_mc_vsc8541_write(const struct device * dev,uint16_t reg_addr,uint32_t data)513 static int phy_mc_vsc8541_write(const struct device *dev, uint16_t reg_addr, uint32_t data)
514 {
515 const struct mc_vsc8541_config *cfg = dev->config;
516 struct mc_vsc8541_data *dev_data = dev->data;
517 int ret;
518
519 /* decode page */
520 uint32_t page = reg_addr >> 8;
521
522 /* mask out lower byte */
523 reg_addr &= 0x00ff;
524
525 /* select page, given by register upper byte */
526 if (dev_data->active_page != page) {
527 ret = mdio_write(cfg->mdio_dev, cfg->addr, PHY_REG_PAGE_SELECTOR, (uint16_t)page);
528 if (ret) {
529 return ret;
530 }
531 dev_data->active_page = (int)page;
532 }
533
534 /* write register, given by lower byte */
535 ret = mdio_write(cfg->mdio_dev, cfg->addr, reg_addr, (uint16_t)data);
536 if (ret) {
537 return ret;
538 }
539
540 return 0;
541 }
542
543 static DEVICE_API(ethphy, mc_vsc8541_phy_api) = {
544 .get_link = phy_mc_vsc8541_get_link,
545 .cfg_link = phy_mc_vsc8541_cfg_link,
546 .link_cb_set = phy_mc_vsc8541_link_cb_set,
547 .read = phy_mc_vsc8541_read,
548 .write = phy_mc_vsc8541_write,
549 };
550
551 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
552 #define RESET_GPIO(n) .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}),
553 #else
554 #define RESET_GPIO(n)
555 #endif /* reset gpio */
556
557 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios)
558 #define INTERRUPT_GPIO(n) .interrupt_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}),
559 #else
560 #define INTERRUPT_GPIO(n)
561 #endif /* interrupt gpio */
562
563 #define MICROCHIP_VSC8541_INIT(n) \
564 static const struct mc_vsc8541_config mc_vsc8541_##n##_config = { \
565 .addr = DT_INST_REG_ADDR(n), \
566 .mdio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)), \
567 .microchip_interface_type = DT_INST_ENUM_IDX(n, microchip_interface_type), \
568 RESET_GPIO(n) INTERRUPT_GPIO(n)}; \
569 \
570 static struct mc_vsc8541_data mc_vsc8541_##n##_data; \
571 \
572 DEVICE_DT_INST_DEFINE(n, &phy_mc_vsc8541_init, NULL, &mc_vsc8541_##n##_data, \
573 &mc_vsc8541_##n##_config, POST_KERNEL, CONFIG_PHY_INIT_PRIORITY, \
574 &mc_vsc8541_phy_api);
575
576 DT_INST_FOREACH_STATUS_OKAY(MICROCHIP_VSC8541_INIT)
577