xref: /freebsd/sys/dev/cxgb/common/cxgb_vsc8211.c (revision aa0a1e58)
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_ok,
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_ok) {
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_ok = (status & BMSR_LSTATUS) != 0;
154 	}
155 	if (!(bmcr & BMCR_ANENABLE)) {
156 		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
157 		if (bmcr & BMCR_SPEED1000)
158 			sp = SPEED_1000;
159 		else if (bmcr & BMCR_SPEED100)
160 			sp = SPEED_100;
161 		else
162 			sp = SPEED_10;
163 	} else if (status & BMSR_ANEGCOMPLETE) {
164 		err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
165 		if (err)
166 			return err;
167 
168 		dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
169 		sp = G_ACSR_SPEED(status);
170 		if (sp == 0)
171 			sp = SPEED_10;
172 		else if (sp == 1)
173 			sp = SPEED_100;
174 		else
175 			sp = SPEED_1000;
176 
177 		if (fc && dplx == DUPLEX_FULL) {
178 			err = mdio_read(cphy, 0, MII_LPA, &lpa);
179 			if (!err)
180 				err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
181 			if (err)
182 				return err;
183 
184 			if (lpa & adv & ADVERTISE_PAUSE_CAP)
185 				pause = PAUSE_RX | PAUSE_TX;
186 			else if ((lpa & ADVERTISE_PAUSE_CAP) &&
187 				 (lpa & ADVERTISE_PAUSE_ASYM) &&
188 				 (adv & ADVERTISE_PAUSE_ASYM))
189 				pause = PAUSE_TX;
190 			else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
191 				 (adv & ADVERTISE_PAUSE_CAP))
192 				pause = PAUSE_RX;
193 		}
194 	}
195 	if (speed)
196 		*speed = sp;
197 	if (duplex)
198 		*duplex = dplx;
199 	if (fc)
200 		*fc = pause;
201 	return 0;
202 }
203 
204 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
205 					 int *speed, int *duplex, int *fc)
206 {
207 	unsigned int bmcr, status, lpa, adv;
208 	int err, sp = -1, dplx = -1, pause = 0;
209 
210 	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
211 	if (!err)
212 		err = mdio_read(cphy, 0, MII_BMSR, &status);
213 	if (err)
214 		return err;
215 
216 	if (link_ok) {
217 		/*
218 		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
219 		 * once more to get the current link state.
220 		 */
221 		if (!(status & BMSR_LSTATUS))
222 			err = mdio_read(cphy, 0, MII_BMSR, &status);
223 		if (err)
224 			return err;
225 		*link_ok = (status & BMSR_LSTATUS) != 0;
226 	}
227 	if (!(bmcr & BMCR_ANENABLE)) {
228 		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
229 		if (bmcr & BMCR_SPEED1000)
230 			sp = SPEED_1000;
231 		else if (bmcr & BMCR_SPEED100)
232 			sp = SPEED_100;
233 		else
234 			sp = SPEED_10;
235 	} else if (status & BMSR_ANEGCOMPLETE) {
236 		err = mdio_read(cphy, 0, MII_LPA, &lpa);
237 		if (!err)
238 			err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
239 		if (err)
240 			return err;
241 
242 		if (adv & lpa & ADVERTISE_1000XFULL) {
243 			dplx = DUPLEX_FULL;
244 			sp = SPEED_1000;
245 		} else if (adv & lpa & ADVERTISE_1000XHALF) {
246 			dplx = DUPLEX_HALF;
247 			sp = SPEED_1000;
248 		}
249 
250 		if (fc && dplx == DUPLEX_FULL) {
251 			if (lpa & adv & ADVERTISE_1000XPAUSE)
252 				pause = PAUSE_RX | PAUSE_TX;
253 			else if ((lpa & ADVERTISE_1000XPAUSE) &&
254 				 (adv & lpa & ADVERTISE_1000XPSE_ASYM))
255 				pause = PAUSE_TX;
256 			else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
257 				 (adv & ADVERTISE_1000XPAUSE))
258 				pause = PAUSE_RX;
259 		}
260 	}
261 	if (speed)
262 		*speed = sp;
263 	if (duplex)
264 		*duplex = dplx;
265 	if (fc)
266 		*fc = pause;
267 	return 0;
268 }
269 
270 /*
271  * Enable/disable auto MDI/MDI-X in forced link speed mode.
272  */
273 static int vsc8211_set_automdi(struct cphy *phy, int enable)
274 {
275 	int err;
276 
277 	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
278 	    (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
279 	    (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
280 	    (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
281 	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
282 		return err;
283 	return 0;
284 }
285 
286 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
287 {
288 	int err;
289 
290 	err = t3_set_phy_speed_duplex(phy, speed, duplex);
291 	if (!err)
292 		err = vsc8211_set_automdi(phy, 1);
293 	return err;
294 }
295 
296 static int vsc8211_power_down(struct cphy *cphy, int enable)
297 {
298 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
299 				   enable ? BMCR_PDOWN : 0);
300 }
301 
302 static int vsc8211_intr_handler(struct cphy *cphy)
303 {
304 	unsigned int cause;
305 	int err, cphy_cause = 0;
306 
307 	err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
308 	if (err)
309 		return err;
310 
311 	cause &= INTR_MASK;
312 	if (cause & CFG_CHG_INTR_MASK)
313 		cphy_cause |= cphy_cause_link_change;
314 	if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
315 		cphy_cause |= cphy_cause_fifo_error;
316 	return cphy_cause;
317 }
318 
319 #ifdef C99_NOT_SUPPORTED
320 static struct cphy_ops vsc8211_ops = {
321 	vsc8211_reset,
322 	vsc8211_intr_enable,
323 	vsc8211_intr_disable,
324 	vsc8211_intr_clear,
325 	vsc8211_intr_handler,
326 	vsc8211_autoneg_enable,
327 	vsc8211_autoneg_restart,
328 	t3_phy_advertise,
329 	NULL,
330 	vsc8211_set_speed_duplex,
331 	vsc8211_get_link_status,
332 	vsc8211_power_down,
333 };
334 
335 static struct cphy_ops vsc8211_fiber_ops = {
336 	vsc8211_reset,
337 	vsc8211_intr_enable,
338 	vsc8211_intr_disable,
339 	vsc8211_intr_clear,
340 	vsc8211_intr_handler,
341 	vsc8211_autoneg_enable,
342 	vsc8211_autoneg_restart,
343 	t3_phy_advertise_fiber,
344 	NULL,
345 	t3_set_phy_speed_duplex,
346 	vsc8211_get_link_status_fiber,
347 	vsc8211_power_down,
348 };
349 #else
350 static struct cphy_ops vsc8211_ops = {
351 	.reset             = vsc8211_reset,
352 	.intr_enable       = vsc8211_intr_enable,
353 	.intr_disable      = vsc8211_intr_disable,
354 	.intr_clear        = vsc8211_intr_clear,
355 	.intr_handler      = vsc8211_intr_handler,
356 	.autoneg_enable    = vsc8211_autoneg_enable,
357 	.autoneg_restart   = vsc8211_autoneg_restart,
358 	.advertise         = t3_phy_advertise,
359 	.set_speed_duplex  = vsc8211_set_speed_duplex,
360 	.get_link_status   = vsc8211_get_link_status,
361 	.power_down        = vsc8211_power_down,
362 };
363 
364 static struct cphy_ops vsc8211_fiber_ops = {
365 	.reset             = vsc8211_reset,
366 	.intr_enable       = vsc8211_intr_enable,
367 	.intr_disable      = vsc8211_intr_disable,
368 	.intr_clear        = vsc8211_intr_clear,
369 	.intr_handler      = vsc8211_intr_handler,
370 	.autoneg_enable    = vsc8211_autoneg_enable,
371 	.autoneg_restart   = vsc8211_autoneg_restart,
372 	.advertise         = t3_phy_advertise_fiber,
373 	.set_speed_duplex  = t3_set_phy_speed_duplex,
374 	.get_link_status   = vsc8211_get_link_status_fiber,
375 	.power_down        = vsc8211_power_down,
376 };
377 #endif
378 
379 #define VSC8211_PHY_CTRL 24
380 
381 #define S_VSC8211_TXFIFODEPTH    7
382 #define M_VSC8211_TXFIFODEPTH    0x7
383 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
384 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
385 
386 #define S_VSC8211_RXFIFODEPTH    4
387 #define M_VSC8211_RXFIFODEPTH    0x7
388 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
389 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
390 
391 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
392 {
393 	/* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
394 	unsigned int val = 4;
395 	unsigned int currentregval;
396 	unsigned int regval;
397 	int err;
398 
399 	/* Retrieve the port info structure from adater_t */
400 	struct port_info *portinfo = adap2pinfo(adap, port);
401 
402 	/* What phy is this */
403 	struct cphy *phy = &portinfo->phy;
404 
405 	/* Read the current value of the PHY control Register */
406 	err = mdio_read(phy, 0, VSC8211_PHY_CTRL, &currentregval);
407 
408 	if (err)
409 		return err;
410 
411 	/* IEEE mode supports up to 1518 bytes */
412 	/* mtu does not contain the header + FCS (18 bytes) */
413 	if (mtu > 1500)
414 		/*
415 		 * If using a packet size > 1500  set TX FIFO Depth bits
416 		 * 9:7 to 011 (Jumbo packet mode)
417 		 */
418 		val = 3;
419 
420 	regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) |
421 		(currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
422 		~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
423 
424 	return  mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
425 }
426 
427 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
428 			const struct mdio_ops *mdio_ops)
429 {
430 	struct cphy *phy = &pinfo->phy;
431 	int err;
432 	unsigned int val;
433 
434 	cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
435 		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
436 		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
437 		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
438 	msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
439 
440 	err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
441 	if (err)
442 		return err;
443 	if (val & VSC_CTRL_MEDIA_MODE_HI) {
444 		/* copper interface, just need to configure the LEDs */
445 		return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
446 	}
447 
448 	phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
449 		    SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
450 	phy->desc = "1000BASE-X";
451 	phy->ops = &vsc8211_fiber_ops;
452 
453 	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
454 	    (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
455 	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
456 	    (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
457 			      val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
458 	    (err = vsc8211_reset(phy, 0)) != 0)
459 		return err;
460 
461 	udelay(5); /* delay after reset before next SMI */
462 	return 0;
463 }
464