cxgb_vsc8211.c revision 330897
1/**************************************************************************
2SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3
4Copyright (c) 2007, Chelsio Inc.
5All rights reserved.
6
7Redistribution and use in source and binary forms, with or without
8modification, 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
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27POSSIBILITY OF SUCH DAMAGE.
28
29***************************************************************************/
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: stable/11/sys/dev/cxgb/common/cxgb_vsc8211.c 330897 2018-03-14 03:19:51Z eadler $");
33
34#include <cxgb_include.h>
35
36#undef msleep
37#define msleep t3_os_sleep
38
39/* VSC8211 PHY specific registers. */
40enum {
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
51enum {
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
69enum {
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 */
98static int vsc8211_reset(struct cphy *cphy, int wait)
99{
100	return t3_phy_reset(cphy, 0, 0);
101}
102
103static int vsc8211_intr_enable(struct cphy *cphy)
104{
105	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
106}
107
108static int vsc8211_intr_disable(struct cphy *cphy)
109{
110	return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
111}
112
113static 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
121static 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
127static 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
133static 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
206static 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 */
276static 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
289static 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
299static 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
305static 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
323static 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
338static 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
353static 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
367static 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
394int 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
430int 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