Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / net / phy / microchip_t1s.c
blob75d291154b4cdb34cadfcccb801b47347a386be8
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Driver for Microchip 10BASE-T1S PHYs
5 * Support: Microchip Phys:
6 * lan8670/1/2 Rev.B1/C1/C2
7 * lan8650/1 Rev.B0/B1 Internal PHYs
8 */
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/phy.h>
14 #define PHY_ID_LAN867X_REVB1 0x0007C162
15 #define PHY_ID_LAN867X_REVC1 0x0007C164
16 #define PHY_ID_LAN867X_REVC2 0x0007C165
17 /* Both Rev.B0 and B1 clause 22 PHYID's are same due to B1 chip limitation */
18 #define PHY_ID_LAN865X_REVB 0x0007C1B3
20 #define LAN867X_REG_STS2 0x0019
22 #define LAN867x_RESET_COMPLETE_STS BIT(11)
24 #define LAN865X_REG_CFGPARAM_ADDR 0x00D8
25 #define LAN865X_REG_CFGPARAM_DATA 0x00D9
26 #define LAN865X_REG_CFGPARAM_CTRL 0x00DA
27 #define LAN865X_REG_STS2 0x0019
29 /* Collision Detector Control 0 Register */
30 #define LAN86XX_REG_COL_DET_CTRL0 0x0087
31 #define COL_DET_CTRL0_ENABLE_BIT_MASK BIT(15)
32 #define COL_DET_ENABLE BIT(15)
33 #define COL_DET_DISABLE 0x0000
35 #define LAN865X_CFGPARAM_READ_ENABLE BIT(1)
37 /* The arrays below are pulled from the following table from AN1699
38 * Access MMD Address Value Mask
39 * RMW 0x1F 0x00D0 0x0002 0x0E03
40 * RMW 0x1F 0x00D1 0x0000 0x0300
41 * RMW 0x1F 0x0084 0x3380 0xFFC0
42 * RMW 0x1F 0x0085 0x0006 0x000F
43 * RMW 0x1F 0x008A 0xC000 0xF800
44 * RMW 0x1F 0x0087 0x801C 0x801C
45 * RMW 0x1F 0x0088 0x033F 0x1FFF
46 * W 0x1F 0x008B 0x0404 ------
47 * RMW 0x1F 0x0080 0x0600 0x0600
48 * RMW 0x1F 0x00F1 0x2400 0x7F00
49 * RMW 0x1F 0x0096 0x2000 0x2000
50 * W 0x1F 0x0099 0x7F80 ------
53 static const u32 lan867x_revb1_fixup_registers[12] = {
54 0x00D0, 0x00D1, 0x0084, 0x0085,
55 0x008A, 0x0087, 0x0088, 0x008B,
56 0x0080, 0x00F1, 0x0096, 0x0099,
59 static const u16 lan867x_revb1_fixup_values[12] = {
60 0x0002, 0x0000, 0x3380, 0x0006,
61 0xC000, 0x801C, 0x033F, 0x0404,
62 0x0600, 0x2400, 0x2000, 0x7F80,
65 static const u16 lan867x_revb1_fixup_masks[12] = {
66 0x0E03, 0x0300, 0xFFC0, 0x000F,
67 0xF800, 0x801C, 0x1FFF, 0xFFFF,
68 0x0600, 0x7F00, 0x2000, 0xFFFF,
71 /* LAN865x Rev.B0/B1 configuration parameters from AN1760
72 * As per the Configuration Application Note AN1760 published in the below link,
73 * https://www.microchip.com/en-us/application-notes/an1760
74 * Revision F (DS60001760G - June 2024)
76 static const u32 lan865x_revb_fixup_registers[17] = {
77 0x00D0, 0x00E0, 0x00E9, 0x00F5,
78 0x00F4, 0x00F8, 0x00F9, 0x0081,
79 0x0091, 0x0043, 0x0044, 0x0045,
80 0x0053, 0x0054, 0x0055, 0x0040,
81 0x0050,
84 static const u16 lan865x_revb_fixup_values[17] = {
85 0x3F31, 0xC000, 0x9E50, 0x1CF8,
86 0xC020, 0xB900, 0x4E53, 0x0080,
87 0x9660, 0x00FF, 0xFFFF, 0x0000,
88 0x00FF, 0xFFFF, 0x0000, 0x0002,
89 0x0002,
92 static const u16 lan865x_revb_fixup_cfg_regs[2] = {
93 0x0084, 0x008A,
96 static const u32 lan865x_revb_sqi_fixup_regs[12] = {
97 0x00B0, 0x00B1, 0x00B2, 0x00B3,
98 0x00B4, 0x00B5, 0x00B6, 0x00B7,
99 0x00B8, 0x00B9, 0x00BA, 0x00BB,
102 static const u16 lan865x_revb_sqi_fixup_values[12] = {
103 0x0103, 0x0910, 0x1D26, 0x002A,
104 0x0103, 0x070D, 0x1720, 0x0027,
105 0x0509, 0x0E13, 0x1C25, 0x002B,
108 static const u16 lan865x_revb_sqi_fixup_cfg_regs[3] = {
109 0x00AD, 0x00AE, 0x00AF,
112 /* Pulled from AN1760 describing 'indirect read'
114 * write_register(0x4, 0x00D8, addr)
115 * write_register(0x4, 0x00DA, 0x2)
116 * return (int8)(read_register(0x4, 0x00D9))
118 * 0x4 refers to memory map selector 4, which maps to MDIO_MMD_VEND2
120 static int lan865x_revb_indirect_read(struct phy_device *phydev, u16 addr)
122 int ret;
124 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_ADDR,
125 addr);
126 if (ret)
127 return ret;
129 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_CTRL,
130 LAN865X_CFGPARAM_READ_ENABLE);
131 if (ret)
132 return ret;
134 return phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_DATA);
137 /* This is pulled straight from AN1760 from 'calculation of offset 1' &
138 * 'calculation of offset 2'
140 static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[])
142 const u16 fixup_regs[2] = {0x0004, 0x0008};
143 int ret;
145 for (int i = 0; i < ARRAY_SIZE(fixup_regs); i++) {
146 ret = lan865x_revb_indirect_read(phydev, fixup_regs[i]);
147 if (ret < 0)
148 return ret;
150 /* 5-bit signed value, sign extend */
151 ret &= GENMASK(4, 0);
152 if (ret & BIT(4))
153 offsets[i] = ret | 0xE0;
154 else
155 offsets[i] = ret;
158 return 0;
161 static int lan865x_read_cfg_params(struct phy_device *phydev,
162 const u16 cfg_regs[], u16 cfg_params[],
163 u8 count)
165 int ret;
167 for (int i = 0; i < count; i++) {
168 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
169 cfg_regs[i]);
170 if (ret < 0)
171 return ret;
172 cfg_params[i] = (u16)ret;
175 return 0;
178 static int lan865x_write_cfg_params(struct phy_device *phydev,
179 const u16 cfg_regs[], u16 cfg_params[],
180 u8 count)
182 int ret;
184 for (int i = 0; i < count; i++) {
185 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, cfg_regs[i],
186 cfg_params[i]);
187 if (ret)
188 return ret;
191 return 0;
194 static int lan865x_setup_cfgparam(struct phy_device *phydev, s8 offsets[])
196 u16 cfg_results[ARRAY_SIZE(lan865x_revb_fixup_cfg_regs)];
197 u16 cfg_params[ARRAY_SIZE(lan865x_revb_fixup_cfg_regs)];
198 int ret;
200 ret = lan865x_read_cfg_params(phydev, lan865x_revb_fixup_cfg_regs,
201 cfg_params, ARRAY_SIZE(cfg_params));
202 if (ret)
203 return ret;
205 cfg_results[0] = FIELD_PREP(GENMASK(15, 10), 9 + offsets[0]) |
206 FIELD_PREP(GENMASK(9, 4), 14 + offsets[0]) |
207 0x03;
208 cfg_results[1] = FIELD_PREP(GENMASK(15, 10), 40 + offsets[1]);
210 return lan865x_write_cfg_params(phydev, lan865x_revb_fixup_cfg_regs,
211 cfg_results, ARRAY_SIZE(cfg_results));
214 static int lan865x_setup_sqi_cfgparam(struct phy_device *phydev, s8 offsets[])
216 u16 cfg_results[ARRAY_SIZE(lan865x_revb_sqi_fixup_cfg_regs)];
217 u16 cfg_params[ARRAY_SIZE(lan865x_revb_sqi_fixup_cfg_regs)];
218 int ret;
220 ret = lan865x_read_cfg_params(phydev, lan865x_revb_sqi_fixup_cfg_regs,
221 cfg_params, ARRAY_SIZE(cfg_params));
222 if (ret)
223 return ret;
225 cfg_results[0] = FIELD_PREP(GENMASK(13, 8), 5 + offsets[0]) |
226 (9 + offsets[0]);
227 cfg_results[1] = FIELD_PREP(GENMASK(13, 8), 9 + offsets[0]) |
228 (14 + offsets[0]);
229 cfg_results[2] = FIELD_PREP(GENMASK(13, 8), 17 + offsets[0]) |
230 (22 + offsets[0]);
232 return lan865x_write_cfg_params(phydev, lan865x_revb_sqi_fixup_cfg_regs,
233 cfg_results, ARRAY_SIZE(cfg_results));
236 static int lan865x_revb_config_init(struct phy_device *phydev)
238 s8 offsets[2];
239 int ret;
241 /* Reference to AN1760
242 * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/SupportingCollateral/AN-LAN8650-1-Configuration-60001760.pdf
244 ret = lan865x_generate_cfg_offsets(phydev, offsets);
245 if (ret)
246 return ret;
248 for (int i = 0; i < ARRAY_SIZE(lan865x_revb_fixup_registers); i++) {
249 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
250 lan865x_revb_fixup_registers[i],
251 lan865x_revb_fixup_values[i]);
252 if (ret)
253 return ret;
255 if (i == 1) {
256 ret = lan865x_setup_cfgparam(phydev, offsets);
257 if (ret)
258 return ret;
262 ret = lan865x_setup_sqi_cfgparam(phydev, offsets);
263 if (ret)
264 return ret;
266 for (int i = 0; i < ARRAY_SIZE(lan865x_revb_sqi_fixup_regs); i++) {
267 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
268 lan865x_revb_sqi_fixup_regs[i],
269 lan865x_revb_sqi_fixup_values[i]);
270 if (ret)
271 return ret;
274 return 0;
277 static int lan867x_check_reset_complete(struct phy_device *phydev)
279 int err;
281 /* The chip completes a reset in 3us, we might get here earlier than
282 * that, as an added margin we'll conditionally sleep 5us.
284 err = phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN867X_REG_STS2);
285 if (err < 0)
286 return err;
288 if (!(err & LAN867x_RESET_COMPLETE_STS)) {
289 udelay(5);
290 err = phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN867X_REG_STS2);
291 if (err < 0)
292 return err;
293 if (!(err & LAN867x_RESET_COMPLETE_STS)) {
294 phydev_err(phydev, "PHY reset failed\n");
295 return -ENODEV;
299 return 0;
302 static int lan867x_revc_config_init(struct phy_device *phydev)
304 s8 offsets[2];
305 int ret;
307 ret = lan867x_check_reset_complete(phydev);
308 if (ret)
309 return ret;
311 ret = lan865x_generate_cfg_offsets(phydev, offsets);
312 if (ret)
313 return ret;
315 /* LAN867x Rev.C1/C2 configuration settings are equal to the first 9
316 * configuration settings and all the sqi fixup settings from LAN865x
317 * Rev.B0/B1. So the same fixup registers and values from LAN865x
318 * Rev.B0/B1 are used for LAN867x Rev.C1/C2 to avoid duplication.
319 * Refer the below links for the comparison.
320 * https://www.microchip.com/en-us/application-notes/an1760
321 * Revision F (DS60001760G - June 2024)
322 * https://www.microchip.com/en-us/application-notes/an1699
323 * Revision E (DS60001699F - June 2024)
325 for (int i = 0; i < 9; i++) {
326 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
327 lan865x_revb_fixup_registers[i],
328 lan865x_revb_fixup_values[i]);
329 if (ret)
330 return ret;
332 if (i == 1) {
333 ret = lan865x_setup_cfgparam(phydev, offsets);
334 if (ret)
335 return ret;
339 ret = lan865x_setup_sqi_cfgparam(phydev, offsets);
340 if (ret)
341 return ret;
343 for (int i = 0; i < ARRAY_SIZE(lan865x_revb_sqi_fixup_regs); i++) {
344 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
345 lan865x_revb_sqi_fixup_regs[i],
346 lan865x_revb_sqi_fixup_values[i]);
347 if (ret)
348 return ret;
351 return 0;
354 static int lan867x_revb1_config_init(struct phy_device *phydev)
356 int err;
358 err = lan867x_check_reset_complete(phydev);
359 if (err)
360 return err;
362 /* Reference to AN1699
363 * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/SupportingCollateral/AN-LAN8670-1-2-config-60001699.pdf
364 * AN1699 says Read, Modify, Write, but the Write is not required if the
365 * register already has the required value. So it is safe to use
366 * phy_modify_mmd here.
368 for (int i = 0; i < ARRAY_SIZE(lan867x_revb1_fixup_registers); i++) {
369 err = phy_modify_mmd(phydev, MDIO_MMD_VEND2,
370 lan867x_revb1_fixup_registers[i],
371 lan867x_revb1_fixup_masks[i],
372 lan867x_revb1_fixup_values[i]);
373 if (err)
374 return err;
377 return 0;
380 /* As per LAN8650/1 Rev.B0/B1 AN1760 (Revision F (DS60001760G - June 2024)) and
381 * LAN8670/1/2 Rev.C1/C2 AN1699 (Revision E (DS60001699F - June 2024)), under
382 * normal operation, the device should be operated in PLCA mode. Disabling
383 * collision detection is recommended to allow the device to operate in noisy
384 * environments or when reflections and other inherent transmission line
385 * distortion cause poor signal quality. Collision detection must be re-enabled
386 * if the device is configured to operate in CSMA/CD mode.
388 * AN1760: https://www.microchip.com/en-us/application-notes/an1760
389 * AN1699: https://www.microchip.com/en-us/application-notes/an1699
391 static int lan86xx_plca_set_cfg(struct phy_device *phydev,
392 const struct phy_plca_cfg *plca_cfg)
394 int ret;
396 ret = genphy_c45_plca_set_cfg(phydev, plca_cfg);
397 if (ret)
398 return ret;
400 if (plca_cfg->enabled)
401 return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
402 LAN86XX_REG_COL_DET_CTRL0,
403 COL_DET_CTRL0_ENABLE_BIT_MASK,
404 COL_DET_DISABLE);
406 return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LAN86XX_REG_COL_DET_CTRL0,
407 COL_DET_CTRL0_ENABLE_BIT_MASK, COL_DET_ENABLE);
410 static int lan86xx_read_status(struct phy_device *phydev)
412 /* The phy has some limitations, namely:
413 * - always reports link up
414 * - only supports 10MBit half duplex
415 * - does not support auto negotiate
417 phydev->link = 1;
418 phydev->duplex = DUPLEX_HALF;
419 phydev->speed = SPEED_10;
420 phydev->autoneg = AUTONEG_DISABLE;
422 return 0;
425 /* OPEN Alliance 10BASE-T1x compliance MAC-PHYs will have both C22 and
426 * C45 registers space. If the PHY is discovered via C22 bus protocol it assumes
427 * it uses C22 protocol and always uses C22 registers indirect access to access
428 * C45 registers. This is because, we don't have a clean separation between
429 * C22/C45 register space and C22/C45 MDIO bus protocols. Resulting, PHY C45
430 * registers direct access can't be used which can save multiple SPI bus access.
431 * To support this feature, set .read_mmd/.write_mmd in the PHY driver to call
432 * .read_c45/.write_c45 in the OPEN Alliance framework
433 * drivers/net/ethernet/oa_tc6.c
435 static int lan865x_phy_read_mmd(struct phy_device *phydev, int devnum,
436 u16 regnum)
438 struct mii_bus *bus = phydev->mdio.bus;
439 int addr = phydev->mdio.addr;
441 return __mdiobus_c45_read(bus, addr, devnum, regnum);
444 static int lan865x_phy_write_mmd(struct phy_device *phydev, int devnum,
445 u16 regnum, u16 val)
447 struct mii_bus *bus = phydev->mdio.bus;
448 int addr = phydev->mdio.addr;
450 return __mdiobus_c45_write(bus, addr, devnum, regnum, val);
453 static struct phy_driver microchip_t1s_driver[] = {
455 PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1),
456 .name = "LAN867X Rev.B1",
457 .features = PHY_BASIC_T1S_P2MP_FEATURES,
458 .config_init = lan867x_revb1_config_init,
459 .read_status = lan86xx_read_status,
460 .get_plca_cfg = genphy_c45_plca_get_cfg,
461 .set_plca_cfg = genphy_c45_plca_set_cfg,
462 .get_plca_status = genphy_c45_plca_get_status,
465 PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC1),
466 .name = "LAN867X Rev.C1",
467 .features = PHY_BASIC_T1S_P2MP_FEATURES,
468 .config_init = lan867x_revc_config_init,
469 .read_status = lan86xx_read_status,
470 .get_plca_cfg = genphy_c45_plca_get_cfg,
471 .set_plca_cfg = lan86xx_plca_set_cfg,
472 .get_plca_status = genphy_c45_plca_get_status,
475 PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC2),
476 .name = "LAN867X Rev.C2",
477 .features = PHY_BASIC_T1S_P2MP_FEATURES,
478 .config_init = lan867x_revc_config_init,
479 .read_status = lan86xx_read_status,
480 .get_plca_cfg = genphy_c45_plca_get_cfg,
481 .set_plca_cfg = lan86xx_plca_set_cfg,
482 .get_plca_status = genphy_c45_plca_get_status,
485 PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB),
486 .name = "LAN865X Rev.B0/B1 Internal Phy",
487 .features = PHY_BASIC_T1S_P2MP_FEATURES,
488 .config_init = lan865x_revb_config_init,
489 .read_status = lan86xx_read_status,
490 .read_mmd = lan865x_phy_read_mmd,
491 .write_mmd = lan865x_phy_write_mmd,
492 .get_plca_cfg = genphy_c45_plca_get_cfg,
493 .set_plca_cfg = lan86xx_plca_set_cfg,
494 .get_plca_status = genphy_c45_plca_get_status,
498 module_phy_driver(microchip_t1s_driver);
500 static struct mdio_device_id __maybe_unused tbl[] = {
501 { PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1) },
502 { PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC1) },
503 { PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC2) },
504 { PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB) },
508 MODULE_DEVICE_TABLE(mdio, tbl);
510 MODULE_DESCRIPTION("Microchip 10BASE-T1S PHYs driver");
511 MODULE_AUTHOR("Ramón Nordin Rodriguez");
512 MODULE_LICENSE("GPL");