xref: /linux/drivers/net/phy/microchip_t1.c (revision e0c1b49f5b674cca7b10549c53b3791d0bbc90a8)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Microchip Technology
3 
4 #include <linux/kernel.h>
5 #include <linux/module.h>
6 #include <linux/delay.h>
7 #include <linux/mii.h>
8 #include <linux/phy.h>
9 #include <linux/ethtool.h>
10 #include <linux/ethtool_netlink.h>
11 
12 /* External Register Control Register */
13 #define LAN87XX_EXT_REG_CTL                     (0x14)
14 #define LAN87XX_EXT_REG_CTL_RD_CTL              (0x1000)
15 #define LAN87XX_EXT_REG_CTL_WR_CTL              (0x0800)
16 
17 /* External Register Read Data Register */
18 #define LAN87XX_EXT_REG_RD_DATA                 (0x15)
19 
20 /* External Register Write Data Register */
21 #define LAN87XX_EXT_REG_WR_DATA                 (0x16)
22 
23 /* Interrupt Source Register */
24 #define LAN87XX_INTERRUPT_SOURCE                (0x18)
25 
26 /* Interrupt Mask Register */
27 #define LAN87XX_INTERRUPT_MASK                  (0x19)
28 #define LAN87XX_MASK_LINK_UP                    (0x0004)
29 #define LAN87XX_MASK_LINK_DOWN                  (0x0002)
30 
31 /* phyaccess nested types */
32 #define	PHYACC_ATTR_MODE_READ		0
33 #define	PHYACC_ATTR_MODE_WRITE		1
34 #define	PHYACC_ATTR_MODE_MODIFY		2
35 
36 #define	PHYACC_ATTR_BANK_SMI		0
37 #define	PHYACC_ATTR_BANK_MISC		1
38 #define	PHYACC_ATTR_BANK_PCS		2
39 #define	PHYACC_ATTR_BANK_AFE		3
40 #define	PHYACC_ATTR_BANK_DSP		4
41 #define	PHYACC_ATTR_BANK_MAX		7
42 
43 /* measurement defines */
44 #define	LAN87XX_CABLE_TEST_OK		0
45 #define	LAN87XX_CABLE_TEST_OPEN	1
46 #define	LAN87XX_CABLE_TEST_SAME_SHORT	2
47 
48 #define DRIVER_AUTHOR	"Nisar Sayed <nisar.sayed@microchip.com>"
49 #define DRIVER_DESC	"Microchip LAN87XX T1 PHY driver"
50 
51 struct access_ereg_val {
52 	u8  mode;
53 	u8  bank;
54 	u8  offset;
55 	u16 val;
56 	u16 mask;
57 };
58 
59 static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank,
60 		       u8 offset, u16 val)
61 {
62 	u16 ereg = 0;
63 	int rc = 0;
64 
65 	if (mode > PHYACC_ATTR_MODE_WRITE || bank > PHYACC_ATTR_BANK_MAX)
66 		return -EINVAL;
67 
68 	if (bank == PHYACC_ATTR_BANK_SMI) {
69 		if (mode == PHYACC_ATTR_MODE_WRITE)
70 			rc = phy_write(phydev, offset, val);
71 		else
72 			rc = phy_read(phydev, offset);
73 		return rc;
74 	}
75 
76 	if (mode == PHYACC_ATTR_MODE_WRITE) {
77 		ereg = LAN87XX_EXT_REG_CTL_WR_CTL;
78 		rc = phy_write(phydev, LAN87XX_EXT_REG_WR_DATA, val);
79 		if (rc < 0)
80 			return rc;
81 	} else {
82 		ereg = LAN87XX_EXT_REG_CTL_RD_CTL;
83 	}
84 
85 	ereg |= (bank << 8) | offset;
86 
87 	rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, ereg);
88 	if (rc < 0)
89 		return rc;
90 
91 	if (mode == PHYACC_ATTR_MODE_READ)
92 		rc = phy_read(phydev, LAN87XX_EXT_REG_RD_DATA);
93 
94 	return rc;
95 }
96 
97 static int access_ereg_modify_changed(struct phy_device *phydev,
98 				      u8 bank, u8 offset, u16 val, u16 mask)
99 {
100 	int new = 0, rc = 0;
101 
102 	if (bank > PHYACC_ATTR_BANK_MAX)
103 		return -EINVAL;
104 
105 	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, bank, offset, val);
106 	if (rc < 0)
107 		return rc;
108 
109 	new = val | (rc & (mask ^ 0xFFFF));
110 	rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, bank, offset, new);
111 
112 	return rc;
113 }
114 
115 static int lan87xx_phy_init(struct phy_device *phydev)
116 {
117 	static const struct access_ereg_val init[] = {
118 		/* TX Amplitude = 5 */
119 		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_AFE, 0x0B,
120 		 0x000A, 0x001E},
121 		/* Clear SMI interrupts */
122 		{PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 0x18,
123 		 0, 0},
124 		/* Clear MISC interrupts */
125 		{PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, 0x08,
126 		 0, 0},
127 		/* Turn on TC10 Ring Oscillator (ROSC) */
128 		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_MISC, 0x20,
129 		 0x0020, 0x0020},
130 		/* WUR Detect Length to 1.2uS, LPC Detect Length to 1.09uS */
131 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_PCS, 0x20,
132 		 0x283C, 0},
133 		/* Wake_In Debounce Length to 39uS, Wake_Out Length to 79uS */
134 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x21,
135 		 0x274F, 0},
136 		/* Enable Auto Wake Forward to Wake_Out, ROSC on, Sleep,
137 		 * and Wake_In to wake PHY
138 		 */
139 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x20,
140 		 0x80A7, 0},
141 		/* Enable WUP Auto Fwd, Enable Wake on MDI, Wakeup Debouncer
142 		 * to 128 uS
143 		 */
144 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x24,
145 		 0xF110, 0},
146 		/* Enable HW Init */
147 		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_SMI, 0x1A,
148 		 0x0100, 0x0100},
149 	};
150 	int rc, i;
151 
152 	/* Start manual initialization procedures in Managed Mode */
153 	rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
154 					0x1a, 0x0000, 0x0100);
155 	if (rc < 0)
156 		return rc;
157 
158 	/* Soft Reset the SMI block */
159 	rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
160 					0x00, 0x8000, 0x8000);
161 	if (rc < 0)
162 		return rc;
163 
164 	/* Check to see if the self-clearing bit is cleared */
165 	usleep_range(1000, 2000);
166 	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
167 			 PHYACC_ATTR_BANK_SMI, 0x00, 0);
168 	if (rc < 0)
169 		return rc;
170 	if ((rc & 0x8000) != 0)
171 		return -ETIMEDOUT;
172 
173 	/* PHY Initialization */
174 	for (i = 0; i < ARRAY_SIZE(init); i++) {
175 		if (init[i].mode == PHYACC_ATTR_MODE_MODIFY) {
176 			rc = access_ereg_modify_changed(phydev, init[i].bank,
177 							init[i].offset,
178 							init[i].val,
179 							init[i].mask);
180 		} else {
181 			rc = access_ereg(phydev, init[i].mode, init[i].bank,
182 					 init[i].offset, init[i].val);
183 		}
184 		if (rc < 0)
185 			return rc;
186 	}
187 
188 	return 0;
189 }
190 
191 static int lan87xx_phy_config_intr(struct phy_device *phydev)
192 {
193 	int rc, val = 0;
194 
195 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
196 		/* unmask all source and clear them before enable */
197 		rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, 0x7FFF);
198 		rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
199 		val = LAN87XX_MASK_LINK_UP | LAN87XX_MASK_LINK_DOWN;
200 		rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
201 	} else {
202 		rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
203 		if (rc)
204 			return rc;
205 
206 		rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
207 	}
208 
209 	return rc < 0 ? rc : 0;
210 }
211 
212 static irqreturn_t lan87xx_handle_interrupt(struct phy_device *phydev)
213 {
214 	int irq_status;
215 
216 	irq_status = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
217 	if (irq_status < 0) {
218 		phy_error(phydev);
219 		return IRQ_NONE;
220 	}
221 
222 	if (irq_status == 0)
223 		return IRQ_NONE;
224 
225 	phy_trigger_machine(phydev);
226 
227 	return IRQ_HANDLED;
228 }
229 
230 static int lan87xx_config_init(struct phy_device *phydev)
231 {
232 	int rc = lan87xx_phy_init(phydev);
233 
234 	return rc < 0 ? rc : 0;
235 }
236 
237 static int microchip_cable_test_start_common(struct phy_device *phydev)
238 {
239 	int bmcr, bmsr, ret;
240 
241 	/* If auto-negotiation is enabled, but not complete, the cable
242 	 * test never completes. So disable auto-neg.
243 	 */
244 	bmcr = phy_read(phydev, MII_BMCR);
245 	if (bmcr < 0)
246 		return bmcr;
247 
248 	bmsr = phy_read(phydev, MII_BMSR);
249 
250 	if (bmsr < 0)
251 		return bmsr;
252 
253 	if (bmcr & BMCR_ANENABLE) {
254 		ret =  phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
255 		if (ret < 0)
256 			return ret;
257 		ret = genphy_soft_reset(phydev);
258 		if (ret < 0)
259 			return ret;
260 	}
261 
262 	/* If the link is up, allow it some time to go down */
263 	if (bmsr & BMSR_LSTATUS)
264 		msleep(1500);
265 
266 	return 0;
267 }
268 
269 static int lan87xx_cable_test_start(struct phy_device *phydev)
270 {
271 	static const struct access_ereg_val cable_test[] = {
272 		/* min wait */
273 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 93,
274 		 0, 0},
275 		/* max wait */
276 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 94,
277 		 10, 0},
278 		/* pulse cycle */
279 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 95,
280 		 90, 0},
281 		/* cable diag thresh */
282 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 92,
283 		 60, 0},
284 		/* max gain */
285 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 79,
286 		 31, 0},
287 		/* clock align for each iteration */
288 		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_DSP, 55,
289 		 0, 0x0038},
290 		/* max cycle wait config */
291 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 94,
292 		 70, 0},
293 		/* start cable diag*/
294 		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 90,
295 		 1, 0},
296 	};
297 	int rc, i;
298 
299 	rc = microchip_cable_test_start_common(phydev);
300 	if (rc < 0)
301 		return rc;
302 
303 	/* start cable diag */
304 	/* check if part is alive - if not, return diagnostic error */
305 	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI,
306 			 0x00, 0);
307 	if (rc < 0)
308 		return rc;
309 
310 	/* master/slave specific configs */
311 	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI,
312 			 0x0A, 0);
313 	if (rc < 0)
314 		return rc;
315 
316 	if ((rc & 0x4000) != 0x4000) {
317 		/* DUT is Slave */
318 		rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_AFE,
319 						0x0E, 0x5, 0x7);
320 		if (rc < 0)
321 			return rc;
322 		rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
323 						0x1A, 0x8, 0x8);
324 		if (rc < 0)
325 			return rc;
326 	} else {
327 		/* DUT is Master */
328 		rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
329 						0x10, 0x8, 0x40);
330 		if (rc < 0)
331 			return rc;
332 	}
333 
334 	for (i = 0; i < ARRAY_SIZE(cable_test); i++) {
335 		if (cable_test[i].mode == PHYACC_ATTR_MODE_MODIFY) {
336 			rc = access_ereg_modify_changed(phydev,
337 							cable_test[i].bank,
338 							cable_test[i].offset,
339 							cable_test[i].val,
340 							cable_test[i].mask);
341 			/* wait 50ms */
342 			msleep(50);
343 		} else {
344 			rc = access_ereg(phydev, cable_test[i].mode,
345 					 cable_test[i].bank,
346 					 cable_test[i].offset,
347 					 cable_test[i].val);
348 		}
349 		if (rc < 0)
350 			return rc;
351 	}
352 	/* cable diag started */
353 
354 	return 0;
355 }
356 
357 static int lan87xx_cable_test_report_trans(u32 result)
358 {
359 	switch (result) {
360 	case LAN87XX_CABLE_TEST_OK:
361 		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
362 	case LAN87XX_CABLE_TEST_OPEN:
363 		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
364 	case LAN87XX_CABLE_TEST_SAME_SHORT:
365 		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
366 	default:
367 		/* DIAGNOSTIC_ERROR */
368 		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
369 	}
370 }
371 
372 static int lan87xx_cable_test_report(struct phy_device *phydev)
373 {
374 	int pos_peak_cycle = 0, pos_peak_in_phases = 0, pos_peak_phase = 0;
375 	int neg_peak_cycle = 0, neg_peak_in_phases = 0, neg_peak_phase = 0;
376 	int noise_margin = 20, time_margin = 89, jitter_var = 30;
377 	int min_time_diff = 96, max_time_diff = 96 + time_margin;
378 	bool fault = false, check_a = false, check_b = false;
379 	int gain_idx = 0, pos_peak = 0, neg_peak = 0;
380 	int pos_peak_time = 0, neg_peak_time = 0;
381 	int pos_peak_in_phases_hybrid = 0;
382 	int detect = -1;
383 
384 	gain_idx = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
385 			       PHYACC_ATTR_BANK_DSP, 151, 0);
386 	/* read non-hybrid results */
387 	pos_peak = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
388 			       PHYACC_ATTR_BANK_DSP, 153, 0);
389 	neg_peak = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
390 			       PHYACC_ATTR_BANK_DSP, 154, 0);
391 	pos_peak_time = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
392 				    PHYACC_ATTR_BANK_DSP, 156, 0);
393 	neg_peak_time = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
394 				    PHYACC_ATTR_BANK_DSP, 157, 0);
395 
396 	pos_peak_cycle = (pos_peak_time >> 7) & 0x7F;
397 	/* calculate non-hybrid values */
398 	pos_peak_phase = pos_peak_time & 0x7F;
399 	pos_peak_in_phases = (pos_peak_cycle * 96) + pos_peak_phase;
400 	neg_peak_cycle = (neg_peak_time >> 7) & 0x7F;
401 	neg_peak_phase = neg_peak_time & 0x7F;
402 	neg_peak_in_phases = (neg_peak_cycle * 96) + neg_peak_phase;
403 
404 	/* process values */
405 	check_a =
406 		((pos_peak_in_phases - neg_peak_in_phases) >= min_time_diff) &&
407 		((pos_peak_in_phases - neg_peak_in_phases) < max_time_diff) &&
408 		pos_peak_in_phases_hybrid < pos_peak_in_phases &&
409 		(pos_peak_in_phases_hybrid < (neg_peak_in_phases + jitter_var));
410 	check_b =
411 		((neg_peak_in_phases - pos_peak_in_phases) >= min_time_diff) &&
412 		((neg_peak_in_phases - pos_peak_in_phases) < max_time_diff) &&
413 		pos_peak_in_phases_hybrid < neg_peak_in_phases &&
414 		(pos_peak_in_phases_hybrid < (pos_peak_in_phases + jitter_var));
415 
416 	if (pos_peak_in_phases > neg_peak_in_phases && check_a)
417 		detect = 2;
418 	else if ((neg_peak_in_phases > pos_peak_in_phases) && check_b)
419 		detect = 1;
420 
421 	if (pos_peak > noise_margin && neg_peak > noise_margin &&
422 	    gain_idx >= 0) {
423 		if (detect == 1 || detect == 2)
424 			fault = true;
425 	}
426 
427 	if (!fault)
428 		detect = 0;
429 
430 	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
431 				lan87xx_cable_test_report_trans(detect));
432 
433 	return 0;
434 }
435 
436 static int lan87xx_cable_test_get_status(struct phy_device *phydev,
437 					 bool *finished)
438 {
439 	int rc = 0;
440 
441 	*finished = false;
442 
443 	/* check if cable diag was finished */
444 	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_DSP,
445 			 90, 0);
446 	if (rc < 0)
447 		return rc;
448 
449 	if ((rc & 2) == 2) {
450 		/* stop cable diag*/
451 		rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE,
452 				 PHYACC_ATTR_BANK_DSP,
453 				 90, 0);
454 		if (rc < 0)
455 			return rc;
456 
457 		*finished = true;
458 
459 		return lan87xx_cable_test_report(phydev);
460 	}
461 
462 	return 0;
463 }
464 
465 static struct phy_driver microchip_t1_phy_driver[] = {
466 	{
467 		.phy_id         = 0x0007c150,
468 		.phy_id_mask    = 0xfffffff0,
469 		.name           = "Microchip LAN87xx T1",
470 		.flags          = PHY_POLL_CABLE_TEST,
471 
472 		.features       = PHY_BASIC_T1_FEATURES,
473 
474 		.config_init	= lan87xx_config_init,
475 
476 		.config_intr    = lan87xx_phy_config_intr,
477 		.handle_interrupt = lan87xx_handle_interrupt,
478 
479 		.suspend        = genphy_suspend,
480 		.resume         = genphy_resume,
481 		.cable_test_start = lan87xx_cable_test_start,
482 		.cable_test_get_status = lan87xx_cable_test_get_status,
483 	}
484 };
485 
486 module_phy_driver(microchip_t1_phy_driver);
487 
488 static struct mdio_device_id __maybe_unused microchip_t1_tbl[] = {
489 	{ 0x0007c150, 0xfffffff0 },
490 	{ }
491 };
492 
493 MODULE_DEVICE_TABLE(mdio, microchip_t1_tbl);
494 
495 MODULE_AUTHOR(DRIVER_AUTHOR);
496 MODULE_DESCRIPTION(DRIVER_DESC);
497 MODULE_LICENSE("GPL");
498