xref: /freebsd/sys/dev/cxgb/common/cxgb_vsc8211.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1 /**************************************************************************
2 SPDX-License-Identifier: BSD-2-Clause
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 #include <cxgb_include.h>
33 
34 #undef msleep
35 #define msleep t3_os_sleep
36 
37 /* VSC8211 PHY specific registers. */
38 enum {
39 	VSC8211_SIGDET_CTRL   = 19,
40 	VSC8211_EXT_CTRL      = 23,
41 	VSC8211_PHY_CTRL      = 24,
42 	VSC8211_INTR_ENABLE   = 25,
43 	VSC8211_INTR_STATUS   = 26,
44 	VSC8211_LED_CTRL      = 27,
45 	VSC8211_AUX_CTRL_STAT = 28,
46 	VSC8211_EXT_PAGE_AXS  = 31,
47 };
48 
49 enum {
50 	VSC_INTR_RX_ERR     = 1 << 0,
51 	VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
52 	VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
53 	VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
54 	VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
55 	VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
56 	VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
57 	VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
58 	VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
59 	VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
60 	VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
61 	VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
62 	VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
63 	VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
64 	VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
65 };
66 
67 enum {
68 	VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
69 	VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
70 };
71 
72 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
73 			   VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
74 	 		   VSC_INTR_NEG_DONE)
75 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
76 		   VSC_INTR_ENABLE)
77 
78 /* PHY specific auxiliary control & status register fields */
79 #define S_ACSR_ACTIPHY_TMR    0
80 #define M_ACSR_ACTIPHY_TMR    0x3
81 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
82 
83 #define S_ACSR_SPEED    3
84 #define M_ACSR_SPEED    0x3
85 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
86 
87 #define S_ACSR_DUPLEX 5
88 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
89 
90 #define S_ACSR_ACTIPHY 6
91 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
92 
93 /*
94  * Reset the PHY.  This PHY completes reset immediately so we never wait.
95  */
vsc8211_reset(struct cphy * cphy,int wait)96 static int vsc8211_reset(struct cphy *cphy, int wait)
97 {
98 	return t3_phy_reset(cphy, 0, 0);
99 }
100 
vsc8211_intr_enable(struct cphy * cphy)101 static int vsc8211_intr_enable(struct cphy *cphy)
102 {
103 	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
104 }
105 
vsc8211_intr_disable(struct cphy * cphy)106 static int vsc8211_intr_disable(struct cphy *cphy)
107 {
108 	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
109 }
110 
vsc8211_intr_clear(struct cphy * cphy)111 static int vsc8211_intr_clear(struct cphy *cphy)
112 {
113 	u32 val;
114 
115 	/* Clear PHY interrupts by reading the register. */
116 	return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
117 }
118 
vsc8211_autoneg_enable(struct cphy * cphy)119 static int vsc8211_autoneg_enable(struct cphy *cphy)
120 {
121 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
122 				   BMCR_ANENABLE | BMCR_ANRESTART);
123 }
124 
vsc8211_autoneg_restart(struct cphy * cphy)125 static int vsc8211_autoneg_restart(struct cphy *cphy)
126 {
127 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
128 				   BMCR_ANRESTART);
129 }
130 
vsc8211_get_link_status(struct cphy * cphy,int * link_state,int * speed,int * duplex,int * fc)131 static int vsc8211_get_link_status(struct cphy *cphy, int *link_state,
132 				     int *speed, int *duplex, int *fc)
133 {
134 	unsigned int bmcr, status, lpa, adv;
135 	int err, sp = -1, dplx = -1, pause = 0;
136 
137 	err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
138 	if (!err)
139 		err = mdio_read(cphy, 0, MII_BMSR, &status);
140 	if (err)
141 		return err;
142 
143 	if (link_state) {
144 		/*
145 		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
146 		 * once more to get the current link state.
147 		 */
148 		if (!(status & BMSR_LSTATUS))
149 			err = mdio_read(cphy, 0, MII_BMSR, &status);
150 		if (err)
151 			return err;
152 		*link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
153 		    PHY_LINK_DOWN;
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 
vsc8211_get_link_status_fiber(struct cphy * cphy,int * link_state,int * speed,int * duplex,int * fc)204 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_state,
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_state) {
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_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
226 		    PHY_LINK_DOWN;
227 	}
228 	if (!(bmcr & BMCR_ANENABLE)) {
229 		dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
230 		if (bmcr & BMCR_SPEED1000)
231 			sp = SPEED_1000;
232 		else if (bmcr & BMCR_SPEED100)
233 			sp = SPEED_100;
234 		else
235 			sp = SPEED_10;
236 	} else if (status & BMSR_ANEGCOMPLETE) {
237 		err = mdio_read(cphy, 0, MII_LPA, &lpa);
238 		if (!err)
239 			err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
240 		if (err)
241 			return err;
242 
243 		if (adv & lpa & ADVERTISE_1000XFULL) {
244 			dplx = DUPLEX_FULL;
245 			sp = SPEED_1000;
246 		} else if (adv & lpa & ADVERTISE_1000XHALF) {
247 			dplx = DUPLEX_HALF;
248 			sp = SPEED_1000;
249 		}
250 
251 		if (fc && dplx == DUPLEX_FULL) {
252 			if (lpa & adv & ADVERTISE_1000XPAUSE)
253 				pause = PAUSE_RX | PAUSE_TX;
254 			else if ((lpa & ADVERTISE_1000XPAUSE) &&
255 				 (adv & lpa & ADVERTISE_1000XPSE_ASYM))
256 				pause = PAUSE_TX;
257 			else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
258 				 (adv & ADVERTISE_1000XPAUSE))
259 				pause = PAUSE_RX;
260 		}
261 	}
262 	if (speed)
263 		*speed = sp;
264 	if (duplex)
265 		*duplex = dplx;
266 	if (fc)
267 		*fc = pause;
268 	return 0;
269 }
270 
271 /*
272  * Enable/disable auto MDI/MDI-X in forced link speed mode.
273  */
vsc8211_set_automdi(struct cphy * phy,int enable)274 static int vsc8211_set_automdi(struct cphy *phy, int enable)
275 {
276 	int err;
277 
278 	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
279 	    (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
280 	    (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
281 	    (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
282 	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
283 		return err;
284 	return 0;
285 }
286 
vsc8211_set_speed_duplex(struct cphy * phy,int speed,int duplex)287 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
288 {
289 	int err;
290 
291 	err = t3_set_phy_speed_duplex(phy, speed, duplex);
292 	if (!err)
293 		err = vsc8211_set_automdi(phy, 1);
294 	return err;
295 }
296 
vsc8211_power_down(struct cphy * cphy,int enable)297 static int vsc8211_power_down(struct cphy *cphy, int enable)
298 {
299 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
300 				   enable ? BMCR_PDOWN : 0);
301 }
302 
vsc8211_intr_handler(struct cphy * cphy)303 static int vsc8211_intr_handler(struct cphy *cphy)
304 {
305 	unsigned int cause;
306 	int err, cphy_cause = 0;
307 
308 	err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
309 	if (err)
310 		return err;
311 
312 	cause &= INTR_MASK;
313 	if (cause & CFG_CHG_INTR_MASK)
314 		cphy_cause |= cphy_cause_link_change;
315 	if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
316 		cphy_cause |= cphy_cause_fifo_error;
317 	return cphy_cause;
318 }
319 
320 #ifdef C99_NOT_SUPPORTED
321 static struct cphy_ops vsc8211_ops = {
322 	vsc8211_reset,
323 	vsc8211_intr_enable,
324 	vsc8211_intr_disable,
325 	vsc8211_intr_clear,
326 	vsc8211_intr_handler,
327 	vsc8211_autoneg_enable,
328 	vsc8211_autoneg_restart,
329 	t3_phy_advertise,
330 	NULL,
331 	vsc8211_set_speed_duplex,
332 	vsc8211_get_link_status,
333 	vsc8211_power_down,
334 };
335 
336 static struct cphy_ops vsc8211_fiber_ops = {
337 	vsc8211_reset,
338 	vsc8211_intr_enable,
339 	vsc8211_intr_disable,
340 	vsc8211_intr_clear,
341 	vsc8211_intr_handler,
342 	vsc8211_autoneg_enable,
343 	vsc8211_autoneg_restart,
344 	t3_phy_advertise_fiber,
345 	NULL,
346 	t3_set_phy_speed_duplex,
347 	vsc8211_get_link_status_fiber,
348 	vsc8211_power_down,
349 };
350 #else
351 static struct cphy_ops vsc8211_ops = {
352 	.reset             = vsc8211_reset,
353 	.intr_enable       = vsc8211_intr_enable,
354 	.intr_disable      = vsc8211_intr_disable,
355 	.intr_clear        = vsc8211_intr_clear,
356 	.intr_handler      = vsc8211_intr_handler,
357 	.autoneg_enable    = vsc8211_autoneg_enable,
358 	.autoneg_restart   = vsc8211_autoneg_restart,
359 	.advertise         = t3_phy_advertise,
360 	.set_speed_duplex  = vsc8211_set_speed_duplex,
361 	.get_link_status   = vsc8211_get_link_status,
362 	.power_down        = vsc8211_power_down,
363 };
364 
365 static struct cphy_ops vsc8211_fiber_ops = {
366 	.reset             = vsc8211_reset,
367 	.intr_enable       = vsc8211_intr_enable,
368 	.intr_disable      = vsc8211_intr_disable,
369 	.intr_clear        = vsc8211_intr_clear,
370 	.intr_handler      = vsc8211_intr_handler,
371 	.autoneg_enable    = vsc8211_autoneg_enable,
372 	.autoneg_restart   = vsc8211_autoneg_restart,
373 	.advertise         = t3_phy_advertise_fiber,
374 	.set_speed_duplex  = t3_set_phy_speed_duplex,
375 	.get_link_status   = vsc8211_get_link_status_fiber,
376 	.power_down        = vsc8211_power_down,
377 };
378 #endif
379 
380 #define VSC8211_PHY_CTRL 24
381 
382 #define S_VSC8211_TXFIFODEPTH    7
383 #define M_VSC8211_TXFIFODEPTH    0x7
384 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
385 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
386 
387 #define S_VSC8211_RXFIFODEPTH    4
388 #define M_VSC8211_RXFIFODEPTH    0x7
389 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
390 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
391 
t3_vsc8211_fifo_depth(adapter_t * adap,unsigned int mtu,int port)392 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
393 {
394 	/* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
395 	unsigned int val = 4;
396 	unsigned int currentregval;
397 	unsigned int regval;
398 	int err;
399 
400 	/* Retrieve the port info structure from adater_t */
401 	struct port_info *portinfo = adap2pinfo(adap, port);
402 
403 	/* What phy is this */
404 	struct cphy *phy = &portinfo->phy;
405 
406 	/* Read the current value of the PHY control Register */
407 	err = mdio_read(phy, 0, VSC8211_PHY_CTRL, &currentregval);
408 
409 	if (err)
410 		return err;
411 
412 	/* IEEE mode supports up to 1518 bytes */
413 	/* mtu does not contain the header + FCS (18 bytes) */
414 	if (mtu > 1500)
415 		/*
416 		 * If using a packet size > 1500  set TX FIFO Depth bits
417 		 * 9:7 to 011 (Jumbo packet mode)
418 		 */
419 		val = 3;
420 
421 	regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) |
422 		(currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
423 		~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
424 
425 	return  mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
426 }
427 
t3_vsc8211_phy_prep(pinfo_t * pinfo,int phy_addr,const struct mdio_ops * mdio_ops)428 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
429 			const struct mdio_ops *mdio_ops)
430 {
431 	struct cphy *phy = &pinfo->phy;
432 	int err;
433 	unsigned int val;
434 
435 	cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
436 		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
437 		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
438 		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
439 	msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
440 
441 	err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
442 	if (err)
443 		return err;
444 	if (val & VSC_CTRL_MEDIA_MODE_HI) {
445 		/* copper interface, just need to configure the LEDs */
446 		return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
447 	}
448 
449 	phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
450 		    SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
451 	phy->desc = "1000BASE-X";
452 	phy->ops = &vsc8211_fiber_ops;
453 
454 	if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
455 	    (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
456 	    (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
457 	    (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
458 			      val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
459 	    (err = vsc8211_reset(phy, 0)) != 0)
460 		return err;
461 
462 	udelay(5); /* delay after reset before next SMI */
463 	return 0;
464 }
465