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