xref: /linux/drivers/net/phy/microchip_t1s.c (revision fcc79e1714e8c2b8e216dc3149812edd37884eef)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Driver for Microchip 10BASE-T1S PHYs
4  *
5  * Support: Microchip Phys:
6  *  lan8670/1/2 Rev.B1/C1/C2
7  *  lan8650/1 Rev.B0/B1 Internal PHYs
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/phy.h>
13 
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
19 
20 #define LAN867X_REG_STS2 0x0019
21 
22 #define LAN867x_RESET_COMPLETE_STS BIT(11)
23 
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
28 
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
34 
35 #define LAN865X_CFGPARAM_READ_ENABLE BIT(1)
36 
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 ------
51  */
52 
53 static const u32 lan867x_revb1_fixup_registers[12] = {
54 	0x00D0, 0x00D1, 0x0084, 0x0085,
55 	0x008A, 0x0087, 0x0088, 0x008B,
56 	0x0080, 0x00F1, 0x0096, 0x0099,
57 };
58 
59 static const u16 lan867x_revb1_fixup_values[12] = {
60 	0x0002, 0x0000, 0x3380, 0x0006,
61 	0xC000, 0x801C, 0x033F, 0x0404,
62 	0x0600, 0x2400, 0x2000, 0x7F80,
63 };
64 
65 static const u16 lan867x_revb1_fixup_masks[12] = {
66 	0x0E03, 0x0300, 0xFFC0, 0x000F,
67 	0xF800, 0x801C, 0x1FFF, 0xFFFF,
68 	0x0600, 0x7F00, 0x2000, 0xFFFF,
69 };
70 
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)
75  */
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,
82 };
83 
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,
90 };
91 
92 static const u16 lan865x_revb_fixup_cfg_regs[2] = {
93 	0x0084, 0x008A,
94 };
95 
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,
100 };
101 
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,
106 };
107 
108 static const u16 lan865x_revb_sqi_fixup_cfg_regs[3] = {
109 	0x00AD, 0x00AE, 0x00AF,
110 };
111 
112 /* Pulled from AN1760 describing 'indirect read'
113  *
114  * write_register(0x4, 0x00D8, addr)
115  * write_register(0x4, 0x00DA, 0x2)
116  * return (int8)(read_register(0x4, 0x00D9))
117  *
118  * 0x4 refers to memory map selector 4, which maps to MDIO_MMD_VEND2
119  */
120 static int lan865x_revb_indirect_read(struct phy_device *phydev, u16 addr)
121 {
122 	int ret;
123 
124 	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_ADDR,
125 			    addr);
126 	if (ret)
127 		return ret;
128 
129 	ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_CTRL,
130 			    LAN865X_CFGPARAM_READ_ENABLE);
131 	if (ret)
132 		return ret;
133 
134 	return phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN865X_REG_CFGPARAM_DATA);
135 }
136 
137 /* This is pulled straight from AN1760 from 'calculation of offset 1' &
138  * 'calculation of offset 2'
139  */
140 static int lan865x_generate_cfg_offsets(struct phy_device *phydev, s8 offsets[])
141 {
142 	const u16 fixup_regs[2] = {0x0004, 0x0008};
143 	int ret;
144 
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;
149 
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;
156 	}
157 
158 	return 0;
159 }
160 
161 static int lan865x_read_cfg_params(struct phy_device *phydev,
162 				   const u16 cfg_regs[], u16 cfg_params[],
163 				   u8 count)
164 {
165 	int ret;
166 
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;
173 	}
174 
175 	return 0;
176 }
177 
178 static int lan865x_write_cfg_params(struct phy_device *phydev,
179 				    const u16 cfg_regs[], u16 cfg_params[],
180 				    u8 count)
181 {
182 	int ret;
183 
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;
189 	}
190 
191 	return 0;
192 }
193 
194 static int lan865x_setup_cfgparam(struct phy_device *phydev, s8 offsets[])
195 {
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;
199 
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;
204 
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]);
209 
210 	return lan865x_write_cfg_params(phydev, lan865x_revb_fixup_cfg_regs,
211 					cfg_results, ARRAY_SIZE(cfg_results));
212 }
213 
214 static int lan865x_setup_sqi_cfgparam(struct phy_device *phydev, s8 offsets[])
215 {
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;
219 
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;
224 
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]);
231 
232 	return lan865x_write_cfg_params(phydev, lan865x_revb_sqi_fixup_cfg_regs,
233 					cfg_results, ARRAY_SIZE(cfg_results));
234 }
235 
236 static int lan865x_revb_config_init(struct phy_device *phydev)
237 {
238 	s8 offsets[2];
239 	int ret;
240 
241 	/* Reference to AN1760
242 	 * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/SupportingCollateral/AN-LAN8650-1-Configuration-60001760.pdf
243 	 */
244 	ret = lan865x_generate_cfg_offsets(phydev, offsets);
245 	if (ret)
246 		return ret;
247 
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;
254 
255 		if (i == 1) {
256 			ret = lan865x_setup_cfgparam(phydev, offsets);
257 			if (ret)
258 				return ret;
259 		}
260 	}
261 
262 	ret = lan865x_setup_sqi_cfgparam(phydev, offsets);
263 	if (ret)
264 		return ret;
265 
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;
272 	}
273 
274 	return 0;
275 }
276 
277 static int lan867x_check_reset_complete(struct phy_device *phydev)
278 {
279 	int err;
280 
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.
283 	 */
284 	err = phy_read_mmd(phydev, MDIO_MMD_VEND2, LAN867X_REG_STS2);
285 	if (err < 0)
286 		return err;
287 
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;
296 		}
297 	}
298 
299 	return 0;
300 }
301 
302 static int lan867x_revc_config_init(struct phy_device *phydev)
303 {
304 	s8 offsets[2];
305 	int ret;
306 
307 	ret = lan867x_check_reset_complete(phydev);
308 	if (ret)
309 		return ret;
310 
311 	ret = lan865x_generate_cfg_offsets(phydev, offsets);
312 	if (ret)
313 		return ret;
314 
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)
324 	 */
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;
331 
332 		if (i == 1) {
333 			ret = lan865x_setup_cfgparam(phydev, offsets);
334 			if (ret)
335 				return ret;
336 		}
337 	}
338 
339 	ret = lan865x_setup_sqi_cfgparam(phydev, offsets);
340 	if (ret)
341 		return ret;
342 
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;
349 	}
350 
351 	return 0;
352 }
353 
354 static int lan867x_revb1_config_init(struct phy_device *phydev)
355 {
356 	int err;
357 
358 	err = lan867x_check_reset_complete(phydev);
359 	if (err)
360 		return err;
361 
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.
367 	 */
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;
375 	}
376 
377 	return 0;
378 }
379 
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.
387  *
388  * AN1760: https://www.microchip.com/en-us/application-notes/an1760
389  * AN1699: https://www.microchip.com/en-us/application-notes/an1699
390  */
391 static int lan86xx_plca_set_cfg(struct phy_device *phydev,
392 				const struct phy_plca_cfg *plca_cfg)
393 {
394 	int ret;
395 
396 	ret = genphy_c45_plca_set_cfg(phydev, plca_cfg);
397 	if (ret)
398 		return ret;
399 
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);
405 
406 	return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LAN86XX_REG_COL_DET_CTRL0,
407 			      COL_DET_CTRL0_ENABLE_BIT_MASK, COL_DET_ENABLE);
408 }
409 
410 static int lan86xx_read_status(struct phy_device *phydev)
411 {
412 	/* The phy has some limitations, namely:
413 	 *  - always reports link up
414 	 *  - only supports 10MBit half duplex
415 	 *  - does not support auto negotiate
416 	 */
417 	phydev->link = 1;
418 	phydev->duplex = DUPLEX_HALF;
419 	phydev->speed = SPEED_10;
420 	phydev->autoneg = AUTONEG_DISABLE;
421 
422 	return 0;
423 }
424 
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
434  */
435 static int lan865x_phy_read_mmd(struct phy_device *phydev, int devnum,
436 				u16 regnum)
437 {
438 	struct mii_bus *bus = phydev->mdio.bus;
439 	int addr = phydev->mdio.addr;
440 
441 	return __mdiobus_c45_read(bus, addr, devnum, regnum);
442 }
443 
444 static int lan865x_phy_write_mmd(struct phy_device *phydev, int devnum,
445 				 u16 regnum, u16 val)
446 {
447 	struct mii_bus *bus = phydev->mdio.bus;
448 	int addr = phydev->mdio.addr;
449 
450 	return __mdiobus_c45_write(bus, addr, devnum, regnum, val);
451 }
452 
453 static struct phy_driver microchip_t1s_driver[] = {
454 	{
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,
463 	},
464 	{
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,
473 	},
474 	{
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,
483 	},
484 	{
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,
495 	},
496 };
497 
498 module_phy_driver(microchip_t1s_driver);
499 
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) },
505 	{ }
506 };
507 
508 MODULE_DEVICE_TABLE(mdio, tbl);
509 
510 MODULE_DESCRIPTION("Microchip 10BASE-T1S PHYs driver");
511 MODULE_AUTHOR("Ramón Nordin Rodriguez");
512 MODULE_LICENSE("GPL");
513