1 /*
2 * Copyright (c) 2024 Microchip Technology Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT microchip_t1s_phy
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/net/phy.h>
11 #include <zephyr/net/mii.h>
12 #include <zephyr/net/mdio.h>
13 #include <zephyr/drivers/mdio.h>
14
15 #define LOG_MODULE_NAME phy_mc_t1s
16 #define LOG_LEVEL CONFIG_PHY_LOG_LEVEL
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
19
20 /* Support: Microchip Phys:
21 * lan8650/1 Rev.B0/B1 Internal PHYs
22 * lan8670/1/2 Rev.C1/C2 PHYs
23 */
24 /* Both Rev.B0 and B1 clause 22 PHYID's are same due to B1 chip limitation */
25 #define PHY_ID_LAN865X_REVB 0x0007C1B3
26 #define PHY_ID_LAN867X_REVC1 0x0007C164
27 #define PHY_ID_LAN867X_REVC2 0x0007C165
28
29 /* Configuration param registers */
30 #define LAN865X_REG_CFGPARAM_ADDR 0x00D8
31 #define LAN865X_REG_CFGPARAM_DATA 0x00D9
32 #define LAN865X_REG_CFGPARAM_CTRL 0x00DA
33 #define LAN865X_REG_STS2 0x0019
34 #define LAN865X_CFGPARAM_READ_ENABLE BIT(1)
35
36 /* Collision detection enable/disable registers */
37 #define LAN86XX_DISABLE_COL_DET 0x0000
38 #define LAN86XX_ENABLE_COL_DET 0x8000
39 #define LAN86XX_COL_DET_MASK 0x8000
40 #define LAN86XX_REG_COL_DET_CTRL0 0x0087
41
42 /* Structure holding configuration register address and value */
43 typedef struct {
44 uint32_t address;
45 uint16_t value;
46 } lan865x_config;
47
48 /* LAN865x Rev.B0/B1 configuration parameters from AN1760
49 * As per the Configuration Application Note AN1760 published in the below link,
50 * https://www.microchip.com/en-us/application-notes/an1760
51 * Revision F (DS60001760G - June 2024)
52 * Addresses 0x0084, 0x008A, 0x00AD, 0x00AE and 0x00AF will be updated with cfgparam1, cfgparam2,
53 * cfgparam3, cfgparam4 and cfgparam5 respectively.
54 *
55 * LAN867x Rev.C1/C2 configuration settings described in AN1699 are equal to
56 * the first 11 configuration settings and all the sqi fixup settings from
57 * LAN865x Rev.B0/B1. So the same fixup registers and values from LAN865x
58 * Rev.B0/B1 are used for LAN867x Rev.C1/C2 to avoid duplication.
59 * Refer the below link for the AN1699,
60 * https://www.microchip.com/en-us/application-notes/an1699
61 * Revision E (DS60001699F - June 2024)
62 */
63 static lan865x_config lan865x_revb_config[] = {
64 {.address = 0x00D0, .value = 0x3F31}, {.address = 0x00E0, .value = 0xC000},
65 {.address = 0x0084, .value = 0x0000}, {.address = 0x008A, .value = 0x0000},
66 {.address = 0x00E9, .value = 0x9E50}, {.address = 0x00F5, .value = 0x1CF8},
67 {.address = 0x00F4, .value = 0xC020}, {.address = 0x00F8, .value = 0xB900},
68 {.address = 0x00F9, .value = 0x4E53}, {.address = 0x0081, .value = 0x0080},
69 {.address = 0x0091, .value = 0x9660}, {.address = 0x0043, .value = 0x00FF},
70 {.address = 0x0044, .value = 0xFFFF}, {.address = 0x0045, .value = 0x0000},
71 {.address = 0x0053, .value = 0x00FF}, {.address = 0x0054, .value = 0xFFFF},
72 {.address = 0x0055, .value = 0x0000}, {.address = 0x0040, .value = 0x0002},
73 {.address = 0x0050, .value = 0x0002}, {.address = 0x00AD, .value = 0x0000},
74 {.address = 0x00AE, .value = 0x0000}, {.address = 0x00AF, .value = 0x0000},
75 {.address = 0x00B0, .value = 0x0103}, {.address = 0x00B1, .value = 0x0910},
76 {.address = 0x00B2, .value = 0x1D26}, {.address = 0x00B3, .value = 0x002A},
77 {.address = 0x00B4, .value = 0x0103}, {.address = 0x00B5, .value = 0x070D},
78 {.address = 0x00B6, .value = 0x1720}, {.address = 0x00B7, .value = 0x0027},
79 {.address = 0x00B8, .value = 0x0509}, {.address = 0x00B9, .value = 0x0E13},
80 {.address = 0x00BA, .value = 0x1C25}, {.address = 0x00BB, .value = 0x002B},
81 };
82
83 struct mc_t1s_plca_config {
84 bool enable;
85 uint8_t node_id;
86 uint8_t node_count;
87 uint8_t burst_count;
88 uint8_t burst_timer;
89 uint8_t to_timer;
90 };
91
92 struct mc_t1s_config {
93 uint8_t phy_addr;
94 const struct device *mdio;
95 struct mc_t1s_plca_config *plca;
96 };
97
98 struct mc_t1s_data {
99 const struct device *dev;
100 struct phy_link_state state;
101 phy_callback_t cb;
102 void *cb_data;
103 struct k_work_delayable phy_monitor_work;
104 };
105
phy_mc_t1s_read(const struct device * dev,uint16_t reg,uint32_t * data)106 static int phy_mc_t1s_read(const struct device *dev, uint16_t reg, uint32_t *data)
107 {
108 const struct mc_t1s_config *cfg = dev->config;
109
110 /* Make sure excessive bits 16-31 are reset */
111 *data = 0U;
112
113 return mdio_read(cfg->mdio, cfg->phy_addr, reg, (uint16_t *)data);
114 }
115
phy_mc_t1s_write(const struct device * dev,uint16_t reg,uint32_t data)116 static int phy_mc_t1s_write(const struct device *dev, uint16_t reg, uint32_t data)
117 {
118 const struct mc_t1s_config *cfg = dev->config;
119
120 return mdio_write(cfg->mdio, cfg->phy_addr, reg, (uint16_t)data);
121 }
122
mdio_setup_c45_indirect_access(const struct device * dev,uint16_t devad,uint16_t reg)123 static int mdio_setup_c45_indirect_access(const struct device *dev, uint16_t devad, uint16_t reg)
124 {
125 const struct mc_t1s_config *cfg = dev->config;
126 int ret;
127
128 ret = mdio_write(cfg->mdio, cfg->phy_addr, MII_MMD_ACR, devad);
129 if (ret) {
130 return ret;
131 }
132
133 ret = mdio_write(cfg->mdio, cfg->phy_addr, MII_MMD_AADR, reg);
134 if (ret < 0) {
135 return ret;
136 }
137
138 return mdio_write(cfg->mdio, cfg->phy_addr, MII_MMD_ACR, devad | BIT(14));
139 }
140
phy_mc_t1s_c45_read(const struct device * dev,uint8_t devad,uint16_t reg,uint16_t * val)141 static int phy_mc_t1s_c45_read(const struct device *dev, uint8_t devad, uint16_t reg, uint16_t *val)
142 {
143 const struct mc_t1s_config *cfg = dev->config;
144 int ret;
145
146 ret = mdio_read_c45(cfg->mdio, cfg->phy_addr, devad, reg, val);
147 /* @retval -ENOSYS if read using Clause 45 direct access is not supported */
148 if (ret == -ENOSYS) {
149 /* Read C45 registers using C22 indirect access registers */
150 ret = mdio_setup_c45_indirect_access(dev, devad, reg);
151 if (ret) {
152 return ret;
153 }
154
155 return mdio_read(cfg->mdio, cfg->phy_addr, MII_MMD_AADR, val);
156 }
157
158 return ret;
159 }
160
phy_mc_t1s_c45_write(const struct device * dev,uint8_t devad,uint16_t reg,uint16_t val)161 static int phy_mc_t1s_c45_write(const struct device *dev, uint8_t devad, uint16_t reg, uint16_t val)
162 {
163 const struct mc_t1s_config *cfg = dev->config;
164 int ret;
165
166 ret = mdio_write_c45(cfg->mdio, cfg->phy_addr, devad, reg, val);
167 /* @retval -ENOSYS if write using Clause 45 direct access is not supported */
168 if (ret == -ENOSYS) {
169 /* Write C45 registers using C22 indirect access registers */
170 ret = mdio_setup_c45_indirect_access(dev, devad, reg);
171 if (ret) {
172 return ret;
173 }
174
175 return mdio_write(cfg->mdio, cfg->phy_addr, MII_MMD_AADR, val);
176 }
177
178 return ret;
179 }
180
phy_mc_t1s_get_link(const struct device * dev,struct phy_link_state * state)181 static int phy_mc_t1s_get_link(const struct device *dev, struct phy_link_state *state)
182 {
183 const struct mc_t1s_config *cfg = dev->config;
184 struct mc_t1s_data *data = dev->data;
185 struct phy_link_state old_state = data->state;
186 uint32_t value = 0;
187 int ret;
188
189 ret = phy_mc_t1s_read(dev, MII_BMSR, &value);
190 if (ret) {
191 LOG_ERR("Failed MII_BMSR register read: %d\n", ret);
192 return ret;
193 }
194
195 state->is_up = value & MII_BMSR_LINK_STATUS;
196 state->speed = LINK_HALF_10BASE_T;
197
198 if (memcmp(&old_state, state, sizeof(struct phy_link_state)) != 0) {
199 if (state->is_up) {
200 LOG_INF("PHY (%d) Link speed 10 Mbps, half duplex\n", cfg->phy_addr);
201 }
202 }
203
204 return 0;
205 }
206
phy_mc_t1s_link_cb_set(const struct device * dev,phy_callback_t cb,void * user_data)207 static int phy_mc_t1s_link_cb_set(const struct device *dev, phy_callback_t cb, void *user_data)
208 {
209 struct mc_t1s_data *data = dev->data;
210
211 data->cb = cb;
212 data->cb_data = user_data;
213
214 if (data->cb) {
215 data->cb(dev, &data->state, data->cb_data);
216 }
217
218 return 0;
219 }
220
phy_monitor_work_handler(struct k_work * work)221 static void phy_monitor_work_handler(struct k_work *work)
222 {
223 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
224 struct mc_t1s_data *const data = CONTAINER_OF(dwork, struct mc_t1s_data, phy_monitor_work);
225 const struct device *dev = data->dev;
226
227 if (!data->cb) {
228 return;
229 }
230
231 phy_mc_t1s_get_link(dev, &data->state);
232
233 data->cb(dev, &data->state, data->cb_data);
234
235 /* Submit delayed work */
236 k_work_reschedule(&data->phy_monitor_work, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));
237 }
238
239 /* Pulled from AN1760 describing 'indirect read'
240 *
241 * write_register(0x4, 0x00D8, addr)
242 * write_register(0x4, 0x00DA, 0x2)
243 * return (int8)(read_register(0x4, 0x00D9))
244 *
245 * 0x4 refers to memory map selector 4, which maps to MDIO_MMD_VENDOR_SPECIFIC2
246 */
lan865x_indirect_read(const struct device * dev,uint16_t addr,uint16_t * value)247 static int lan865x_indirect_read(const struct device *dev, uint16_t addr, uint16_t *value)
248 {
249 int ret;
250
251 ret = phy_mc_t1s_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC2, LAN865X_REG_CFGPARAM_ADDR, addr);
252 if (ret) {
253 return ret;
254 }
255
256 ret = phy_mc_t1s_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC2, LAN865X_REG_CFGPARAM_CTRL,
257 LAN865X_CFGPARAM_READ_ENABLE);
258 if (ret) {
259 return ret;
260 }
261
262 return phy_mc_t1s_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC2, LAN865X_REG_CFGPARAM_DATA,
263 value);
264 }
265
lan865x_calculate_offset(const struct device * dev,uint16_t address,int8_t * offset)266 static int lan865x_calculate_offset(const struct device *dev, uint16_t address, int8_t *offset)
267 {
268 uint16_t value;
269 int ret;
270
271 ret = lan865x_indirect_read(dev, address, &value);
272 if (ret) {
273 return ret;
274 }
275
276 /* 5-bit signed value, sign extend */
277 value &= GENMASK(4, 0);
278 if (value & BIT(4)) {
279 *offset = (int8_t)((uint8_t)value - 0x20);
280 } else {
281 *offset = (int8_t)value;
282 }
283
284 return 0;
285 }
286
lan865x_update_cfgparam(uint32_t address,uint16_t cfgparam)287 static void lan865x_update_cfgparam(uint32_t address, uint16_t cfgparam)
288 {
289 for (uint8_t i = 0; i < ARRAY_SIZE(lan865x_revb_config); i++) {
290 if (lan865x_revb_config[i].address == address) {
291 lan865x_revb_config[i].value = cfgparam;
292 }
293 }
294 }
295
lan865x_calculate_update_cfgparams(const struct device * dev)296 static int lan865x_calculate_update_cfgparams(const struct device *dev)
297 {
298 uint16_t cfgparam;
299 int8_t offset1;
300 int8_t offset2;
301 int ret;
302
303 /* Calculate offset1 */
304 ret = lan865x_calculate_offset(dev, 0x04, &offset1);
305 if (ret) {
306 return ret;
307 }
308
309 /* Calculate offset2 */
310 ret = lan865x_calculate_offset(dev, 0x08, &offset2);
311 if (ret) {
312 return ret;
313 }
314
315 /* Calculate & update cfgparam1 for configuration */
316 cfgparam = (uint16_t)(((9 + offset1) & 0x3F) << 10) |
317 (uint16_t)(((14 + offset1) & 0x3F) << 4) | 0x03;
318 lan865x_update_cfgparam(0x0084, cfgparam);
319
320 /* Calculate & update cfgparam2 for configuration */
321 cfgparam = (uint16_t)(((40 + offset2) & 0x3F) << 10);
322 lan865x_update_cfgparam(0x008A, cfgparam);
323
324 /* Calculate & update cfgparam3 for configuration */
325 cfgparam = (uint16_t)(((5 + offset1) & 0x3F) << 8) | (uint16_t)((9 + offset1) & 0x3F);
326 lan865x_update_cfgparam(0x00AD, cfgparam);
327
328 /* Calculate & update cfgparam4 for configuration */
329 cfgparam = (uint16_t)(((9 + offset1) & 0x3F) << 8) | (uint16_t)((14 + offset1) & 0x3F);
330 lan865x_update_cfgparam(0x00AE, cfgparam);
331
332 /* Calculate & update cfgparam5 for configuration */
333 cfgparam = (uint16_t)(((17 + offset1) & 0x3F) << 8) | (uint16_t)((22 + offset1) & 0x3F);
334 lan865x_update_cfgparam(0x00AF, cfgparam);
335
336 return 0;
337 }
338
phy_mc_lan865x_revb_config_init(const struct device * dev)339 static int phy_mc_lan865x_revb_config_init(const struct device *dev)
340 {
341 int ret;
342
343 ret = lan865x_calculate_update_cfgparams(dev);
344 if (ret) {
345 return ret;
346 }
347
348 /* Configure lan865x initial settings */
349 for (int i = 0; i < ARRAY_SIZE(lan865x_revb_config); i++) {
350 ret = phy_mc_t1s_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC2,
351 lan865x_revb_config[i].address,
352 lan865x_revb_config[i].value);
353 if (ret) {
354 return ret;
355 }
356 }
357
358 return 0;
359 }
360
361 /* LAN867x Rev.C1/C2 configuration settings are equal to the first 11 configuration settings and all
362 * the sqi fixup settings from LAN865x Rev.B0/B1. So the same fixup registers and values from
363 * LAN865x Rev.B0/B1 are used for LAN867x Rev.C1/C2 to avoid duplication.
364 * Refer the below links for the comparison.
365 * https://www.microchip.com/en-us/application-notes/an1760
366 * Revision F (DS60001760G - June 2024)
367 * https://www.microchip.com/en-us/application-notes/an1699
368 * Revision E (DS60001699F - June 2024)
369 */
phy_mc_lan867x_revc_config_init(const struct device * dev)370 static int phy_mc_lan867x_revc_config_init(const struct device *dev)
371 {
372 int ret;
373
374 ret = lan865x_calculate_update_cfgparams(dev);
375 if (ret) {
376 return ret;
377 }
378
379 for (int i = 0; i < ARRAY_SIZE(lan865x_revb_config); i++) {
380 ret = phy_mc_t1s_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC2,
381 lan865x_revb_config[i].address,
382 lan865x_revb_config[i].value);
383 if (ret) {
384 return ret;
385 }
386
387 /* LAN867x Rev.C1/C2 configuration settings are equal to the first 11 configuration
388 * settings and all the sqi fixup settings from LAN865x Rev.B0/B1. So the 8
389 * inbetween configuration settings are skipped.
390 */
391 if (i == 10) {
392 i += 8;
393 }
394 }
395
396 return 0;
397 }
398
lan86xx_config_collision_detection(const struct device * dev,bool plca_enable)399 static int lan86xx_config_collision_detection(const struct device *dev, bool plca_enable)
400 {
401 uint16_t val;
402 uint16_t new;
403 int ret;
404
405 ret = phy_mc_t1s_c45_read(dev, MDIO_MMD_VENDOR_SPECIFIC2, LAN86XX_REG_COL_DET_CTRL0, &val);
406 if (ret) {
407 return ret;
408 }
409
410 if (plca_enable) {
411 new = (val & ~LAN86XX_COL_DET_MASK) | LAN86XX_DISABLE_COL_DET;
412 } else {
413 new = (val & ~LAN86XX_COL_DET_MASK) | LAN86XX_ENABLE_COL_DET;
414 }
415
416 if (new == val) {
417 return 0;
418 }
419
420 return phy_mc_t1s_c45_write(dev, MDIO_MMD_VENDOR_SPECIFIC2, LAN86XX_REG_COL_DET_CTRL0, new);
421 }
422
phy_mc_t1s_cfg_link(const struct device * dev,enum phy_link_speed speeds)423 static int phy_mc_t1s_cfg_link(const struct device *dev, enum phy_link_speed speeds)
424 {
425 ARG_UNUSED(dev);
426
427 if (speeds & LINK_HALF_10BASE_T) {
428 return 0;
429 }
430
431 return -ENOTSUP;
432 }
433
phy_mc_t1s_id(const struct device * dev,uint32_t * phy_id)434 static int phy_mc_t1s_id(const struct device *dev, uint32_t *phy_id)
435 {
436 uint32_t value;
437 int ret;
438
439 ret = phy_mc_t1s_read(dev, MII_PHYID1R, &value);
440 if (ret) {
441 LOG_ERR("Failed MII_PHYID1R register read: %d\n", ret);
442 return ret;
443 }
444
445 *phy_id = value << 16;
446
447 ret = phy_mc_t1s_read(dev, MII_PHYID2R, &value);
448 if (ret) {
449 LOG_ERR("Failed MII_PHYID2R register read: %d\n", ret);
450 return ret;
451 }
452
453 *phy_id |= value;
454
455 return 0;
456 }
457
phy_mc_t1s_set_plca_cfg(const struct device * dev,struct phy_plca_cfg * plca_cfg)458 static int phy_mc_t1s_set_plca_cfg(const struct device *dev, struct phy_plca_cfg *plca_cfg)
459 {
460 int ret;
461
462 ret = genphy_set_plca_cfg(dev, plca_cfg);
463 if (ret) {
464 return ret;
465 }
466
467 return lan86xx_config_collision_detection(dev, plca_cfg->enable);
468 }
469
phy_mc_t1s_set_dt_plca(const struct device * dev)470 static int phy_mc_t1s_set_dt_plca(const struct device *dev)
471 {
472 const struct mc_t1s_config *cfg = dev->config;
473 struct phy_plca_cfg plca_cfg;
474
475 if (!cfg->plca->enable) {
476 return 0;
477 }
478
479 plca_cfg.enable = cfg->plca->enable;
480 plca_cfg.node_id = cfg->plca->node_id;
481 plca_cfg.node_count = cfg->plca->node_count;
482 plca_cfg.burst_count = cfg->plca->burst_count;
483 plca_cfg.burst_timer = cfg->plca->burst_timer;
484 plca_cfg.to_timer = cfg->plca->to_timer;
485
486 return phy_mc_t1s_set_plca_cfg(dev, &plca_cfg);
487 }
488
phy_mc_t1s_init(const struct device * dev)489 static int phy_mc_t1s_init(const struct device *dev)
490 {
491 struct mc_t1s_data *data = dev->data;
492 uint32_t phy_id;
493 int ret;
494
495 data->dev = dev;
496
497 ret = phy_mc_t1s_id(dev, &phy_id);
498 if (ret) {
499 return ret;
500 }
501
502 switch (phy_id) {
503 case PHY_ID_LAN867X_REVC1:
504 case PHY_ID_LAN867X_REVC2:
505 ret = phy_mc_lan867x_revc_config_init(dev);
506 if (ret) {
507 LOG_ERR("PHY initial configuration error: %d\n", ret);
508 return ret;
509 }
510 break;
511 case PHY_ID_LAN865X_REVB:
512 ret = phy_mc_lan865x_revb_config_init(dev);
513 if (ret) {
514 LOG_ERR("PHY initial configuration error: %d\n", ret);
515 return ret;
516 }
517 break;
518 default:
519 LOG_ERR("Unsupported PHY ID: %x\n", phy_id);
520 return -ENODEV;
521 }
522
523 ret = phy_mc_t1s_set_dt_plca(dev);
524 if (ret) {
525 return ret;
526 }
527
528 k_work_init_delayable(&data->phy_monitor_work, phy_monitor_work_handler);
529 phy_monitor_work_handler(&data->phy_monitor_work.work);
530
531 return 0;
532 }
533
534 static DEVICE_API(ethphy, mc_t1s_phy_api) = {
535 .get_link = phy_mc_t1s_get_link,
536 .cfg_link = phy_mc_t1s_cfg_link,
537 .link_cb_set = phy_mc_t1s_link_cb_set,
538 .set_plca_cfg = phy_mc_t1s_set_plca_cfg,
539 .get_plca_cfg = genphy_get_plca_cfg,
540 .get_plca_sts = genphy_get_plca_sts,
541 .read = phy_mc_t1s_read,
542 .write = phy_mc_t1s_write,
543 .read_c45 = phy_mc_t1s_c45_read,
544 .write_c45 = phy_mc_t1s_c45_write,
545 };
546
547 #define MICROCHIP_T1S_PHY_INIT(n) \
548 static struct mc_t1s_plca_config mc_t1s_plca_##n##_config = { \
549 .enable = DT_INST_PROP(n, plca_enable), \
550 .node_id = DT_INST_PROP(n, plca_node_id), \
551 .node_count = DT_INST_PROP(n, plca_node_count), \
552 .burst_count = DT_INST_PROP(n, plca_burst_count), \
553 .burst_timer = DT_INST_PROP(n, plca_burst_timer), \
554 .to_timer = DT_INST_PROP(n, plca_to_timer), \
555 }; \
556 \
557 static const struct mc_t1s_config mc_t1s_##n##_config = { \
558 .phy_addr = DT_INST_REG_ADDR(n), \
559 .mdio = DEVICE_DT_GET(DT_INST_PARENT(n)), \
560 .plca = &mc_t1s_plca_##n##_config, \
561 }; \
562 \
563 static struct mc_t1s_data mc_t1s_##n##_data; \
564 \
565 DEVICE_DT_INST_DEFINE(n, &phy_mc_t1s_init, NULL, &mc_t1s_##n##_data, &mc_t1s_##n##_config, \
566 POST_KERNEL, CONFIG_PHY_MICROCHIP_T1S_INIT_PRIORITY, &mc_t1s_phy_api);
567
568 DT_INST_FOREACH_STATUS_OKAY(MICROCHIP_T1S_PHY_INIT);
569