1 // SPDX-License-Identifier: GPL-2.0+
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
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,
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,
92 static const u16 lan865x_revb_fixup_cfg_regs
[2] = {
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
)
124 ret
= phy_write_mmd(phydev
, MDIO_MMD_VEND2
, LAN865X_REG_CFGPARAM_ADDR
,
129 ret
= phy_write_mmd(phydev
, MDIO_MMD_VEND2
, LAN865X_REG_CFGPARAM_CTRL
,
130 LAN865X_CFGPARAM_READ_ENABLE
);
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};
145 for (int i
= 0; i
< ARRAY_SIZE(fixup_regs
); i
++) {
146 ret
= lan865x_revb_indirect_read(phydev
, fixup_regs
[i
]);
150 /* 5-bit signed value, sign extend */
151 ret
&= GENMASK(4, 0);
153 offsets
[i
] = ret
| 0xE0;
161 static int lan865x_read_cfg_params(struct phy_device
*phydev
,
162 const u16 cfg_regs
[], u16 cfg_params
[],
167 for (int i
= 0; i
< count
; i
++) {
168 ret
= phy_read_mmd(phydev
, MDIO_MMD_VEND2
,
172 cfg_params
[i
] = (u16
)ret
;
178 static int lan865x_write_cfg_params(struct phy_device
*phydev
,
179 const u16 cfg_regs
[], u16 cfg_params
[],
184 for (int i
= 0; i
< count
; i
++) {
185 ret
= phy_write_mmd(phydev
, MDIO_MMD_VEND2
, cfg_regs
[i
],
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
)];
200 ret
= lan865x_read_cfg_params(phydev
, lan865x_revb_fixup_cfg_regs
,
201 cfg_params
, ARRAY_SIZE(cfg_params
));
205 cfg_results
[0] = FIELD_PREP(GENMASK(15, 10), 9 + offsets
[0]) |
206 FIELD_PREP(GENMASK(9, 4), 14 + offsets
[0]) |
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
)];
220 ret
= lan865x_read_cfg_params(phydev
, lan865x_revb_sqi_fixup_cfg_regs
,
221 cfg_params
, ARRAY_SIZE(cfg_params
));
225 cfg_results
[0] = FIELD_PREP(GENMASK(13, 8), 5 + offsets
[0]) |
227 cfg_results
[1] = FIELD_PREP(GENMASK(13, 8), 9 + offsets
[0]) |
229 cfg_results
[2] = FIELD_PREP(GENMASK(13, 8), 17 + 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
)
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
);
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
]);
256 ret
= lan865x_setup_cfgparam(phydev
, offsets
);
262 ret
= lan865x_setup_sqi_cfgparam(phydev
, offsets
);
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
]);
277 static int lan867x_check_reset_complete(struct phy_device
*phydev
)
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
);
288 if (!(err
& LAN867x_RESET_COMPLETE_STS
)) {
290 err
= phy_read_mmd(phydev
, MDIO_MMD_VEND2
, LAN867X_REG_STS2
);
293 if (!(err
& LAN867x_RESET_COMPLETE_STS
)) {
294 phydev_err(phydev
, "PHY reset failed\n");
302 static int lan867x_revc_config_init(struct phy_device
*phydev
)
307 ret
= lan867x_check_reset_complete(phydev
);
311 ret
= lan865x_generate_cfg_offsets(phydev
, offsets
);
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
]);
333 ret
= lan865x_setup_cfgparam(phydev
, offsets
);
339 ret
= lan865x_setup_sqi_cfgparam(phydev
, offsets
);
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
]);
354 static int lan867x_revb1_config_init(struct phy_device
*phydev
)
358 err
= lan867x_check_reset_complete(phydev
);
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
]);
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
)
396 ret
= genphy_c45_plca_set_cfg(phydev
, plca_cfg
);
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
,
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
418 phydev
->duplex
= DUPLEX_HALF
;
419 phydev
->speed
= SPEED_10
;
420 phydev
->autoneg
= AUTONEG_DISABLE
;
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
,
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
,
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");