xref: /freebsd/sys/dev/cxgb/common/cxgb_vsc8211.c (revision 8e10660f)
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 #ifdef CONFIG_DEFINED
34 #include <cxgb_include.h>
35 #else
36 #include <dev/cxgb/cxgb_include.h>
37 #endif
38 
39 #undef msleep
40 #define msleep t3_os_sleep
41 
42 /* VSC8211 PHY specific registers. */
43 enum {
44 	VSC8211_SIGDET_CTRL   = 19,
45 	VSC8211_EXT_CTRL      = 23,
46 	VSC8211_INTR_ENABLE   = 25,
47 	VSC8211_INTR_STATUS   = 26,
48 	VSC8211_AUX_CTRL_STAT = 28,
49 	VSC8211_EXT_PAGE_AXS  = 31,
50 };
51 
52 enum {
53 	VSC_INTR_RX_ERR     = 1 << 0,
54 	VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
55 	VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
56 	VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
57 	VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
58 	VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
59 	VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
60 	VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
61 	VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
62 	VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
63 	VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
64 	VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
65 	VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
66 	VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
67 	VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
68 };
69 
70 enum {
71 	VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
72 	VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
73 };
74 
75 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
76 			   VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
77 	 		   VSC_INTR_NEG_DONE)
78 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
79 		   VSC_INTR_ENABLE)
80 
81 /* PHY specific auxiliary control & status register fields */
82 #define S_ACSR_ACTIPHY_TMR    0
83 #define M_ACSR_ACTIPHY_TMR    0x3
84 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
85 
86 #define S_ACSR_SPEED    3
87 #define M_ACSR_SPEED    0x3
88 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
89 
90 #define S_ACSR_DUPLEX 5
91 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
92 
93 #define S_ACSR_ACTIPHY 6
94 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
95 
96 /*
97  * Reset the PHY.  This PHY completes reset immediately so we never wait.
98  */
99 static int vsc8211_reset(struct cphy *cphy, int wait)
100 {
101 	return t3_phy_reset(cphy, 0, 0);
102 }
103 
104 static int vsc8211_intr_enable(struct cphy *cphy)
105 {
106 	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
107 }
108 
109 static int vsc8211_intr_disable(struct cphy *cphy)
110 {
111 	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
112 }
113 
114 static int vsc8211_intr_clear(struct cphy *cphy)
115 {
116 	u32 val;
117 
118 	/* Clear PHY interrupts by reading the register. */
119 	return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
120 }
121 
122 static int vsc8211_autoneg_enable(struct cphy *cphy)
123 {
124 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
125 				   BMCR_ANENABLE | BMCR_ANRESTART);
126 }
127 
128 static int vsc8211_autoneg_restart(struct cphy *cphy)
129 {
130 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
131 				   BMCR_ANRESTART);
132 }
133 
134 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
135 				     int *speed, int *duplex, int *fc)
136 {
137 	unsigned int bmcr, status, lpa, adv;
138 	int err, sp = -1, dplx = -1, pause = 0;
139 
140 	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
141 	if (!err)
142 		err = mdio_read(cphy, 0, MII_BMSR, &status);
143 	if (err)
144 		return err;
145 
146 	if (link_ok) {
147 		/*
148 		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
149 		 * once more to get the current link state.
150 		 */
151 		if (!(status & BMSR_LSTATUS))
152 			err = mdio_read(cphy, 0, MII_BMSR, &status);
153 		if (err)
154 			return err;
155 		*link_ok = (status & BMSR_LSTATUS) != 0;
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_ok,
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_ok) {
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_ok = (status & BMSR_LSTATUS) != 0;
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 int t3_vsc8211_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
382 			const struct mdio_ops *mdio_ops)
383 {
384 	int err;
385 	unsigned int val;
386 
387 	cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops,
388 		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
389 		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
390 		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
391 	msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
392 
393 	err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
394 	if (err)
395 		return err;
396 	if (val & VSC_CTRL_MEDIA_MODE_HI)
397 		return 0;   /* copper interface, done */
398 
399 	phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
400 		    SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
401 	phy->desc = "1000BASE-X";
402 	phy->ops = &vsc8211_fiber_ops;
403 
404 	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
405 	    (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
406 	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
407 	    (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
408 			      val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
409 	    (err = vsc8211_reset(phy, 0)) != 0)
410 		return err;
411 
412 	udelay(5); /* delay after reset before next SMI */
413 	return 0;
414 }
415