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, &reg);
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, &reg_sr);
367 	if (ret) {
368 		return ret;
369 	}
370 
371 	ret = phy_mc_vsc8541_read(dev, PHY_REG_PAGE0_BMCR, &reg_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