xref: /freebsd/sys/dev/cxgb/common/cxgb_vsc8211.c (revision 42249ef2)
1 /**************************************************************************
2 SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 
4 Copyright (c) 2007, Chelsio Inc.
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10  1. Redistributions of source code must retain the above copyright notice,
11     this list of conditions and the following disclaimer.
12 
13  2. Neither the name of the Chelsio Corporation nor the names of its
14     contributors may be used to endorse or promote products derived from
15     this software without specific prior written permission.
16 
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
28 
29 ***************************************************************************/
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <cxgb_include.h>
35 
36 #undef msleep
37 #define msleep t3_os_sleep
38 
39 /* VSC8211 PHY specific registers. */
40 enum {
41 	VSC8211_SIGDET_CTRL   = 19,
42 	VSC8211_EXT_CTRL      = 23,
43 	VSC8211_PHY_CTRL      = 24,
44 	VSC8211_INTR_ENABLE   = 25,
45 	VSC8211_INTR_STATUS   = 26,
46 	VSC8211_LED_CTRL      = 27,
47 	VSC8211_AUX_CTRL_STAT = 28,
48 	VSC8211_EXT_PAGE_AXS  = 31,
49 };
50 
51 enum {
52 	VSC_INTR_RX_ERR     = 1 << 0,
53 	VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
54 	VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
55 	VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
56 	VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
57 	VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
58 	VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
59 	VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
60 	VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
61 	VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
62 	VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
63 	VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
64 	VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
65 	VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
66 	VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
67 };
68 
69 enum {
70 	VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
71 	VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
72 };
73 
74 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
75 			   VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
76 	 		   VSC_INTR_NEG_DONE)
77 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
78 		   VSC_INTR_ENABLE)
79 
80 /* PHY specific auxiliary control & status register fields */
81 #define S_ACSR_ACTIPHY_TMR    0
82 #define M_ACSR_ACTIPHY_TMR    0x3
83 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
84 
85 #define S_ACSR_SPEED    3
86 #define M_ACSR_SPEED    0x3
87 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
88 
89 #define S_ACSR_DUPLEX 5
90 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
91 
92 #define S_ACSR_ACTIPHY 6
93 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
94 
95 /*
96  * Reset the PHY.  This PHY completes reset immediately so we never wait.
97  */
98 static int vsc8211_reset(struct cphy *cphy, int wait)
99 {
100 	return t3_phy_reset(cphy, 0, 0);
101 }
102 
103 static int vsc8211_intr_enable(struct cphy *cphy)
104 {
105 	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
106 }
107 
108 static int vsc8211_intr_disable(struct cphy *cphy)
109 {
110 	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
111 }
112 
113 static int vsc8211_intr_clear(struct cphy *cphy)
114 {
115 	u32 val;
116 
117 	/* Clear PHY interrupts by reading the register. */
118 	return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
119 }
120 
121 static int vsc8211_autoneg_enable(struct cphy *cphy)
122 {
123 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
124 				   BMCR_ANENABLE | BMCR_ANRESTART);
125 }
126 
127 static int vsc8211_autoneg_restart(struct cphy *cphy)
128 {
129 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
130 				   BMCR_ANRESTART);
131 }
132 
133 static int vsc8211_get_link_status(struct cphy *cphy, int *link_state,
134 				     int *speed, int *duplex, int *fc)
135 {
136 	unsigned int bmcr, status, lpa, adv;
137 	int err, sp = -1, dplx = -1, pause = 0;
138 
139 	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
140 	if (!err)
141 		err = mdio_read(cphy, 0, MII_BMSR, &status);
142 	if (err)
143 		return err;
144 
145 	if (link_state) {
146 		/*
147 		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
148 		 * once more to get the current link state.
149 		 */
150 		if (!(status & BMSR_LSTATUS))
151 			err = mdio_read(cphy, 0, MII_BMSR, &status);
152 		if (err)
153 			return err;
154 		*link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
155 		    PHY_LINK_DOWN;
156 	}
157 	if (!(bmcr & BMCR_ANENABLE)) {
158 		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
159 		if (bmcr & BMCR_SPEED1000)
160 			sp = SPEED_1000;
161 		else if (bmcr & BMCR_SPEED100)
162 			sp = SPEED_100;
163 		else
164 			sp = SPEED_10;
165 	} else if (status & BMSR_ANEGCOMPLETE) {
166 		err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
167 		if (err)
168 			return err;
169 
170 		dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
171 		sp = G_ACSR_SPEED(status);
172 		if (sp == 0)
173 			sp = SPEED_10;
174 		else if (sp == 1)
175 			sp = SPEED_100;
176 		else
177 			sp = SPEED_1000;
178 
179 		if (fc && dplx == DUPLEX_FULL) {
180 			err = mdio_read(cphy, 0, MII_LPA, &lpa);
181 			if (!err)
182 				err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
183 			if (err)
184 				return err;
185 
186 			if (lpa & adv & ADVERTISE_PAUSE_CAP)
187 				pause = PAUSE_RX | PAUSE_TX;
188 			else if ((lpa & ADVERTISE_PAUSE_CAP) &&
189 				 (lpa & ADVERTISE_PAUSE_ASYM) &&
190 				 (adv & ADVERTISE_PAUSE_ASYM))
191 				pause = PAUSE_TX;
192 			else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
193 				 (adv & ADVERTISE_PAUSE_CAP))
194 				pause = PAUSE_RX;
195 		}
196 	}
197 	if (speed)
198 		*speed = sp;
199 	if (duplex)
200 		*duplex = dplx;
201 	if (fc)
202 		*fc = pause;
203 	return 0;
204 }
205 
206 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_state,
207 					 int *speed, int *duplex, int *fc)
208 {
209 	unsigned int bmcr, status, lpa, adv;
210 	int err, sp = -1, dplx = -1, pause = 0;
211 
212 	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
213 	if (!err)
214 		err = mdio_read(cphy, 0, MII_BMSR, &status);
215 	if (err)
216 		return err;
217 
218 	if (link_state) {
219 		/*
220 		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
221 		 * once more to get the current link state.
222 		 */
223 		if (!(status & BMSR_LSTATUS))
224 			err = mdio_read(cphy, 0, MII_BMSR, &status);
225 		if (err)
226 			return err;
227 		*link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
228 		    PHY_LINK_DOWN;
229 	}
230 	if (!(bmcr & BMCR_ANENABLE)) {
231 		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
232 		if (bmcr & BMCR_SPEED1000)
233 			sp = SPEED_1000;
234 		else if (bmcr & BMCR_SPEED100)
235 			sp = SPEED_100;
236 		else
237 			sp = SPEED_10;
238 	} else if (status & BMSR_ANEGCOMPLETE) {
239 		err = mdio_read(cphy, 0, MII_LPA, &lpa);
240 		if (!err)
241 			err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
242 		if (err)
243 			return err;
244 
245 		if (adv & lpa & ADVERTISE_1000XFULL) {
246 			dplx = DUPLEX_FULL;
247 			sp = SPEED_1000;
248 		} else if (adv & lpa & ADVERTISE_1000XHALF) {
249 			dplx = DUPLEX_HALF;
250 			sp = SPEED_1000;
251 		}
252 
253 		if (fc && dplx == DUPLEX_FULL) {
254 			if (lpa & adv & ADVERTISE_1000XPAUSE)
255 				pause = PAUSE_RX | PAUSE_TX;
256 			else if ((lpa & ADVERTISE_1000XPAUSE) &&
257 				 (adv & lpa & ADVERTISE_1000XPSE_ASYM))
258 				pause = PAUSE_TX;
259 			else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
260 				 (adv & ADVERTISE_1000XPAUSE))
261 				pause = PAUSE_RX;
262 		}
263 	}
264 	if (speed)
265 		*speed = sp;
266 	if (duplex)
267 		*duplex = dplx;
268 	if (fc)
269 		*fc = pause;
270 	return 0;
271 }
272 
273 /*
274  * Enable/disable auto MDI/MDI-X in forced link speed mode.
275  */
276 static int vsc8211_set_automdi(struct cphy *phy, int enable)
277 {
278 	int err;
279 
280 	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
281 	    (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
282 	    (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
283 	    (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
284 	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
285 		return err;
286 	return 0;
287 }
288 
289 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
290 {
291 	int err;
292 
293 	err = t3_set_phy_speed_duplex(phy, speed, duplex);
294 	if (!err)
295 		err = vsc8211_set_automdi(phy, 1);
296 	return err;
297 }
298 
299 static int vsc8211_power_down(struct cphy *cphy, int enable)
300 {
301 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
302 				   enable ? BMCR_PDOWN : 0);
303 }
304 
305 static int vsc8211_intr_handler(struct cphy *cphy)
306 {
307 	unsigned int cause;
308 	int err, cphy_cause = 0;
309 
310 	err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
311 	if (err)
312 		return err;
313 
314 	cause &= INTR_MASK;
315 	if (cause & CFG_CHG_INTR_MASK)
316 		cphy_cause |= cphy_cause_link_change;
317 	if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
318 		cphy_cause |= cphy_cause_fifo_error;
319 	return cphy_cause;
320 }
321 
322 #ifdef C99_NOT_SUPPORTED
323 static struct cphy_ops vsc8211_ops = {
324 	vsc8211_reset,
325 	vsc8211_intr_enable,
326 	vsc8211_intr_disable,
327 	vsc8211_intr_clear,
328 	vsc8211_intr_handler,
329 	vsc8211_autoneg_enable,
330 	vsc8211_autoneg_restart,
331 	t3_phy_advertise,
332 	NULL,
333 	vsc8211_set_speed_duplex,
334 	vsc8211_get_link_status,
335 	vsc8211_power_down,
336 };
337 
338 static struct cphy_ops vsc8211_fiber_ops = {
339 	vsc8211_reset,
340 	vsc8211_intr_enable,
341 	vsc8211_intr_disable,
342 	vsc8211_intr_clear,
343 	vsc8211_intr_handler,
344 	vsc8211_autoneg_enable,
345 	vsc8211_autoneg_restart,
346 	t3_phy_advertise_fiber,
347 	NULL,
348 	t3_set_phy_speed_duplex,
349 	vsc8211_get_link_status_fiber,
350 	vsc8211_power_down,
351 };
352 #else
353 static struct cphy_ops vsc8211_ops = {
354 	.reset             = vsc8211_reset,
355 	.intr_enable       = vsc8211_intr_enable,
356 	.intr_disable      = vsc8211_intr_disable,
357 	.intr_clear        = vsc8211_intr_clear,
358 	.intr_handler      = vsc8211_intr_handler,
359 	.autoneg_enable    = vsc8211_autoneg_enable,
360 	.autoneg_restart   = vsc8211_autoneg_restart,
361 	.advertise         = t3_phy_advertise,
362 	.set_speed_duplex  = vsc8211_set_speed_duplex,
363 	.get_link_status   = vsc8211_get_link_status,
364 	.power_down        = vsc8211_power_down,
365 };
366 
367 static struct cphy_ops vsc8211_fiber_ops = {
368 	.reset             = vsc8211_reset,
369 	.intr_enable       = vsc8211_intr_enable,
370 	.intr_disable      = vsc8211_intr_disable,
371 	.intr_clear        = vsc8211_intr_clear,
372 	.intr_handler      = vsc8211_intr_handler,
373 	.autoneg_enable    = vsc8211_autoneg_enable,
374 	.autoneg_restart   = vsc8211_autoneg_restart,
375 	.advertise         = t3_phy_advertise_fiber,
376 	.set_speed_duplex  = t3_set_phy_speed_duplex,
377 	.get_link_status   = vsc8211_get_link_status_fiber,
378 	.power_down        = vsc8211_power_down,
379 };
380 #endif
381 
382 #define VSC8211_PHY_CTRL 24
383 
384 #define S_VSC8211_TXFIFODEPTH    7
385 #define M_VSC8211_TXFIFODEPTH    0x7
386 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
387 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
388 
389 #define S_VSC8211_RXFIFODEPTH    4
390 #define M_VSC8211_RXFIFODEPTH    0x7
391 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
392 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
393 
394 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
395 {
396 	/* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
397 	unsigned int val = 4;
398 	unsigned int currentregval;
399 	unsigned int regval;
400 	int err;
401 
402 	/* Retrieve the port info structure from adater_t */
403 	struct port_info *portinfo = adap2pinfo(adap, port);
404 
405 	/* What phy is this */
406 	struct cphy *phy = &portinfo->phy;
407 
408 	/* Read the current value of the PHY control Register */
409 	err = mdio_read(phy, 0, VSC8211_PHY_CTRL, &currentregval);
410 
411 	if (err)
412 		return err;
413 
414 	/* IEEE mode supports up to 1518 bytes */
415 	/* mtu does not contain the header + FCS (18 bytes) */
416 	if (mtu > 1500)
417 		/*
418 		 * If using a packet size > 1500  set TX FIFO Depth bits
419 		 * 9:7 to 011 (Jumbo packet mode)
420 		 */
421 		val = 3;
422 
423 	regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) |
424 		(currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
425 		~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
426 
427 	return  mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
428 }
429 
430 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
431 			const struct mdio_ops *mdio_ops)
432 {
433 	struct cphy *phy = &pinfo->phy;
434 	int err;
435 	unsigned int val;
436 
437 	cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
438 		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
439 		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
440 		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
441 	msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
442 
443 	err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
444 	if (err)
445 		return err;
446 	if (val & VSC_CTRL_MEDIA_MODE_HI) {
447 		/* copper interface, just need to configure the LEDs */
448 		return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
449 	}
450 
451 	phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
452 		    SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
453 	phy->desc = "1000BASE-X";
454 	phy->ops = &vsc8211_fiber_ops;
455 
456 	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
457 	    (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
458 	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
459 	    (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
460 			      val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
461 	    (err = vsc8211_reset(phy, 0)) != 0)
462 		return err;
463 
464 	udelay(5); /* delay after reset before next SMI */
465 	return 0;
466 }
467