xref: /onnv-gate/usr/src/uts/common/io/bge/bge_mii.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include "sys/bge_impl.h"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate /*
32*0Sstevel@tonic-gate  * Bit test macros, returning boolean_t values
33*0Sstevel@tonic-gate  */
34*0Sstevel@tonic-gate #define	BIS(w, b)	(((w) & (b)) ? B_TRUE : B_FALSE)
35*0Sstevel@tonic-gate #define	BIC(w, b)	(((w) & (b)) ? B_FALSE : B_TRUE)
36*0Sstevel@tonic-gate #define	UPORDOWN(x)	((x) ? "up" : "down")
37*0Sstevel@tonic-gate 
38*0Sstevel@tonic-gate /*
39*0Sstevel@tonic-gate  * ========== Copper (PHY) support ==========
40*0Sstevel@tonic-gate  */
41*0Sstevel@tonic-gate 
42*0Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_PHY	/* debug flag for this code	*/
43*0Sstevel@tonic-gate 
44*0Sstevel@tonic-gate /*
45*0Sstevel@tonic-gate  * #defines:
46*0Sstevel@tonic-gate  *	BGE_COPPER_WIRESPEED controls whether the Broadcom WireSpeed(tm)
47*0Sstevel@tonic-gate  *	feature is enabled.  We need to recheck whether this can be
48*0Sstevel@tonic-gate  *	enabled; at one time it seemed to interact unpleasantly with the
49*0Sstevel@tonic-gate  *	loopback modes.
50*0Sstevel@tonic-gate  *
51*0Sstevel@tonic-gate  *	BGE_COPPER_IDLEOFF controls whether the (copper) PHY power is
52*0Sstevel@tonic-gate  *	turned off when the PHY is idled i.e. during driver suspend().
53*0Sstevel@tonic-gate  *	For now this is disabled because the chip doesn't seem to
54*0Sstevel@tonic-gate  *	resume cleanly if the PHY power is turned off.
55*0Sstevel@tonic-gate  */
56*0Sstevel@tonic-gate #define	BGE_COPPER_WIRESPEED	B_TRUE
57*0Sstevel@tonic-gate #define	BGE_COPPER_IDLEOFF	B_FALSE
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate /*
60*0Sstevel@tonic-gate  * The arrays below can be indexed by the MODE bits from the Auxiliary
61*0Sstevel@tonic-gate  * Status register to determine the current speed/duplex settings.
62*0Sstevel@tonic-gate  */
63*0Sstevel@tonic-gate static const int16_t bge_copper_link_speed[] = {
64*0Sstevel@tonic-gate 	0,				/* MII_AUX_STATUS_MODE_NONE	*/
65*0Sstevel@tonic-gate 	10,				/* MII_AUX_STATUS_MODE_10_H	*/
66*0Sstevel@tonic-gate 	10,				/* MII_AUX_STATUS_MODE_10_F	*/
67*0Sstevel@tonic-gate 	100,				/* MII_AUX_STATUS_MODE_100_H	*/
68*0Sstevel@tonic-gate 	0,				/* MII_AUX_STATUS_MODE_100_4	*/
69*0Sstevel@tonic-gate 	100,				/* MII_AUX_STATUS_MODE_100_F	*/
70*0Sstevel@tonic-gate 	1000,				/* MII_AUX_STATUS_MODE_1000_H	*/
71*0Sstevel@tonic-gate 	1000				/* MII_AUX_STATUS_MODE_1000_F	*/
72*0Sstevel@tonic-gate };
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate static const int8_t bge_copper_link_duplex[] = {
75*0Sstevel@tonic-gate 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_NONE	*/
76*0Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_10_H	*/
77*0Sstevel@tonic-gate 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_10_F	*/
78*0Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_100_H	*/
79*0Sstevel@tonic-gate 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_100_4	*/
80*0Sstevel@tonic-gate 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_100_F	*/
81*0Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_1000_H	*/
82*0Sstevel@tonic-gate 	LINK_DUPLEX_FULL		/* MII_AUX_STATUS_MODE_1000_F	*/
83*0Sstevel@tonic-gate };
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate static const char * const bge_copper_link_text[] = {
86*0Sstevel@tonic-gate 	"down",				/* MII_AUX_STATUS_MODE_NONE	*/
87*0Sstevel@tonic-gate 	"up 10Mbps Half-Duplex",	/* MII_AUX_STATUS_MODE_10_H	*/
88*0Sstevel@tonic-gate 	"up 10Mbps Full-Duplex",	/* MII_AUX_STATUS_MODE_10_F	*/
89*0Sstevel@tonic-gate 	"up 100Mbps Half-Duplex",	/* MII_AUX_STATUS_MODE_100_H	*/
90*0Sstevel@tonic-gate 	"down",				/* MII_AUX_STATUS_MODE_100_4	*/
91*0Sstevel@tonic-gate 	"up 100Mbps Full-Duplex",	/* MII_AUX_STATUS_MODE_100_F	*/
92*0Sstevel@tonic-gate 	"up 1000Mbps Half-Duplex",	/* MII_AUX_STATUS_MODE_1000_H	*/
93*0Sstevel@tonic-gate 	"up 1000Mbps Full-Duplex"	/* MII_AUX_STATUS_MODE_1000_F	*/
94*0Sstevel@tonic-gate };
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate #if	BGE_DEBUGGING
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate static void
99*0Sstevel@tonic-gate bge_phydump(bge_t *bgep, uint16_t mii_status, uint16_t aux)
100*0Sstevel@tonic-gate {
101*0Sstevel@tonic-gate 	uint16_t regs[32];
102*0Sstevel@tonic-gate 	int i;
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate 	for (i = 0; i < 32; ++i)
107*0Sstevel@tonic-gate 		switch (i) {
108*0Sstevel@tonic-gate 		default:
109*0Sstevel@tonic-gate 			regs[i] = bge_mii_get16(bgep, i);
110*0Sstevel@tonic-gate 			break;
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate 		case MII_STATUS:
113*0Sstevel@tonic-gate 			regs[i] = mii_status;
114*0Sstevel@tonic-gate 			break;
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate 		case MII_AUX_STATUS:
117*0Sstevel@tonic-gate 			regs[i] = aux;
118*0Sstevel@tonic-gate 			break;
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 		case 0x0b: case 0x0c: case 0x0d: case 0x0e:
121*0Sstevel@tonic-gate 		case 0x15: case 0x16: case 0x17:
122*0Sstevel@tonic-gate 		case 0x1c:
123*0Sstevel@tonic-gate 		case 0x1f:
124*0Sstevel@tonic-gate 			/* reserved registers -- don't read these */
125*0Sstevel@tonic-gate 			regs[i] = 0;
126*0Sstevel@tonic-gate 			break;
127*0Sstevel@tonic-gate 		}
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 	for (i = 0; i < 32; i += 8)
130*0Sstevel@tonic-gate 		BGE_DEBUG(("bge_phydump: "
131*0Sstevel@tonic-gate 				"0x%04x %04x %04x %04x %04x %04x %04x %04x",
132*0Sstevel@tonic-gate 			regs[i+0], regs[i+1], regs[i+2], regs[i+3],
133*0Sstevel@tonic-gate 			regs[i+4], regs[i+5], regs[i+6], regs[i+7]));
134*0Sstevel@tonic-gate }
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate #endif	/* BGE_DEBUGGING */
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate /*
139*0Sstevel@tonic-gate  * Basic low-level function to probe for a PHY
140*0Sstevel@tonic-gate  *
141*0Sstevel@tonic-gate  * Returns TRUE if the PHY responds with valid data, FALSE otherwise
142*0Sstevel@tonic-gate  */
143*0Sstevel@tonic-gate static boolean_t
144*0Sstevel@tonic-gate bge_phy_probe(bge_t *bgep)
145*0Sstevel@tonic-gate {
146*0Sstevel@tonic-gate 	uint16_t phy_status;
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_probe($%p)", (void *)bgep));
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate 	/*
153*0Sstevel@tonic-gate 	 * Read the MII_STATUS register twice, in
154*0Sstevel@tonic-gate 	 * order to clear any sticky bits (but they should
155*0Sstevel@tonic-gate 	 * have been cleared by the RESET, I think).
156*0Sstevel@tonic-gate 	 */
157*0Sstevel@tonic-gate 	phy_status = bge_mii_get16(bgep, MII_STATUS);
158*0Sstevel@tonic-gate 	phy_status = bge_mii_get16(bgep, MII_STATUS);
159*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_phy_probe: status 0x%x", phy_status));
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 	/*
162*0Sstevel@tonic-gate 	 * Now check the value read; it should have at least one bit set
163*0Sstevel@tonic-gate 	 * (for the device capabilities) and at least one clear (one of
164*0Sstevel@tonic-gate 	 * the error bits). So if we see all 0s or all 1s, there's a
165*0Sstevel@tonic-gate 	 * problem.  In particular, bge_mii_get16() returns all 1s if
166*0Sstevel@tonic-gate 	 * communications fails ...
167*0Sstevel@tonic-gate 	 */
168*0Sstevel@tonic-gate 	switch (phy_status) {
169*0Sstevel@tonic-gate 	case 0x0000:
170*0Sstevel@tonic-gate 	case 0xffff:
171*0Sstevel@tonic-gate 		return (B_FALSE);
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 	default :
174*0Sstevel@tonic-gate 		return (B_TRUE);
175*0Sstevel@tonic-gate 	}
176*0Sstevel@tonic-gate }
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate /*
179*0Sstevel@tonic-gate  * Basic low-level function to reset the PHY.
180*0Sstevel@tonic-gate  * Doesn't incorporate any special-case workarounds.
181*0Sstevel@tonic-gate  *
182*0Sstevel@tonic-gate  * Returns TRUE on success, FALSE if the RESET bit doesn't clear
183*0Sstevel@tonic-gate  */
184*0Sstevel@tonic-gate static boolean_t
185*0Sstevel@tonic-gate bge_phy_reset(bge_t *bgep)
186*0Sstevel@tonic-gate {
187*0Sstevel@tonic-gate 	uint16_t control;
188*0Sstevel@tonic-gate 	uint_t count;
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_reset($%p)", (void *)bgep));
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate 	/*
195*0Sstevel@tonic-gate 	 * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear
196*0Sstevel@tonic-gate 	 */
197*0Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_RESET);
198*0Sstevel@tonic-gate 	for (count = 0; ++count < 1000; ) {
199*0Sstevel@tonic-gate 		drv_usecwait(5);
200*0Sstevel@tonic-gate 		control = bge_mii_get16(bgep, MII_CONTROL);
201*0Sstevel@tonic-gate 		if (BIC(control, MII_CONTROL_RESET))
202*0Sstevel@tonic-gate 			return (B_TRUE);
203*0Sstevel@tonic-gate 	}
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_phy_reset: FAILED, control now 0x%x", control));
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate 	return (B_FALSE);
208*0Sstevel@tonic-gate }
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate /*
211*0Sstevel@tonic-gate  * Basic low-level function to powerdown the PHY, if supported
212*0Sstevel@tonic-gate  * If powerdown support is compiled out, this function does nothing.
213*0Sstevel@tonic-gate  */
214*0Sstevel@tonic-gate static void
215*0Sstevel@tonic-gate bge_phy_powerdown(bge_t *bgep)
216*0Sstevel@tonic-gate {
217*0Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_powerdown"));
218*0Sstevel@tonic-gate #if	BGE_COPPER_IDLEOFF
219*0Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_PWRDN);
220*0Sstevel@tonic-gate #endif	/* BGE_COPPER_IDLEOFF */
221*0Sstevel@tonic-gate }
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate /*
224*0Sstevel@tonic-gate  * The following functions are based on sample code provided by
225*0Sstevel@tonic-gate  * Broadcom (20-June-2003), and implement workarounds said to be
226*0Sstevel@tonic-gate  * required on the early revisions of the BCM5703/4C.
227*0Sstevel@tonic-gate  *
228*0Sstevel@tonic-gate  * The registers and values used are mostly UNDOCUMENTED, and
229*0Sstevel@tonic-gate  * therefore don't have symbolic names ;-(
230*0Sstevel@tonic-gate  *
231*0Sstevel@tonic-gate  * Many of the comments are straight out of the Broadcom code:
232*0Sstevel@tonic-gate  * even where the code has been restructured, the original
233*0Sstevel@tonic-gate  * comments have been preserved in order to explain what these
234*0Sstevel@tonic-gate  * undocumented registers & values are all about ...
235*0Sstevel@tonic-gate  */
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate static void
238*0Sstevel@tonic-gate bge_phy_macro_wait(bge_t *bgep)
239*0Sstevel@tonic-gate {
240*0Sstevel@tonic-gate 	uint_t count;
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	for (count = 100; --count; )
243*0Sstevel@tonic-gate 		if ((bge_mii_get16(bgep, 0x16) & 0x1000) == 0)
244*0Sstevel@tonic-gate 			break;
245*0Sstevel@tonic-gate }
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate /*
248*0Sstevel@tonic-gate  * PHY test data pattern:
249*0Sstevel@tonic-gate  *
250*0Sstevel@tonic-gate  * For 5703/04, each DFE TAP has 21-bits (low word 15, hi word 6)
251*0Sstevel@tonic-gate  * For 5705,    each DFE TAP has 19-bits (low word 15, hi word 4)
252*0Sstevel@tonic-gate  * For simplicity, we check only 19-bits, so we don't have to
253*0Sstevel@tonic-gate  * distinguish which chip it is.
254*0Sstevel@tonic-gate  * the LO word contains 15 bits, make sure pattern data is < 0x7fff
255*0Sstevel@tonic-gate  * the HI word contains  6 bits, make sure pattern data is < 0x003f
256*0Sstevel@tonic-gate  */
257*0Sstevel@tonic-gate #define	N_CHANNELS	4
258*0Sstevel@tonic-gate #define	N_TAPS		3
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate static struct {
261*0Sstevel@tonic-gate 	uint16_t	lo;
262*0Sstevel@tonic-gate 	uint16_t	hi;
263*0Sstevel@tonic-gate } tap_data[N_CHANNELS][N_TAPS] = {
264*0Sstevel@tonic-gate 	{
265*0Sstevel@tonic-gate 		{ 0x5555, 0x0005 },	/* ch0, TAP 0, LO/HI pattern */
266*0Sstevel@tonic-gate 		{ 0x2aaa, 0x000a },	/* ch0, TAP 1, LO/HI pattern */
267*0Sstevel@tonic-gate 		{ 0x3456, 0x0003 }	/* ch0, TAP 2, LO/HI pattern */
268*0Sstevel@tonic-gate 	},
269*0Sstevel@tonic-gate 	{
270*0Sstevel@tonic-gate 		{ 0x2aaa, 0x000a },	/* ch1, TAP 0, LO/HI pattern */
271*0Sstevel@tonic-gate 		{ 0x3333, 0x0003 },	/* ch1, TAP 1, LO/HI pattern */
272*0Sstevel@tonic-gate 		{ 0x789a, 0x0005 }	/* ch1, TAP 2, LO/HI pattern */
273*0Sstevel@tonic-gate 	},
274*0Sstevel@tonic-gate 	{
275*0Sstevel@tonic-gate 		{ 0x5a5a, 0x0005 },	/* ch2, TAP 0, LO/HI pattern */
276*0Sstevel@tonic-gate 		{ 0x2a6a, 0x000a },	/* ch2, TAP 1, LO/HI pattern */
277*0Sstevel@tonic-gate 		{ 0x1bcd, 0x0003 }	/* ch2, TAP 2, LO/HI pattern */
278*0Sstevel@tonic-gate 	},
279*0Sstevel@tonic-gate 	{
280*0Sstevel@tonic-gate 		{ 0x2a5a, 0x000a },	/* ch3, TAP 0, LO/HI pattern */
281*0Sstevel@tonic-gate 		{ 0x33c3, 0x0003 },	/* ch3, TAP 1, LO/HI pattern */
282*0Sstevel@tonic-gate 		{ 0x2ef1, 0x0005 }	/* ch3, TAP 2, LO/HI pattern */
283*0Sstevel@tonic-gate 	}
284*0Sstevel@tonic-gate };
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate /*
287*0Sstevel@tonic-gate  * Check whether the PHY has locked up after a RESET.
288*0Sstevel@tonic-gate  *
289*0Sstevel@tonic-gate  * Returns TRUE if it did, FALSE is it's OK ;-)
290*0Sstevel@tonic-gate  */
291*0Sstevel@tonic-gate static boolean_t
292*0Sstevel@tonic-gate bge_phy_locked_up(bge_t *bgep)
293*0Sstevel@tonic-gate {
294*0Sstevel@tonic-gate 	uint16_t dataLo;
295*0Sstevel@tonic-gate 	uint16_t dataHi;
296*0Sstevel@tonic-gate 	uint_t chan;
297*0Sstevel@tonic-gate 	uint_t tap;
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate 	/*
300*0Sstevel@tonic-gate 	 * Check TAPs for all 4 channels, as soon as we see a lockup
301*0Sstevel@tonic-gate 	 * we'll stop checking.
302*0Sstevel@tonic-gate 	 */
303*0Sstevel@tonic-gate 	for (chan = 0; chan < N_CHANNELS; ++chan) {
304*0Sstevel@tonic-gate 		/* Select channel and set TAP index to 0 */
305*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
306*0Sstevel@tonic-gate 		/* Freeze filter again just to be safe */
307*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0002);
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 		/*
310*0Sstevel@tonic-gate 		 * Write fixed pattern to the RAM, 3 TAPs for
311*0Sstevel@tonic-gate 		 * each channel, each TAP have 2 WORDs (LO/HI)
312*0Sstevel@tonic-gate 		 */
313*0Sstevel@tonic-gate 		for (tap = 0; tap < N_TAPS; ++tap) {
314*0Sstevel@tonic-gate 			bge_mii_put16(bgep, 0x15, tap_data[chan][tap].lo);
315*0Sstevel@tonic-gate 			bge_mii_put16(bgep, 0x15, tap_data[chan][tap].hi);
316*0Sstevel@tonic-gate 		}
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 		/*
319*0Sstevel@tonic-gate 		 * Active PHY's Macro operation to write DFE
320*0Sstevel@tonic-gate 		 * TAP from RAM, and wait for Macro to complete.
321*0Sstevel@tonic-gate 		 */
322*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0202);
323*0Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 		/*
326*0Sstevel@tonic-gate 		 * Done with write phase, now begin read phase.
327*0Sstevel@tonic-gate 		 */
328*0Sstevel@tonic-gate 
329*0Sstevel@tonic-gate 		/* Select channel and set TAP index to 0 */
330*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 		/*
333*0Sstevel@tonic-gate 		 * Active PHY's Macro operation to load DFE
334*0Sstevel@tonic-gate 		 * TAP to RAM, and wait for Macro to complete
335*0Sstevel@tonic-gate 		 */
336*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0082);
337*0Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 		/* Enable "pre-fetch" */
340*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0802);
341*0Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 		/*
344*0Sstevel@tonic-gate 		 * Read back the TAP values.  3 TAPs for each
345*0Sstevel@tonic-gate 		 * channel, each TAP have 2 WORDs (LO/HI)
346*0Sstevel@tonic-gate 		 */
347*0Sstevel@tonic-gate 		for (tap = 0; tap < N_TAPS; ++tap) {
348*0Sstevel@tonic-gate 			/*
349*0Sstevel@tonic-gate 			 * Read Lo/Hi then wait for 'done' is faster.
350*0Sstevel@tonic-gate 			 * For DFE TAP, the HI word contains 6 bits,
351*0Sstevel@tonic-gate 			 * LO word contains 15 bits
352*0Sstevel@tonic-gate 			 */
353*0Sstevel@tonic-gate 			dataLo = bge_mii_get16(bgep, 0x15) & 0x7fff;
354*0Sstevel@tonic-gate 			dataHi = bge_mii_get16(bgep, 0x15) & 0x003f;
355*0Sstevel@tonic-gate 			bge_phy_macro_wait(bgep);
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate 			/*
358*0Sstevel@tonic-gate 			 * Check if what we wrote is what we read back.
359*0Sstevel@tonic-gate 			 * If failed, then the PHY is locked up, we need
360*0Sstevel@tonic-gate 			 * to do PHY reset again
361*0Sstevel@tonic-gate 			 */
362*0Sstevel@tonic-gate 			if (dataLo != tap_data[chan][tap].lo)
363*0Sstevel@tonic-gate 				return (B_TRUE);	/* wedged!	*/
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate 			if (dataHi != tap_data[chan][tap].hi)
366*0Sstevel@tonic-gate 				return (B_TRUE);	/* wedged!	*/
367*0Sstevel@tonic-gate 		}
368*0Sstevel@tonic-gate 	}
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 	/*
371*0Sstevel@tonic-gate 	 * The PHY isn't locked up ;-)
372*0Sstevel@tonic-gate 	 */
373*0Sstevel@tonic-gate 	return (B_FALSE);
374*0Sstevel@tonic-gate }
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate /*
377*0Sstevel@tonic-gate  * Special-case code to reset the PHY on the 5702/5703/5704C/5705/5782.
378*0Sstevel@tonic-gate  * Tries up to 5 times to recover from failure to reset or PHY lockup.
379*0Sstevel@tonic-gate  *
380*0Sstevel@tonic-gate  * Returns TRUE on success, FALSE if there's an unrecoverable problem
381*0Sstevel@tonic-gate  */
382*0Sstevel@tonic-gate static boolean_t
383*0Sstevel@tonic-gate bge_phy_reset_and_check(bge_t *bgep)
384*0Sstevel@tonic-gate {
385*0Sstevel@tonic-gate 	boolean_t reset_success;
386*0Sstevel@tonic-gate 	boolean_t phy_locked;
387*0Sstevel@tonic-gate 	uint16_t extctrl;
388*0Sstevel@tonic-gate 	uint_t retries;
389*0Sstevel@tonic-gate 
390*0Sstevel@tonic-gate 	for (retries = 0; retries < 5; ++retries) {
391*0Sstevel@tonic-gate 		/* Issue a phy reset, and wait for reset to complete */
392*0Sstevel@tonic-gate 		/* Assuming reset is successful first */
393*0Sstevel@tonic-gate 		reset_success = bge_phy_reset(bgep);
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate 		/*
396*0Sstevel@tonic-gate 		 * Now go check the DFE TAPs to see if locked up, but
397*0Sstevel@tonic-gate 		 * first, we need to set up PHY so we can read DFE
398*0Sstevel@tonic-gate 		 * TAPs.
399*0Sstevel@tonic-gate 		 */
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate 		/*
402*0Sstevel@tonic-gate 		 * Disable Transmitter and Interrupt, while we play
403*0Sstevel@tonic-gate 		 * with the PHY registers, so the link partner won't
404*0Sstevel@tonic-gate 		 * see any strange data and the Driver won't see any
405*0Sstevel@tonic-gate 		 * interrupts.
406*0Sstevel@tonic-gate 		 */
407*0Sstevel@tonic-gate 		extctrl = bge_mii_get16(bgep, 0x10);
408*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x10, extctrl | 0x3000);
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 		/* Setup Full-Duplex, 1000 mbps */
411*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x0, 0x0140);
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate 		/* Set to Master mode */
414*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x9, 0x1800);
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 		/* Enable SM_DSP_CLOCK & 6dB */
417*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x18, 0x0c00);	/* "the ADC fix" */
418*0Sstevel@tonic-gate 
419*0Sstevel@tonic-gate 		/* Work-arounds */
420*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x201f);
421*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x2aaa);
422*0Sstevel@tonic-gate 
423*0Sstevel@tonic-gate 		/* More workarounds */
424*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x000a);
425*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x0323);	/* "the Gamma fix" */
426*0Sstevel@tonic-gate 
427*0Sstevel@tonic-gate 		/* Blocks the PHY control access */
428*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x8005);
429*0Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x0800);
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate 		/* Test whether PHY locked up ;-( */
432*0Sstevel@tonic-gate 		phy_locked = bge_phy_locked_up(bgep);
433*0Sstevel@tonic-gate 		if (reset_success && !phy_locked)
434*0Sstevel@tonic-gate 			break;
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 		/*
437*0Sstevel@tonic-gate 		 * Some problem here ... log it & retry
438*0Sstevel@tonic-gate 		 */
439*0Sstevel@tonic-gate 		if (!reset_success)
440*0Sstevel@tonic-gate 			BGE_REPORT((bgep, "PHY didn't reset!"));
441*0Sstevel@tonic-gate 		if (phy_locked)
442*0Sstevel@tonic-gate 			BGE_REPORT((bgep, "PHY locked up!"));
443*0Sstevel@tonic-gate 	}
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 	/* Remove block phy control */
446*0Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x17, 0x8005);
447*0Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x15, 0x0000);
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 	/* Unfreeze DFE TAP filter for all channels */
450*0Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x17, 0x8200);
451*0Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x16, 0x0000);
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 	/* Restore PHY back to operating state */
454*0Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x18, 0x0400);
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 	/* Enable transmitter and interrupt */
457*0Sstevel@tonic-gate 	extctrl = bge_mii_get16(bgep, 0x10);
458*0Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x10, extctrl & ~0x3000);
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	return (reset_success && !phy_locked);
461*0Sstevel@tonic-gate }
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate static void
464*0Sstevel@tonic-gate bge_phy_tweak_gmii(bge_t *bgep)
465*0Sstevel@tonic-gate {
466*0Sstevel@tonic-gate 	/* Tweak GMII timing */
467*0Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x1c, 0x8d68);
468*0Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x1c, 0x8d68);
469*0Sstevel@tonic-gate }
470*0Sstevel@tonic-gate 
471*0Sstevel@tonic-gate /*
472*0Sstevel@tonic-gate  * End of Broadcom-derived workaround code				*
473*0Sstevel@tonic-gate  */
474*0Sstevel@tonic-gate 
475*0Sstevel@tonic-gate static void
476*0Sstevel@tonic-gate bge_restart_copper(bge_t *bgep, boolean_t powerdown)
477*0Sstevel@tonic-gate {
478*0Sstevel@tonic-gate 	uint16_t phy_status;
479*0Sstevel@tonic-gate 	boolean_t reset_ok;
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	BGE_TRACE(("bge_restart_copper($%p, %d)", (void *)bgep, powerdown));
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 	switch (MHCR_CHIP_ASIC_REV(bgep->chipid.asic_rev)) {
486*0Sstevel@tonic-gate 	default:
487*0Sstevel@tonic-gate 		/*
488*0Sstevel@tonic-gate 		 * Shouldn't happen; it means we don't recognise this chip.
489*0Sstevel@tonic-gate 		 * It's probably a new one, so we'll try our best anyway ...
490*0Sstevel@tonic-gate 		 */
491*0Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5703:
492*0Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5704:
493*0Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5705:
494*0Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5721_5751:
495*0Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5714:
496*0Sstevel@tonic-gate 		reset_ok = bge_phy_reset_and_check(bgep);
497*0Sstevel@tonic-gate 		break;
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5700:
500*0Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5701:
501*0Sstevel@tonic-gate 		/*
502*0Sstevel@tonic-gate 		 * Just a plain reset; the "check" code breaks these chips
503*0Sstevel@tonic-gate 		 */
504*0Sstevel@tonic-gate 		reset_ok = bge_phy_reset(bgep);
505*0Sstevel@tonic-gate 		break;
506*0Sstevel@tonic-gate 	}
507*0Sstevel@tonic-gate 	if (!reset_ok)
508*0Sstevel@tonic-gate 		bge_problem(bgep, "PHY failed to reset correctly");
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 	/*
511*0Sstevel@tonic-gate 	 * Step 5: disable WOL (not required after RESET)
512*0Sstevel@tonic-gate 	 *
513*0Sstevel@tonic-gate 	 * Step 6: refer to errata
514*0Sstevel@tonic-gate 	 */
515*0Sstevel@tonic-gate 	switch (bgep->chipid.asic_rev) {
516*0Sstevel@tonic-gate 	default:
517*0Sstevel@tonic-gate 		break;
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 	case MHCR_CHIP_REV_5704_A0:
520*0Sstevel@tonic-gate 		bge_phy_tweak_gmii(bgep);
521*0Sstevel@tonic-gate 		break;
522*0Sstevel@tonic-gate 	}
523*0Sstevel@tonic-gate 
524*0Sstevel@tonic-gate 	/*
525*0Sstevel@tonic-gate 	 * Step 7: read the MII_INTR_STATUS register twice,
526*0Sstevel@tonic-gate 	 * in order to clear any sticky bits (but they should
527*0Sstevel@tonic-gate 	 * have been cleared by the RESET, I think), and we're
528*0Sstevel@tonic-gate 	 * not using PHY interrupts anyway.
529*0Sstevel@tonic-gate 	 *
530*0Sstevel@tonic-gate 	 * Step 8: enable the PHY to interrupt on link status
531*0Sstevel@tonic-gate 	 * change (not required)
532*0Sstevel@tonic-gate 	 *
533*0Sstevel@tonic-gate 	 * Step 9: configure PHY LED Mode - not applicable?
534*0Sstevel@tonic-gate 	 *
535*0Sstevel@tonic-gate 	 * Step 10: read the MII_STATUS register twice, in
536*0Sstevel@tonic-gate 	 * order to clear any sticky bits (but they should
537*0Sstevel@tonic-gate 	 * have been cleared by the RESET, I think).
538*0Sstevel@tonic-gate 	 */
539*0Sstevel@tonic-gate 	phy_status = bge_mii_get16(bgep, MII_STATUS);
540*0Sstevel@tonic-gate 	phy_status = bge_mii_get16(bgep, MII_STATUS);
541*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_restart_copper: status 0x%x", phy_status));
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate 	/*
544*0Sstevel@tonic-gate 	 * Finally, shut down the PHY, if required
545*0Sstevel@tonic-gate 	 */
546*0Sstevel@tonic-gate 	if (powerdown)
547*0Sstevel@tonic-gate 		bge_phy_powerdown(bgep);
548*0Sstevel@tonic-gate }
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate /*
551*0Sstevel@tonic-gate  * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities
552*0Sstevel@tonic-gate  * and advertisements with the required settings as specified by the various
553*0Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
554*0Sstevel@tonic-gate  *
555*0Sstevel@tonic-gate  * We always reset the PHY and reprogram *all* the relevant registers,
556*0Sstevel@tonic-gate  * not just those changed.  This should cause the link to go down, and then
557*0Sstevel@tonic-gate  * back up again once the link is stable and autonegotiation (if enabled)
558*0Sstevel@tonic-gate  * is complete.  We should get a link state change interrupt somewhere along
559*0Sstevel@tonic-gate  * the way ...
560*0Sstevel@tonic-gate  *
561*0Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
562*0Sstevel@tonic-gate  */
563*0Sstevel@tonic-gate static void
564*0Sstevel@tonic-gate bge_update_copper(bge_t *bgep)
565*0Sstevel@tonic-gate {
566*0Sstevel@tonic-gate 	boolean_t adv_autoneg;
567*0Sstevel@tonic-gate 	boolean_t adv_pause;
568*0Sstevel@tonic-gate 	boolean_t adv_asym_pause;
569*0Sstevel@tonic-gate 	boolean_t adv_1000fdx;
570*0Sstevel@tonic-gate 	boolean_t adv_1000hdx;
571*0Sstevel@tonic-gate 	boolean_t adv_100fdx;
572*0Sstevel@tonic-gate 	boolean_t adv_100hdx;
573*0Sstevel@tonic-gate 	boolean_t adv_10fdx;
574*0Sstevel@tonic-gate 	boolean_t adv_10hdx;
575*0Sstevel@tonic-gate 
576*0Sstevel@tonic-gate 	uint16_t control;
577*0Sstevel@tonic-gate 	uint16_t gigctrl;
578*0Sstevel@tonic-gate 	uint16_t auxctrl;
579*0Sstevel@tonic-gate 	uint16_t anar;
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate 	BGE_TRACE(("bge_update_copper($%p)", (void *)bgep));
582*0Sstevel@tonic-gate 
583*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: autoneg %d "
586*0Sstevel@tonic-gate 			"pause %d asym_pause %d "
587*0Sstevel@tonic-gate 			"1000fdx %d 1000hdx %d "
588*0Sstevel@tonic-gate 			"100fdx %d 100hdx %d "
589*0Sstevel@tonic-gate 			"10fdx %d 10hdx %d ",
590*0Sstevel@tonic-gate 		bgep->param_adv_autoneg,
591*0Sstevel@tonic-gate 		bgep->param_adv_pause, bgep->param_adv_asym_pause,
592*0Sstevel@tonic-gate 		bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
593*0Sstevel@tonic-gate 		bgep->param_adv_100fdx, bgep->param_adv_100hdx,
594*0Sstevel@tonic-gate 		bgep->param_adv_10fdx, bgep->param_adv_10hdx));
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate 	control = gigctrl = auxctrl = anar = 0;
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 	/*
599*0Sstevel@tonic-gate 	 * PHY settings are normally based on the param_* variables,
600*0Sstevel@tonic-gate 	 * but if any loopback mode is in effect, that takes precedence.
601*0Sstevel@tonic-gate 	 *
602*0Sstevel@tonic-gate 	 * BGE supports MAC-internal loopback, PHY-internal loopback,
603*0Sstevel@tonic-gate 	 * and External loopback at a variety of speeds (with a special
604*0Sstevel@tonic-gate 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
605*0Sstevel@tonic-gate 	 * is turned ON, and the speed/mastership is forced.
606*0Sstevel@tonic-gate 	 */
607*0Sstevel@tonic-gate 	switch (bgep->param_loop_mode) {
608*0Sstevel@tonic-gate 	case BGE_LOOP_NONE:
609*0Sstevel@tonic-gate 	default:
610*0Sstevel@tonic-gate 		adv_autoneg = bgep->param_adv_autoneg;
611*0Sstevel@tonic-gate 		adv_pause = bgep->param_adv_pause;
612*0Sstevel@tonic-gate 		adv_asym_pause = bgep->param_adv_asym_pause;
613*0Sstevel@tonic-gate 		adv_1000fdx = bgep->param_adv_1000fdx;
614*0Sstevel@tonic-gate 		adv_1000hdx = bgep->param_adv_1000hdx;
615*0Sstevel@tonic-gate 		adv_100fdx = bgep->param_adv_100fdx;
616*0Sstevel@tonic-gate 		adv_100hdx = bgep->param_adv_100hdx;
617*0Sstevel@tonic-gate 		adv_10fdx = bgep->param_adv_10fdx;
618*0Sstevel@tonic-gate 		adv_10hdx = bgep->param_adv_10hdx;
619*0Sstevel@tonic-gate 		break;
620*0Sstevel@tonic-gate 
621*0Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_1000:
622*0Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_100:
623*0Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_10:
624*0Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_PHY:
625*0Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_MAC:
626*0Sstevel@tonic-gate 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
627*0Sstevel@tonic-gate 		adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE;
628*0Sstevel@tonic-gate 		adv_1000hdx = adv_100hdx = adv_10hdx = B_FALSE;
629*0Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_FULL;
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate 		switch (bgep->param_loop_mode) {
632*0Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_1000:
633*0Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
634*0Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
635*0Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
636*0Sstevel@tonic-gate 			gigctrl |= MII_1000BT_CTL_MASTER_CFG;
637*0Sstevel@tonic-gate 			gigctrl |= MII_1000BT_CTL_MASTER_SEL;
638*0Sstevel@tonic-gate 			break;
639*0Sstevel@tonic-gate 
640*0Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_100:
641*0Sstevel@tonic-gate 			bgep->param_link_speed = 100;
642*0Sstevel@tonic-gate 			adv_100fdx = B_TRUE;
643*0Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
644*0Sstevel@tonic-gate 			break;
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_10:
647*0Sstevel@tonic-gate 			bgep->param_link_speed = 10;
648*0Sstevel@tonic-gate 			adv_10fdx = B_TRUE;
649*0Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
650*0Sstevel@tonic-gate 			break;
651*0Sstevel@tonic-gate 
652*0Sstevel@tonic-gate 		case BGE_LOOP_INTERNAL_PHY:
653*0Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
654*0Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
655*0Sstevel@tonic-gate 			control = MII_CONTROL_LOOPBACK;
656*0Sstevel@tonic-gate 			break;
657*0Sstevel@tonic-gate 
658*0Sstevel@tonic-gate 		case BGE_LOOP_INTERNAL_MAC:
659*0Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
660*0Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
661*0Sstevel@tonic-gate 			break;
662*0Sstevel@tonic-gate 		}
663*0Sstevel@tonic-gate 	}
664*0Sstevel@tonic-gate 
665*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: autoneg %d "
666*0Sstevel@tonic-gate 			"pause %d asym_pause %d "
667*0Sstevel@tonic-gate 			"1000fdx %d 1000hdx %d "
668*0Sstevel@tonic-gate 			"100fdx %d 100hdx %d "
669*0Sstevel@tonic-gate 			"10fdx %d 10hdx %d ",
670*0Sstevel@tonic-gate 		adv_autoneg,
671*0Sstevel@tonic-gate 		adv_pause, adv_asym_pause,
672*0Sstevel@tonic-gate 		adv_1000fdx, adv_1000hdx,
673*0Sstevel@tonic-gate 		adv_100fdx, adv_100hdx,
674*0Sstevel@tonic-gate 		adv_10fdx, adv_10hdx));
675*0Sstevel@tonic-gate 
676*0Sstevel@tonic-gate 	/*
677*0Sstevel@tonic-gate 	 * We should have at least one technology capability set;
678*0Sstevel@tonic-gate 	 * if not, we select a default of 1000Mb/s full-duplex
679*0Sstevel@tonic-gate 	 */
680*0Sstevel@tonic-gate 	if (!adv_1000fdx && !adv_100fdx && !adv_10fdx &&
681*0Sstevel@tonic-gate 	    !adv_1000hdx && !adv_100hdx && !adv_10hdx)
682*0Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
683*0Sstevel@tonic-gate 
684*0Sstevel@tonic-gate 	/*
685*0Sstevel@tonic-gate 	 * Now transform the adv_* variables into the proper settings
686*0Sstevel@tonic-gate 	 * of the PHY registers ...
687*0Sstevel@tonic-gate 	 *
688*0Sstevel@tonic-gate 	 * If autonegotiation is (now) enabled, we want to trigger
689*0Sstevel@tonic-gate 	 * a new autonegotiation cycle once the PHY has been
690*0Sstevel@tonic-gate 	 * programmed with the capabilities to be advertised.
691*0Sstevel@tonic-gate 	 */
692*0Sstevel@tonic-gate 	if (adv_autoneg)
693*0Sstevel@tonic-gate 		control |= MII_CONTROL_ANE|MII_CONTROL_RSAN;
694*0Sstevel@tonic-gate 
695*0Sstevel@tonic-gate 	if (adv_1000fdx)
696*0Sstevel@tonic-gate 		control |= MII_CONTROL_1000MB|MII_CONTROL_FDUPLEX;
697*0Sstevel@tonic-gate 	else if (adv_1000hdx)
698*0Sstevel@tonic-gate 		control |= MII_CONTROL_1000MB;
699*0Sstevel@tonic-gate 	else if (adv_100fdx)
700*0Sstevel@tonic-gate 		control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX;
701*0Sstevel@tonic-gate 	else if (adv_100hdx)
702*0Sstevel@tonic-gate 		control |= MII_CONTROL_100MB;
703*0Sstevel@tonic-gate 	else if (adv_10fdx)
704*0Sstevel@tonic-gate 		control |= MII_CONTROL_FDUPLEX;
705*0Sstevel@tonic-gate 	else if (adv_10hdx)
706*0Sstevel@tonic-gate 		control |= 0;
707*0Sstevel@tonic-gate 	else
708*0Sstevel@tonic-gate 		{ _NOTE(EMPTY); }	/* Can't get here anyway ...	*/
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 	if (adv_1000fdx)
711*0Sstevel@tonic-gate 		gigctrl |= MII_1000BT_CTL_ADV_FDX;
712*0Sstevel@tonic-gate 	if (adv_1000hdx)
713*0Sstevel@tonic-gate 		gigctrl |= MII_1000BT_CTL_ADV_HDX;
714*0Sstevel@tonic-gate 
715*0Sstevel@tonic-gate 	if (adv_100fdx)
716*0Sstevel@tonic-gate 		anar |= MII_ABILITY_100BASE_TX_FD;
717*0Sstevel@tonic-gate 	if (adv_100hdx)
718*0Sstevel@tonic-gate 		anar |= MII_ABILITY_100BASE_TX;
719*0Sstevel@tonic-gate 	if (adv_10fdx)
720*0Sstevel@tonic-gate 		anar |= MII_ABILITY_10BASE_T_FD;
721*0Sstevel@tonic-gate 	if (adv_10hdx)
722*0Sstevel@tonic-gate 		anar |= MII_ABILITY_10BASE_T;
723*0Sstevel@tonic-gate 
724*0Sstevel@tonic-gate 	if (adv_pause)
725*0Sstevel@tonic-gate 		anar |= MII_ABILITY_PAUSE;
726*0Sstevel@tonic-gate 	if (adv_asym_pause)
727*0Sstevel@tonic-gate 		anar |= MII_ABILITY_ASYM_PAUSE;
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 	/*
730*0Sstevel@tonic-gate 	 * Munge in any other fixed bits we require ...
731*0Sstevel@tonic-gate 	 */
732*0Sstevel@tonic-gate 	anar |= MII_AN_SELECTOR_8023;
733*0Sstevel@tonic-gate 	auxctrl |= MII_AUX_CTRL_NORM_TX_MODE;
734*0Sstevel@tonic-gate 	auxctrl |= MII_AUX_CTRL_NORMAL;
735*0Sstevel@tonic-gate 
736*0Sstevel@tonic-gate 	/*
737*0Sstevel@tonic-gate 	 * Restart the PHY and write the new values.  Note the
738*0Sstevel@tonic-gate 	 * time, so that we can say whether subsequent link state
739*0Sstevel@tonic-gate 	 * changes can be attributed to our reprogramming the PHY
740*0Sstevel@tonic-gate 	 */
741*0Sstevel@tonic-gate 	bgep->phys_write_time = gethrtime();
742*0Sstevel@tonic-gate 	(*bgep->physops->phys_restart)(bgep, B_FALSE);
743*0Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_AN_ADVERT, anar);
744*0Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, control);
745*0Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl);
746*0Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_1000BASE_T_CONTROL, gigctrl);
747*0Sstevel@tonic-gate 
748*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: anar <- 0x%x", anar));
749*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: control <- 0x%x", control));
750*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: auxctrl <- 0x%x", auxctrl));
751*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: gigctrl <- 0x%x", gigctrl));
752*0Sstevel@tonic-gate 
753*0Sstevel@tonic-gate #if	BGE_COPPER_WIRESPEED
754*0Sstevel@tonic-gate 	/*
755*0Sstevel@tonic-gate 	 * Enable the 'wire-speed' feature, if the chip supports it
756*0Sstevel@tonic-gate 	 * and we haven't got (any) loopback mode selected.
757*0Sstevel@tonic-gate 	 */
758*0Sstevel@tonic-gate 	switch (bgep->chipid.device) {
759*0Sstevel@tonic-gate 	case DEVICE_ID_5700:
760*0Sstevel@tonic-gate 	case DEVICE_ID_5700x:
761*0Sstevel@tonic-gate 	case DEVICE_ID_5705:
762*0Sstevel@tonic-gate 	case DEVICE_ID_5705C:
763*0Sstevel@tonic-gate 	case DEVICE_ID_5782:
764*0Sstevel@tonic-gate 		/*
765*0Sstevel@tonic-gate 		 * These chips are known or assumed not to support it
766*0Sstevel@tonic-gate 		 */
767*0Sstevel@tonic-gate 		break;
768*0Sstevel@tonic-gate 
769*0Sstevel@tonic-gate 	default:
770*0Sstevel@tonic-gate 		/*
771*0Sstevel@tonic-gate 		 * All other Broadcom chips are expected to support it.
772*0Sstevel@tonic-gate 		 */
773*0Sstevel@tonic-gate 		if (bgep->param_loop_mode == BGE_LOOP_NONE)
774*0Sstevel@tonic-gate 			bge_mii_put16(bgep, MII_AUX_CONTROL,
775*0Sstevel@tonic-gate 					MII_AUX_CTRL_MISC_WRITE_ENABLE |
776*0Sstevel@tonic-gate 					MII_AUX_CTRL_MISC_WIRE_SPEED |
777*0Sstevel@tonic-gate 					MII_AUX_CTRL_MISC);
778*0Sstevel@tonic-gate 		break;
779*0Sstevel@tonic-gate 	}
780*0Sstevel@tonic-gate #endif	/* BGE_COPPER_WIRESPEED */
781*0Sstevel@tonic-gate }
782*0Sstevel@tonic-gate 
783*0Sstevel@tonic-gate static boolean_t
784*0Sstevel@tonic-gate bge_check_copper(bge_t *bgep, boolean_t recheck)
785*0Sstevel@tonic-gate {
786*0Sstevel@tonic-gate 	uint32_t emac_status;
787*0Sstevel@tonic-gate 	uint16_t mii_status;
788*0Sstevel@tonic-gate 	uint16_t aux;
789*0Sstevel@tonic-gate 	uint_t mode;
790*0Sstevel@tonic-gate 	boolean_t linkup;
791*0Sstevel@tonic-gate 
792*0Sstevel@tonic-gate 	/*
793*0Sstevel@tonic-gate 	 * Step 10: read the status from the PHY (which is self-clearing
794*0Sstevel@tonic-gate 	 * on read!); also read & clear the main (Ethernet) MAC status
795*0Sstevel@tonic-gate 	 * (the relevant bits of this are write-one-to-clear).
796*0Sstevel@tonic-gate 	 */
797*0Sstevel@tonic-gate 	mii_status = bge_mii_get16(bgep, MII_STATUS);
798*0Sstevel@tonic-gate 	emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG);
799*0Sstevel@tonic-gate 	bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status);
800*0Sstevel@tonic-gate 
801*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link %d/%s, MII status 0x%x "
802*0Sstevel@tonic-gate 			"(was 0x%x), Ethernet MAC status 0x%x",
803*0Sstevel@tonic-gate 		bgep->link_state, UPORDOWN(bgep->param_link_up), mii_status,
804*0Sstevel@tonic-gate 		bgep->phy_gen_status, emac_status));
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate 	/*
807*0Sstevel@tonic-gate 	 * If the PHY status hasn't changed since last we looked, and
808*0Sstevel@tonic-gate 	 * we not forcing a recheck (i.e. the link state was already
809*0Sstevel@tonic-gate 	 * known), there's nothing to do.
810*0Sstevel@tonic-gate 	 */
811*0Sstevel@tonic-gate 	if (mii_status == bgep->phy_gen_status && !recheck)
812*0Sstevel@tonic-gate 		return (B_FALSE);
813*0Sstevel@tonic-gate 
814*0Sstevel@tonic-gate 	do {
815*0Sstevel@tonic-gate 		/*
816*0Sstevel@tonic-gate 		 * If the PHY status changed, record the time
817*0Sstevel@tonic-gate 		 */
818*0Sstevel@tonic-gate 		if (mii_status != bgep->phy_gen_status)
819*0Sstevel@tonic-gate 			bgep->phys_event_time = gethrtime();
820*0Sstevel@tonic-gate 
821*0Sstevel@tonic-gate 		/*
822*0Sstevel@tonic-gate 		 * Step 11: read AUX STATUS register to find speed/duplex
823*0Sstevel@tonic-gate 		 */
824*0Sstevel@tonic-gate 		aux = bge_mii_get16(bgep, MII_AUX_STATUS);
825*0Sstevel@tonic-gate 		BGE_CDB(bge_phydump, (bgep, mii_status, aux));
826*0Sstevel@tonic-gate 
827*0Sstevel@tonic-gate 		/*
828*0Sstevel@tonic-gate 		 * We will only consider the link UP if all the readings
829*0Sstevel@tonic-gate 		 * are consistent and give meaningful results ...
830*0Sstevel@tonic-gate 		 */
831*0Sstevel@tonic-gate 		mode = aux & MII_AUX_STATUS_MODE_MASK;
832*0Sstevel@tonic-gate 		mode >>= MII_AUX_STATUS_MODE_SHIFT;
833*0Sstevel@tonic-gate 		linkup = bge_copper_link_speed[mode] > 0;
834*0Sstevel@tonic-gate 		linkup &= bge_copper_link_duplex[mode] != LINK_DUPLEX_UNKNOWN;
835*0Sstevel@tonic-gate 		linkup &= BIS(aux, MII_AUX_STATUS_LINKUP);
836*0Sstevel@tonic-gate 		linkup &= BIS(mii_status, MII_STATUS_LINKUP);
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 		BGE_DEBUG(("bge_check_copper: MII status 0x%x aux 0x%x "
839*0Sstevel@tonic-gate 				"=> mode %d (%s)",
840*0Sstevel@tonic-gate 			mii_status, aux,
841*0Sstevel@tonic-gate 			mode, UPORDOWN(linkup)));
842*0Sstevel@tonic-gate 
843*0Sstevel@tonic-gate 		/*
844*0Sstevel@tonic-gate 		 * Record current register values, then reread status
845*0Sstevel@tonic-gate 		 * register & loop until it stabilises ...
846*0Sstevel@tonic-gate 		 */
847*0Sstevel@tonic-gate 		bgep->phy_aux_status = aux;
848*0Sstevel@tonic-gate 		bgep->phy_gen_status = mii_status;
849*0Sstevel@tonic-gate 		mii_status = bge_mii_get16(bgep, MII_STATUS);
850*0Sstevel@tonic-gate 	} while (mii_status != bgep->phy_gen_status);
851*0Sstevel@tonic-gate 
852*0Sstevel@tonic-gate 	/*
853*0Sstevel@tonic-gate 	 * Assume very little ...
854*0Sstevel@tonic-gate 	 */
855*0Sstevel@tonic-gate 	bgep->param_lp_autoneg = B_FALSE;
856*0Sstevel@tonic-gate 	bgep->param_lp_1000fdx = B_FALSE;
857*0Sstevel@tonic-gate 	bgep->param_lp_1000hdx = B_FALSE;
858*0Sstevel@tonic-gate 	bgep->param_lp_100fdx = B_FALSE;
859*0Sstevel@tonic-gate 	bgep->param_lp_100hdx = B_FALSE;
860*0Sstevel@tonic-gate 	bgep->param_lp_10fdx = B_FALSE;
861*0Sstevel@tonic-gate 	bgep->param_lp_10hdx = B_FALSE;
862*0Sstevel@tonic-gate 	bgep->param_lp_pause = B_FALSE;
863*0Sstevel@tonic-gate 	bgep->param_lp_asym_pause = B_FALSE;
864*0Sstevel@tonic-gate 	bgep->param_link_autoneg = B_FALSE;
865*0Sstevel@tonic-gate 	bgep->param_link_tx_pause = B_FALSE;
866*0Sstevel@tonic-gate 	if (bgep->param_adv_autoneg)
867*0Sstevel@tonic-gate 		bgep->param_link_rx_pause = B_FALSE;
868*0Sstevel@tonic-gate 	else
869*0Sstevel@tonic-gate 		bgep->param_link_rx_pause = bgep->param_adv_pause;
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate 	/*
872*0Sstevel@tonic-gate 	 * Discover all the link partner's abilities.
873*0Sstevel@tonic-gate 	 * These are scattered through various registters ...
874*0Sstevel@tonic-gate 	 */
875*0Sstevel@tonic-gate 	if (BIS(aux, MII_AUX_STATUS_LP_ANEG_ABLE)) {
876*0Sstevel@tonic-gate 		bgep->param_lp_autoneg = B_TRUE;
877*0Sstevel@tonic-gate 		bgep->param_link_autoneg = B_TRUE;
878*0Sstevel@tonic-gate 		bgep->param_link_tx_pause = BIS(aux, MII_AUX_STATUS_TX_PAUSE);
879*0Sstevel@tonic-gate 		bgep->param_link_rx_pause = BIS(aux, MII_AUX_STATUS_RX_PAUSE);
880*0Sstevel@tonic-gate 
881*0Sstevel@tonic-gate 		aux = bge_mii_get16(bgep, MII_1000BASE_T_STATUS);
882*0Sstevel@tonic-gate 		bgep->param_lp_1000fdx = BIS(aux, MII_1000BT_STAT_LP_FDX_CAP);
883*0Sstevel@tonic-gate 		bgep->param_lp_1000hdx = BIS(aux, MII_1000BT_STAT_LP_HDX_CAP);
884*0Sstevel@tonic-gate 
885*0Sstevel@tonic-gate 		aux = bge_mii_get16(bgep, MII_AN_LPABLE);
886*0Sstevel@tonic-gate 		bgep->param_lp_100fdx = BIS(aux, MII_ABILITY_100BASE_TX_FD);
887*0Sstevel@tonic-gate 		bgep->param_lp_100hdx = BIS(aux, MII_ABILITY_100BASE_TX);
888*0Sstevel@tonic-gate 		bgep->param_lp_10fdx = BIS(aux, MII_ABILITY_10BASE_T_FD);
889*0Sstevel@tonic-gate 		bgep->param_lp_10hdx = BIS(aux, MII_ABILITY_10BASE_T);
890*0Sstevel@tonic-gate 		bgep->param_lp_pause = BIS(aux, MII_ABILITY_PAUSE);
891*0Sstevel@tonic-gate 		bgep->param_lp_asym_pause = BIS(aux, MII_ABILITY_ASYM_PAUSE);
892*0Sstevel@tonic-gate 	}
893*0Sstevel@tonic-gate 
894*0Sstevel@tonic-gate 	/*
895*0Sstevel@tonic-gate 	 * Step 12: update ndd-visible state parameters, BUT!
896*0Sstevel@tonic-gate 	 * we don't transfer the new state to <link_state> just yet;
897*0Sstevel@tonic-gate 	 * instead we mark the <link_state> as UNKNOWN, and our caller
898*0Sstevel@tonic-gate 	 * will resolve it once the status has stopped changing and
899*0Sstevel@tonic-gate 	 * been stable for several seconds.
900*0Sstevel@tonic-gate 	 */
901*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link was %s speed %d duplex %d",
902*0Sstevel@tonic-gate 		UPORDOWN(bgep->param_link_up),
903*0Sstevel@tonic-gate 		bgep->param_link_speed,
904*0Sstevel@tonic-gate 		bgep->param_link_duplex));
905*0Sstevel@tonic-gate 
906*0Sstevel@tonic-gate 	if (!linkup)
907*0Sstevel@tonic-gate 		mode = MII_AUX_STATUS_MODE_NONE;
908*0Sstevel@tonic-gate 	bgep->param_link_up = linkup;
909*0Sstevel@tonic-gate 	bgep->param_link_speed = bge_copper_link_speed[mode];
910*0Sstevel@tonic-gate 	bgep->param_link_duplex = bge_copper_link_duplex[mode];
911*0Sstevel@tonic-gate 	bgep->link_mode_msg = bge_copper_link_text[mode];
912*0Sstevel@tonic-gate 	bgep->link_state = LINK_STATE_UNKNOWN;
913*0Sstevel@tonic-gate 
914*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link now %s speed %d duplex %d",
915*0Sstevel@tonic-gate 		UPORDOWN(bgep->param_link_up),
916*0Sstevel@tonic-gate 		bgep->param_link_speed,
917*0Sstevel@tonic-gate 		bgep->param_link_duplex));
918*0Sstevel@tonic-gate 
919*0Sstevel@tonic-gate 	return (B_TRUE);
920*0Sstevel@tonic-gate }
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate static const phys_ops_t copper_ops = {
923*0Sstevel@tonic-gate 	bge_restart_copper,
924*0Sstevel@tonic-gate 	bge_update_copper,
925*0Sstevel@tonic-gate 	bge_check_copper
926*0Sstevel@tonic-gate };
927*0Sstevel@tonic-gate 
928*0Sstevel@tonic-gate 
929*0Sstevel@tonic-gate /*
930*0Sstevel@tonic-gate  * ========== SerDes support ==========
931*0Sstevel@tonic-gate  */
932*0Sstevel@tonic-gate 
933*0Sstevel@tonic-gate #undef	BGE_DBG
934*0Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_SERDES	/* debug flag for this code	*/
935*0Sstevel@tonic-gate 
936*0Sstevel@tonic-gate /*
937*0Sstevel@tonic-gate  * Reinitialise the SerDes interface.  Note that it normally powers
938*0Sstevel@tonic-gate  * up in the disabled state, so we need to explicitly activate it.
939*0Sstevel@tonic-gate  */
940*0Sstevel@tonic-gate static void
941*0Sstevel@tonic-gate bge_restart_serdes(bge_t *bgep, boolean_t powerdown)
942*0Sstevel@tonic-gate {
943*0Sstevel@tonic-gate 	uint32_t macmode;
944*0Sstevel@tonic-gate 
945*0Sstevel@tonic-gate 	BGE_TRACE(("bge_restart_serdes($%p, %d)", (void *)bgep, powerdown));
946*0Sstevel@tonic-gate 
947*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
948*0Sstevel@tonic-gate 
949*0Sstevel@tonic-gate 	/*
950*0Sstevel@tonic-gate 	 * Ensure that the main Ethernet MAC mode register is programmed
951*0Sstevel@tonic-gate 	 * appropriately for the SerDes interface ...
952*0Sstevel@tonic-gate 	 */
953*0Sstevel@tonic-gate 	macmode = bge_reg_get32(bgep, ETHERNET_MAC_MODE_REG);
954*0Sstevel@tonic-gate 	macmode &= ~ETHERNET_MODE_LINK_POLARITY;
955*0Sstevel@tonic-gate 	macmode &= ~ETHERNET_MODE_PORTMODE_MASK;
956*0Sstevel@tonic-gate 	macmode |= ETHERNET_MODE_PORTMODE_TBI;
957*0Sstevel@tonic-gate 	bge_reg_put32(bgep, ETHERNET_MAC_MODE_REG, macmode);
958*0Sstevel@tonic-gate 
959*0Sstevel@tonic-gate 	/*
960*0Sstevel@tonic-gate 	 * Ensure that loopback is OFF and comma detection is enabled.  Then
961*0Sstevel@tonic-gate 	 * disable the SerDes output (the first time through, it may/will
962*0Sstevel@tonic-gate 	 * already be disabled).  If we're shutting down, leave it disabled.
963*0Sstevel@tonic-gate 	 */
964*0Sstevel@tonic-gate 	bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TBI_LOOPBACK);
965*0Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_COMMA_DETECT);
966*0Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
967*0Sstevel@tonic-gate 	if (powerdown)
968*0Sstevel@tonic-gate 		return;
969*0Sstevel@tonic-gate 
970*0Sstevel@tonic-gate 	/*
971*0Sstevel@tonic-gate 	 * Otherwise, pause, (re-)enable the SerDes output, and send
972*0Sstevel@tonic-gate 	 * all-zero config words in order to force autoneg restart.
973*0Sstevel@tonic-gate 	 * Invalidate the saved "link partners received configs", as
974*0Sstevel@tonic-gate 	 * we're starting over ...
975*0Sstevel@tonic-gate 	 */
976*0Sstevel@tonic-gate 	drv_usecwait(10000);
977*0Sstevel@tonic-gate 	bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
978*0Sstevel@tonic-gate 	bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG, 0);
979*0Sstevel@tonic-gate 	bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
980*0Sstevel@tonic-gate 	drv_usecwait(10);
981*0Sstevel@tonic-gate 	bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
982*0Sstevel@tonic-gate 	bgep->serdes_lpadv = AUTONEG_CODE_FAULT_ANEG_ERR;
983*0Sstevel@tonic-gate 	bgep->serdes_status = ~0U;
984*0Sstevel@tonic-gate }
985*0Sstevel@tonic-gate 
986*0Sstevel@tonic-gate /*
987*0Sstevel@tonic-gate  * Synchronise the SerDes speed/duplex/autonegotiation capabilities and
988*0Sstevel@tonic-gate  * advertisements with the required settings as specified by the various
989*0Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
990*0Sstevel@tonic-gate  *
991*0Sstevel@tonic-gate  * We always reinitalise the SerDes; this should cause the link to go down,
992*0Sstevel@tonic-gate  * and then back up again once the link is stable and autonegotiation
993*0Sstevel@tonic-gate  * (if enabled) is complete.  We should get a link state change interrupt
994*0Sstevel@tonic-gate  * somewhere along the way ...
995*0Sstevel@tonic-gate  *
996*0Sstevel@tonic-gate  * NOTE: SerDes only supports 1000FDX/HDX (with or without pause) so the
997*0Sstevel@tonic-gate  * param_* variables relating to lower speeds are ignored.
998*0Sstevel@tonic-gate  *
999*0Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
1000*0Sstevel@tonic-gate  */
1001*0Sstevel@tonic-gate static void
1002*0Sstevel@tonic-gate bge_update_serdes(bge_t *bgep)
1003*0Sstevel@tonic-gate {
1004*0Sstevel@tonic-gate 	boolean_t adv_autoneg;
1005*0Sstevel@tonic-gate 	boolean_t adv_pause;
1006*0Sstevel@tonic-gate 	boolean_t adv_asym_pause;
1007*0Sstevel@tonic-gate 	boolean_t adv_1000fdx;
1008*0Sstevel@tonic-gate 	boolean_t adv_1000hdx;
1009*0Sstevel@tonic-gate 
1010*0Sstevel@tonic-gate 	uint32_t serdes;
1011*0Sstevel@tonic-gate 	uint32_t advert;
1012*0Sstevel@tonic-gate 
1013*0Sstevel@tonic-gate 	BGE_TRACE(("bge_update_serdes($%p)", (void *)bgep));
1014*0Sstevel@tonic-gate 
1015*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1016*0Sstevel@tonic-gate 
1017*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: autoneg %d "
1018*0Sstevel@tonic-gate 			"pause %d asym_pause %d "
1019*0Sstevel@tonic-gate 			"1000fdx %d 1000hdx %d "
1020*0Sstevel@tonic-gate 			"100fdx %d 100hdx %d "
1021*0Sstevel@tonic-gate 			"10fdx %d 10hdx %d ",
1022*0Sstevel@tonic-gate 		bgep->param_adv_autoneg,
1023*0Sstevel@tonic-gate 		bgep->param_adv_pause, bgep->param_adv_asym_pause,
1024*0Sstevel@tonic-gate 		bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
1025*0Sstevel@tonic-gate 		bgep->param_adv_100fdx, bgep->param_adv_100hdx,
1026*0Sstevel@tonic-gate 		bgep->param_adv_10fdx, bgep->param_adv_10hdx));
1027*0Sstevel@tonic-gate 
1028*0Sstevel@tonic-gate 	serdes = advert = 0;
1029*0Sstevel@tonic-gate 
1030*0Sstevel@tonic-gate 	/*
1031*0Sstevel@tonic-gate 	 * SerDes settings are normally based on the param_* variables,
1032*0Sstevel@tonic-gate 	 * but if any loopback mode is in effect, that takes precedence.
1033*0Sstevel@tonic-gate 	 *
1034*0Sstevel@tonic-gate 	 * BGE supports MAC-internal loopback, PHY-internal loopback,
1035*0Sstevel@tonic-gate 	 * and External loopback at a variety of speeds (with a special
1036*0Sstevel@tonic-gate 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
1037*0Sstevel@tonic-gate 	 * is turned ON, and the speed/mastership is forced.
1038*0Sstevel@tonic-gate 	 *
1039*0Sstevel@tonic-gate 	 * Note: for the SerDes interface, "PHY" internal loopback is
1040*0Sstevel@tonic-gate 	 * interpreted as SerDes internal loopback, and all external
1041*0Sstevel@tonic-gate 	 * loopback modes are treated equivalently, as 1Gb/external.
1042*0Sstevel@tonic-gate 	 */
1043*0Sstevel@tonic-gate 	switch (bgep->param_loop_mode) {
1044*0Sstevel@tonic-gate 	case BGE_LOOP_NONE:
1045*0Sstevel@tonic-gate 	default:
1046*0Sstevel@tonic-gate 		adv_autoneg = bgep->param_adv_autoneg;
1047*0Sstevel@tonic-gate 		adv_pause = bgep->param_adv_pause;
1048*0Sstevel@tonic-gate 		adv_asym_pause = bgep->param_adv_asym_pause;
1049*0Sstevel@tonic-gate 		adv_1000fdx = bgep->param_adv_1000fdx;
1050*0Sstevel@tonic-gate 		adv_1000hdx = bgep->param_adv_1000hdx;
1051*0Sstevel@tonic-gate 		break;
1052*0Sstevel@tonic-gate 
1053*0Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_PHY:
1054*0Sstevel@tonic-gate 		serdes |= SERDES_CONTROL_TBI_LOOPBACK;
1055*0Sstevel@tonic-gate 		/* FALLTHRU */
1056*0Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_MAC:
1057*0Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_1000:
1058*0Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_100:
1059*0Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_10:
1060*0Sstevel@tonic-gate 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
1061*0Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
1062*0Sstevel@tonic-gate 		adv_1000hdx = B_FALSE;
1063*0Sstevel@tonic-gate 		break;
1064*0Sstevel@tonic-gate 	}
1065*0Sstevel@tonic-gate 
1066*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: autoneg %d "
1067*0Sstevel@tonic-gate 			"pause %d asym_pause %d "
1068*0Sstevel@tonic-gate 			"1000fdx %d 1000hdx %d ",
1069*0Sstevel@tonic-gate 		adv_autoneg,
1070*0Sstevel@tonic-gate 		adv_pause, adv_asym_pause,
1071*0Sstevel@tonic-gate 		adv_1000fdx, adv_1000hdx));
1072*0Sstevel@tonic-gate 
1073*0Sstevel@tonic-gate 	/*
1074*0Sstevel@tonic-gate 	 * We should have at least one gigabit technology capability
1075*0Sstevel@tonic-gate 	 * set; if not, we select a default of 1000Mb/s full-duplex
1076*0Sstevel@tonic-gate 	 */
1077*0Sstevel@tonic-gate 	if (!adv_1000fdx && !adv_1000hdx)
1078*0Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
1079*0Sstevel@tonic-gate 
1080*0Sstevel@tonic-gate 	/*
1081*0Sstevel@tonic-gate 	 * Now transform the adv_* variables into the proper settings
1082*0Sstevel@tonic-gate 	 * of the SerDes registers ...
1083*0Sstevel@tonic-gate 	 *
1084*0Sstevel@tonic-gate 	 * If autonegotiation is (now) not enabled, pretend it's been
1085*0Sstevel@tonic-gate 	 * done and failed ...
1086*0Sstevel@tonic-gate 	 */
1087*0Sstevel@tonic-gate 	if (!adv_autoneg)
1088*0Sstevel@tonic-gate 		advert |= AUTONEG_CODE_FAULT_ANEG_ERR;
1089*0Sstevel@tonic-gate 
1090*0Sstevel@tonic-gate 	if (adv_1000fdx) {
1091*0Sstevel@tonic-gate 		advert |= AUTONEG_CODE_FULL_DUPLEX;
1092*0Sstevel@tonic-gate 		bgep->param_adv_1000fdx = adv_1000fdx;
1093*0Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_FULL;
1094*0Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
1095*0Sstevel@tonic-gate 	}
1096*0Sstevel@tonic-gate 	if (adv_1000hdx) {
1097*0Sstevel@tonic-gate 		advert |= AUTONEG_CODE_HALF_DUPLEX;
1098*0Sstevel@tonic-gate 		bgep->param_adv_1000hdx = adv_1000hdx;
1099*0Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_HALF;
1100*0Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
1101*0Sstevel@tonic-gate 	}
1102*0Sstevel@tonic-gate 
1103*0Sstevel@tonic-gate 	if (adv_pause)
1104*0Sstevel@tonic-gate 		advert |= AUTONEG_CODE_PAUSE;
1105*0Sstevel@tonic-gate 	if (adv_asym_pause)
1106*0Sstevel@tonic-gate 		advert |= AUTONEG_CODE_ASYM_PAUSE;
1107*0Sstevel@tonic-gate 
1108*0Sstevel@tonic-gate 	/*
1109*0Sstevel@tonic-gate 	 * Restart the SerDes and write the new values.  Note the
1110*0Sstevel@tonic-gate 	 * time, so that we can say whether subsequent link state
1111*0Sstevel@tonic-gate 	 * changes can be attributed to our reprogramming the SerDes
1112*0Sstevel@tonic-gate 	 */
1113*0Sstevel@tonic-gate 	bgep->serdes_advert = advert;
1114*0Sstevel@tonic-gate 	bgep->phys_write_time = gethrtime();
1115*0Sstevel@tonic-gate 	bge_restart_serdes(bgep, B_FALSE);
1116*0Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, serdes);
1117*0Sstevel@tonic-gate 
1118*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: serdes |= 0x%x, advert 0x%x",
1119*0Sstevel@tonic-gate 		serdes, advert));
1120*0Sstevel@tonic-gate }
1121*0Sstevel@tonic-gate 
1122*0Sstevel@tonic-gate /*
1123*0Sstevel@tonic-gate  * Bare-minimum autoneg protocol
1124*0Sstevel@tonic-gate  *
1125*0Sstevel@tonic-gate  * This code is only called when the link is up and we're receiving config
1126*0Sstevel@tonic-gate  * words, which implies that the link partner wants to autonegotiate
1127*0Sstevel@tonic-gate  * (otherwise, we wouldn't see configs and wouldn't reach this code).
1128*0Sstevel@tonic-gate  */
1129*0Sstevel@tonic-gate static void
1130*0Sstevel@tonic-gate bge_autoneg_serdes(bge_t *bgep)
1131*0Sstevel@tonic-gate {
1132*0Sstevel@tonic-gate 	boolean_t ack;
1133*0Sstevel@tonic-gate 
1134*0Sstevel@tonic-gate 	bgep->serdes_lpadv = bge_reg_get32(bgep, RX_1000BASEX_AUTONEG_REG);
1135*0Sstevel@tonic-gate 	ack = BIS(bgep->serdes_lpadv, AUTONEG_CODE_ACKNOWLEDGE);
1136*0Sstevel@tonic-gate 
1137*0Sstevel@tonic-gate 	if (!ack) {
1138*0Sstevel@tonic-gate 		/*
1139*0Sstevel@tonic-gate 		 * Phase 1: after SerDes reset, we send a few zero configs
1140*0Sstevel@tonic-gate 		 * but then stop.  Here the partner is sending configs, but
1141*0Sstevel@tonic-gate 		 * not ACKing ours; we assume that's 'cos we're not sending
1142*0Sstevel@tonic-gate 		 * any.  So here we send ours, with ACK already set.
1143*0Sstevel@tonic-gate 		 */
1144*0Sstevel@tonic-gate 		bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG,
1145*0Sstevel@tonic-gate 			bgep->serdes_advert | AUTONEG_CODE_ACKNOWLEDGE);
1146*0Sstevel@tonic-gate 		bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG,
1147*0Sstevel@tonic-gate 			ETHERNET_MODE_SEND_CFGS);
1148*0Sstevel@tonic-gate 	} else {
1149*0Sstevel@tonic-gate 		/*
1150*0Sstevel@tonic-gate 		 * Phase 2: partner has ACKed our configs, so now we can
1151*0Sstevel@tonic-gate 		 * stop sending; once our partner also stops sending, we
1152*0Sstevel@tonic-gate 		 * can resolve the Tx/Rx configs.
1153*0Sstevel@tonic-gate 		 */
1154*0Sstevel@tonic-gate 		bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG,
1155*0Sstevel@tonic-gate 			ETHERNET_MODE_SEND_CFGS);
1156*0Sstevel@tonic-gate 	}
1157*0Sstevel@tonic-gate 
1158*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_autoneg_serdes: Rx 0x%x %s Tx 0x%x",
1159*0Sstevel@tonic-gate 		bgep->serdes_lpadv,
1160*0Sstevel@tonic-gate 		ack ? "stop" : "send",
1161*0Sstevel@tonic-gate 		bgep->serdes_advert));
1162*0Sstevel@tonic-gate }
1163*0Sstevel@tonic-gate 
1164*0Sstevel@tonic-gate static boolean_t
1165*0Sstevel@tonic-gate bge_check_serdes(bge_t *bgep, boolean_t recheck)
1166*0Sstevel@tonic-gate {
1167*0Sstevel@tonic-gate 	uint32_t emac_status;
1168*0Sstevel@tonic-gate 	uint32_t lpadv;
1169*0Sstevel@tonic-gate 	boolean_t linkup;
1170*0Sstevel@tonic-gate 
1171*0Sstevel@tonic-gate 	for (;;) {
1172*0Sstevel@tonic-gate 		/*
1173*0Sstevel@tonic-gate 		 * Step 10: read & clear the main (Ethernet) MAC status
1174*0Sstevel@tonic-gate 		 * (the relevant bits of this are write-one-to-clear).
1175*0Sstevel@tonic-gate 		 */
1176*0Sstevel@tonic-gate 		emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG);
1177*0Sstevel@tonic-gate 		bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status);
1178*0Sstevel@tonic-gate 
1179*0Sstevel@tonic-gate 		BGE_DEBUG(("bge_check_serdes: link %d/%s, "
1180*0Sstevel@tonic-gate 				"MAC status 0x%x (was 0x%x)",
1181*0Sstevel@tonic-gate 			bgep->link_state, UPORDOWN(bgep->param_link_up),
1182*0Sstevel@tonic-gate 			emac_status, bgep->serdes_status));
1183*0Sstevel@tonic-gate 
1184*0Sstevel@tonic-gate 		/*
1185*0Sstevel@tonic-gate 		 * We will only consider the link UP if all the readings
1186*0Sstevel@tonic-gate 		 * are consistent and give meaningful results ...
1187*0Sstevel@tonic-gate 		 */
1188*0Sstevel@tonic-gate 		bgep->serdes_status = emac_status;
1189*0Sstevel@tonic-gate 		linkup = BIS(emac_status, ETHERNET_STATUS_SIGNAL_DETECT);
1190*0Sstevel@tonic-gate 		linkup &= BIS(emac_status, ETHERNET_STATUS_PCS_SYNCHED);
1191*0Sstevel@tonic-gate 
1192*0Sstevel@tonic-gate 		/*
1193*0Sstevel@tonic-gate 		 * Now some fiddling with the interpretation:
1194*0Sstevel@tonic-gate 		 *	if there's been an error at the PCS level, treat
1195*0Sstevel@tonic-gate 		 *	it as a link change (the h/w doesn't do this)
1196*0Sstevel@tonic-gate 		 *
1197*0Sstevel@tonic-gate 		 *	if there's been a change, but it's only a PCS sync
1198*0Sstevel@tonic-gate 		 *	change (not a config change), AND the link already
1199*0Sstevel@tonic-gate 		 *	was & is still UP, then ignore the change
1200*0Sstevel@tonic-gate 		 */
1201*0Sstevel@tonic-gate 		if (BIS(emac_status, ETHERNET_STATUS_PCS_ERROR))
1202*0Sstevel@tonic-gate 			emac_status |= ETHERNET_STATUS_LINK_CHANGED;
1203*0Sstevel@tonic-gate 		else if (BIC(emac_status, ETHERNET_STATUS_CFG_CHANGED))
1204*0Sstevel@tonic-gate 			if (bgep->param_link_up && linkup)
1205*0Sstevel@tonic-gate 				emac_status &= ~ETHERNET_STATUS_LINK_CHANGED;
1206*0Sstevel@tonic-gate 
1207*0Sstevel@tonic-gate 		BGE_DEBUG(("bge_check_serdes: status 0x%x => 0x%x %s",
1208*0Sstevel@tonic-gate 			bgep->serdes_status, emac_status, UPORDOWN(linkup)));
1209*0Sstevel@tonic-gate 
1210*0Sstevel@tonic-gate 		/*
1211*0Sstevel@tonic-gate 		 * If we're receiving configs, run the autoneg protocol
1212*0Sstevel@tonic-gate 		 */
1213*0Sstevel@tonic-gate 		if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG))
1214*0Sstevel@tonic-gate 			bge_autoneg_serdes(bgep);
1215*0Sstevel@tonic-gate 
1216*0Sstevel@tonic-gate 		/*
1217*0Sstevel@tonic-gate 		 * If the SerDes status hasn't changed, we're done ...
1218*0Sstevel@tonic-gate 		 */
1219*0Sstevel@tonic-gate 		if (BIC(emac_status, ETHERNET_STATUS_LINK_CHANGED))
1220*0Sstevel@tonic-gate 			break;
1221*0Sstevel@tonic-gate 
1222*0Sstevel@tonic-gate 		/*
1223*0Sstevel@tonic-gate 		 * Record when the SerDes status changed, then go
1224*0Sstevel@tonic-gate 		 * round again until we no longer see a change ...
1225*0Sstevel@tonic-gate 		 */
1226*0Sstevel@tonic-gate 		bgep->phys_event_time = gethrtime();
1227*0Sstevel@tonic-gate 		recheck = B_TRUE;
1228*0Sstevel@tonic-gate 	}
1229*0Sstevel@tonic-gate 
1230*0Sstevel@tonic-gate 	/*
1231*0Sstevel@tonic-gate 	 * If we're not forcing a recheck (i.e. the link state was already
1232*0Sstevel@tonic-gate 	 * known), and we didn't see the hardware flag a change, there's
1233*0Sstevel@tonic-gate 	 * no more to do (and we tell the caller nothing happened).
1234*0Sstevel@tonic-gate 	 */
1235*0Sstevel@tonic-gate 	if (!recheck)
1236*0Sstevel@tonic-gate 		return (B_FALSE);
1237*0Sstevel@tonic-gate 
1238*0Sstevel@tonic-gate 	/*
1239*0Sstevel@tonic-gate 	 * Don't resolve autoneg until we're no longer receiving configs
1240*0Sstevel@tonic-gate 	 */
1241*0Sstevel@tonic-gate 	if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG))
1242*0Sstevel@tonic-gate 		return (B_FALSE);
1243*0Sstevel@tonic-gate 
1244*0Sstevel@tonic-gate 	/*
1245*0Sstevel@tonic-gate 	 * Assume very little ...
1246*0Sstevel@tonic-gate 	 */
1247*0Sstevel@tonic-gate 	bgep->param_lp_autoneg = B_FALSE;
1248*0Sstevel@tonic-gate 	bgep->param_lp_1000fdx = B_FALSE;
1249*0Sstevel@tonic-gate 	bgep->param_lp_1000hdx = B_FALSE;
1250*0Sstevel@tonic-gate 	bgep->param_lp_100fdx = B_FALSE;
1251*0Sstevel@tonic-gate 	bgep->param_lp_100hdx = B_FALSE;
1252*0Sstevel@tonic-gate 	bgep->param_lp_10fdx = B_FALSE;
1253*0Sstevel@tonic-gate 	bgep->param_lp_10hdx = B_FALSE;
1254*0Sstevel@tonic-gate 	bgep->param_lp_pause = B_FALSE;
1255*0Sstevel@tonic-gate 	bgep->param_lp_asym_pause = B_FALSE;
1256*0Sstevel@tonic-gate 	bgep->param_link_autoneg = B_FALSE;
1257*0Sstevel@tonic-gate 	bgep->param_link_tx_pause = B_FALSE;
1258*0Sstevel@tonic-gate 	if (bgep->param_adv_autoneg)
1259*0Sstevel@tonic-gate 		bgep->param_link_rx_pause = B_FALSE;
1260*0Sstevel@tonic-gate 	else
1261*0Sstevel@tonic-gate 		bgep->param_link_rx_pause = bgep->param_adv_pause;
1262*0Sstevel@tonic-gate 
1263*0Sstevel@tonic-gate 	/*
1264*0Sstevel@tonic-gate 	 * Discover all the link partner's abilities.
1265*0Sstevel@tonic-gate 	 */
1266*0Sstevel@tonic-gate 	lpadv = bgep->serdes_lpadv;
1267*0Sstevel@tonic-gate 	if (lpadv != 0 && BIC(lpadv, AUTONEG_CODE_FAULT_MASK)) {
1268*0Sstevel@tonic-gate 		/*
1269*0Sstevel@tonic-gate 		 * No fault, so derive partner's capabilities
1270*0Sstevel@tonic-gate 		 */
1271*0Sstevel@tonic-gate 		bgep->param_lp_autoneg = B_TRUE;
1272*0Sstevel@tonic-gate 		bgep->param_lp_1000fdx = BIS(lpadv, AUTONEG_CODE_FULL_DUPLEX);
1273*0Sstevel@tonic-gate 		bgep->param_lp_1000hdx = BIS(lpadv, AUTONEG_CODE_HALF_DUPLEX);
1274*0Sstevel@tonic-gate 		bgep->param_lp_pause = BIS(lpadv, AUTONEG_CODE_PAUSE);
1275*0Sstevel@tonic-gate 		bgep->param_lp_asym_pause = BIS(lpadv, AUTONEG_CODE_ASYM_PAUSE);
1276*0Sstevel@tonic-gate 
1277*0Sstevel@tonic-gate 		/*
1278*0Sstevel@tonic-gate 		 * Pause direction resolution
1279*0Sstevel@tonic-gate 		 */
1280*0Sstevel@tonic-gate 		bgep->param_link_autoneg = B_TRUE;
1281*0Sstevel@tonic-gate 		if (bgep->param_adv_pause &&
1282*0Sstevel@tonic-gate 		    bgep->param_lp_pause) {
1283*0Sstevel@tonic-gate 			bgep->param_link_tx_pause = B_TRUE;
1284*0Sstevel@tonic-gate 			bgep->param_link_rx_pause = B_TRUE;
1285*0Sstevel@tonic-gate 		}
1286*0Sstevel@tonic-gate 		if (bgep->param_adv_asym_pause &&
1287*0Sstevel@tonic-gate 		    bgep->param_lp_asym_pause) {
1288*0Sstevel@tonic-gate 			if (bgep->param_adv_pause)
1289*0Sstevel@tonic-gate 				bgep->param_link_rx_pause = B_TRUE;
1290*0Sstevel@tonic-gate 			if (bgep->param_lp_pause)
1291*0Sstevel@tonic-gate 				bgep->param_link_tx_pause = B_TRUE;
1292*0Sstevel@tonic-gate 		}
1293*0Sstevel@tonic-gate 	}
1294*0Sstevel@tonic-gate 
1295*0Sstevel@tonic-gate 	/*
1296*0Sstevel@tonic-gate 	 * Step 12: update ndd-visible state parameters, BUT!
1297*0Sstevel@tonic-gate 	 * we don't transfer the new state to <link_state> just yet;
1298*0Sstevel@tonic-gate 	 * instead we mark the <link_state> as UNKNOWN, and our caller
1299*0Sstevel@tonic-gate 	 * will resolve it once the status has stopped changing and
1300*0Sstevel@tonic-gate 	 * been stable for several seconds.
1301*0Sstevel@tonic-gate 	 */
1302*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_serdes: link was %s speed %d duplex %d",
1303*0Sstevel@tonic-gate 		UPORDOWN(bgep->param_link_up),
1304*0Sstevel@tonic-gate 		bgep->param_link_speed,
1305*0Sstevel@tonic-gate 		bgep->param_link_duplex));
1306*0Sstevel@tonic-gate 
1307*0Sstevel@tonic-gate 	if (linkup) {
1308*0Sstevel@tonic-gate 		bgep->param_link_up = B_TRUE;
1309*0Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
1310*0Sstevel@tonic-gate 		if (bgep->param_adv_1000fdx)
1311*0Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_FULL;
1312*0Sstevel@tonic-gate 		else
1313*0Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_HALF;
1314*0Sstevel@tonic-gate 		if (bgep->param_lp_autoneg && !bgep->param_lp_1000fdx)
1315*0Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_HALF;
1316*0Sstevel@tonic-gate 	} else {
1317*0Sstevel@tonic-gate 		bgep->param_link_up = B_FALSE;
1318*0Sstevel@tonic-gate 		bgep->param_link_speed = 0;
1319*0Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_UNKNOWN;
1320*0Sstevel@tonic-gate 	}
1321*0Sstevel@tonic-gate 	switch (bgep->param_link_duplex) {
1322*0Sstevel@tonic-gate 	default:
1323*0Sstevel@tonic-gate 	case LINK_DUPLEX_UNKNOWN:
1324*0Sstevel@tonic-gate 		bgep->link_mode_msg = "down";
1325*0Sstevel@tonic-gate 		break;
1326*0Sstevel@tonic-gate 
1327*0Sstevel@tonic-gate 	case LINK_DUPLEX_HALF:
1328*0Sstevel@tonic-gate 		bgep->link_mode_msg = "up 1000Mbps Half-Duplex";
1329*0Sstevel@tonic-gate 		break;
1330*0Sstevel@tonic-gate 
1331*0Sstevel@tonic-gate 	case LINK_DUPLEX_FULL:
1332*0Sstevel@tonic-gate 		bgep->link_mode_msg = "up 1000Mbps Full-Duplex";
1333*0Sstevel@tonic-gate 		break;
1334*0Sstevel@tonic-gate 	}
1335*0Sstevel@tonic-gate 	bgep->link_state = LINK_STATE_UNKNOWN;
1336*0Sstevel@tonic-gate 
1337*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_serdes: link now %s speed %d duplex %d",
1338*0Sstevel@tonic-gate 		UPORDOWN(bgep->param_link_up),
1339*0Sstevel@tonic-gate 		bgep->param_link_speed,
1340*0Sstevel@tonic-gate 		bgep->param_link_duplex));
1341*0Sstevel@tonic-gate 
1342*0Sstevel@tonic-gate 	return (B_TRUE);
1343*0Sstevel@tonic-gate }
1344*0Sstevel@tonic-gate 
1345*0Sstevel@tonic-gate static const phys_ops_t serdes_ops = {
1346*0Sstevel@tonic-gate 	bge_restart_serdes,
1347*0Sstevel@tonic-gate 	bge_update_serdes,
1348*0Sstevel@tonic-gate 	bge_check_serdes
1349*0Sstevel@tonic-gate };
1350*0Sstevel@tonic-gate 
1351*0Sstevel@tonic-gate /*
1352*0Sstevel@tonic-gate  * ========== Exported physical layer control routines ==========
1353*0Sstevel@tonic-gate  */
1354*0Sstevel@tonic-gate 
1355*0Sstevel@tonic-gate #undef	BGE_DBG
1356*0Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_PHYS	/* debug flag for this code	*/
1357*0Sstevel@tonic-gate 
1358*0Sstevel@tonic-gate /*
1359*0Sstevel@tonic-gate  * Here we have to determine which media we're using (copper or serdes).
1360*0Sstevel@tonic-gate  * Once that's done, we can initialise the physical layer appropriately.
1361*0Sstevel@tonic-gate  */
1362*0Sstevel@tonic-gate void
1363*0Sstevel@tonic-gate bge_phys_init(bge_t *bgep)
1364*0Sstevel@tonic-gate {
1365*0Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_init($%p)", (void *)bgep));
1366*0Sstevel@tonic-gate 
1367*0Sstevel@tonic-gate 	mutex_enter(bgep->genlock);
1368*0Sstevel@tonic-gate 
1369*0Sstevel@tonic-gate 	/*
1370*0Sstevel@tonic-gate 	 * Probe for the (internal) PHY.  If it's not there, we'll assume
1371*0Sstevel@tonic-gate 	 * that this is a 5703/4S, with a SerDes interface rather than a PHY.
1372*0Sstevel@tonic-gate 	 */
1373*0Sstevel@tonic-gate 	bgep->phy_mii_addr = 1;
1374*0Sstevel@tonic-gate 	if (bge_phy_probe(bgep)) {
1375*0Sstevel@tonic-gate 		bgep->chipid.flags &= ~CHIP_FLAG_SERDES;
1376*0Sstevel@tonic-gate 		bgep->phys_delta_time = BGE_PHY_STABLE_TIME;
1377*0Sstevel@tonic-gate 		bgep->physops = &copper_ops;
1378*0Sstevel@tonic-gate 	} else {
1379*0Sstevel@tonic-gate 		bgep->chipid.flags |= CHIP_FLAG_SERDES;
1380*0Sstevel@tonic-gate 		bgep->phys_delta_time = BGE_SERDES_STABLE_TIME;
1381*0Sstevel@tonic-gate 		bgep->physops = &serdes_ops;
1382*0Sstevel@tonic-gate 	}
1383*0Sstevel@tonic-gate 
1384*0Sstevel@tonic-gate 	(*bgep->physops->phys_restart)(bgep, B_FALSE);
1385*0Sstevel@tonic-gate 	mutex_exit(bgep->genlock);
1386*0Sstevel@tonic-gate }
1387*0Sstevel@tonic-gate 
1388*0Sstevel@tonic-gate /*
1389*0Sstevel@tonic-gate  * Reset the physical layer
1390*0Sstevel@tonic-gate  */
1391*0Sstevel@tonic-gate void
1392*0Sstevel@tonic-gate bge_phys_reset(bge_t *bgep)
1393*0Sstevel@tonic-gate {
1394*0Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_reset($%p)", (void *)bgep));
1395*0Sstevel@tonic-gate 
1396*0Sstevel@tonic-gate 	mutex_enter(bgep->genlock);
1397*0Sstevel@tonic-gate 	(*bgep->physops->phys_restart)(bgep, B_FALSE);
1398*0Sstevel@tonic-gate 	mutex_exit(bgep->genlock);
1399*0Sstevel@tonic-gate }
1400*0Sstevel@tonic-gate 
1401*0Sstevel@tonic-gate /*
1402*0Sstevel@tonic-gate  * Reset and power off the physical layer.
1403*0Sstevel@tonic-gate  *
1404*0Sstevel@tonic-gate  * Another RESET should get it back to working, but it may take a few
1405*0Sstevel@tonic-gate  * seconds it may take a few moments to return to normal operation ...
1406*0Sstevel@tonic-gate  */
1407*0Sstevel@tonic-gate void
1408*0Sstevel@tonic-gate bge_phys_idle(bge_t *bgep)
1409*0Sstevel@tonic-gate {
1410*0Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_idle($%p)", (void *)bgep));
1411*0Sstevel@tonic-gate 
1412*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1413*0Sstevel@tonic-gate 	(*bgep->physops->phys_restart)(bgep, B_TRUE);
1414*0Sstevel@tonic-gate }
1415*0Sstevel@tonic-gate 
1416*0Sstevel@tonic-gate /*
1417*0Sstevel@tonic-gate  * Synchronise the PHYSICAL layer's speed/duplex/autonegotiation capabilities
1418*0Sstevel@tonic-gate  * and advertisements with the required settings as specified by the various
1419*0Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
1420*0Sstevel@tonic-gate  *
1421*0Sstevel@tonic-gate  * We always reset the PHYSICAL layer and reprogram *all* relevant registers.
1422*0Sstevel@tonic-gate  * This is expected to cause the link to go down, and then back up again once
1423*0Sstevel@tonic-gate  * the link is stable and autonegotiation (if enabled) is complete.  We should
1424*0Sstevel@tonic-gate  * get a link state change interrupt somewhere along the way ...
1425*0Sstevel@tonic-gate  *
1426*0Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
1427*0Sstevel@tonic-gate  */
1428*0Sstevel@tonic-gate void
1429*0Sstevel@tonic-gate bge_phys_update(bge_t *bgep)
1430*0Sstevel@tonic-gate {
1431*0Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_update($%p)", (void *)bgep));
1432*0Sstevel@tonic-gate 
1433*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1434*0Sstevel@tonic-gate 	(*bgep->physops->phys_update)(bgep);
1435*0Sstevel@tonic-gate }
1436*0Sstevel@tonic-gate 
1437*0Sstevel@tonic-gate #undef	BGE_DBG
1438*0Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_LINK	/* debug flag for this code	*/
1439*0Sstevel@tonic-gate 
1440*0Sstevel@tonic-gate /*
1441*0Sstevel@tonic-gate  * Read the link status and determine whether anything's changed ...
1442*0Sstevel@tonic-gate  *
1443*0Sstevel@tonic-gate  * This routine should be called whenever the chip flags a change
1444*0Sstevel@tonic-gate  * in the hardware link state, and repeatedly for several seconds
1445*0Sstevel@tonic-gate  * afterwards, until we're sure the state has stabilised (sometimes
1446*0Sstevel@tonic-gate  * it goes up and down several times during autonegotiation before
1447*0Sstevel@tonic-gate  * settling on the proper configuration).  This routine applies
1448*0Sstevel@tonic-gate  * timing-based heuristics to determine when the state is stable.
1449*0Sstevel@tonic-gate  *
1450*0Sstevel@tonic-gate  * This routine returns B_FALSE if the link state has not changed,
1451*0Sstevel@tonic-gate  * or if it has changed, but hasn't settled for long enough yet.  It
1452*0Sstevel@tonic-gate  * returns B_TRUE when the change to the new state should be accepted.
1453*0Sstevel@tonic-gate  * In such a case, the param_* variables give the new hardware state,
1454*0Sstevel@tonic-gate  * which the caller should use to update link_state etc.
1455*0Sstevel@tonic-gate  *
1456*0Sstevel@tonic-gate  * The caller must already hold <genlock>
1457*0Sstevel@tonic-gate  */
1458*0Sstevel@tonic-gate boolean_t
1459*0Sstevel@tonic-gate bge_phys_check(bge_t *bgep)
1460*0Sstevel@tonic-gate {
1461*0Sstevel@tonic-gate 	int32_t orig_state;
1462*0Sstevel@tonic-gate 	boolean_t recheck;
1463*0Sstevel@tonic-gate 	boolean_t linkup;
1464*0Sstevel@tonic-gate 	hrtime_t deltat;
1465*0Sstevel@tonic-gate 	hrtime_t now;
1466*0Sstevel@tonic-gate 
1467*0Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_check($%p)", (void *)bgep));
1468*0Sstevel@tonic-gate 
1469*0Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1470*0Sstevel@tonic-gate 
1471*0Sstevel@tonic-gate 	linkup = bgep->param_link_up;
1472*0Sstevel@tonic-gate 	orig_state = bgep->link_state;
1473*0Sstevel@tonic-gate 	recheck = orig_state == LINK_STATE_UNKNOWN;
1474*0Sstevel@tonic-gate 	recheck = (*bgep->physops->phys_check)(bgep, recheck);
1475*0Sstevel@tonic-gate 	if (!recheck)
1476*0Sstevel@tonic-gate 		return (B_FALSE);
1477*0Sstevel@tonic-gate 
1478*0Sstevel@tonic-gate 	/*
1479*0Sstevel@tonic-gate 	 * At this point, the check_*_link() function above has detected
1480*0Sstevel@tonic-gate 	 * a change and updated the param_* variables to show what the
1481*0Sstevel@tonic-gate 	 * latest hardware state seems to be -- but it might still be
1482*0Sstevel@tonic-gate 	 * changing.
1483*0Sstevel@tonic-gate 	 *
1484*0Sstevel@tonic-gate 	 * The link_state must now be UNKNOWN, but if it was previously
1485*0Sstevel@tonic-gate 	 * UP, we want to recognise this immediately, whereas in any other
1486*0Sstevel@tonic-gate 	 * case (e.g. DOWN->UP) we don't accept it until a few seconds have
1487*0Sstevel@tonic-gate 	 * elapsed, to give the hardware time to settle.
1488*0Sstevel@tonic-gate 	 */
1489*0Sstevel@tonic-gate 	now = gethrtime();
1490*0Sstevel@tonic-gate 	deltat = now - bgep->phys_event_time;
1491*0Sstevel@tonic-gate 
1492*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_phys_check: link was %d/%s now %d/%s",
1493*0Sstevel@tonic-gate 		orig_state, UPORDOWN(linkup),
1494*0Sstevel@tonic-gate 		bgep->link_state, UPORDOWN(bgep->param_link_up)));
1495*0Sstevel@tonic-gate 	BGE_DEBUG(("bge_phys_check: update %lld change %lld "
1496*0Sstevel@tonic-gate 			"now %lld delta %lld",
1497*0Sstevel@tonic-gate 		bgep->phys_write_time, bgep->phys_event_time, now, deltat));
1498*0Sstevel@tonic-gate 
1499*0Sstevel@tonic-gate 	if (orig_state == LINK_STATE_UP)
1500*0Sstevel@tonic-gate 		return (B_TRUE);
1501*0Sstevel@tonic-gate 	else
1502*0Sstevel@tonic-gate 		return (deltat > bgep->phys_delta_time);
1503*0Sstevel@tonic-gate }
1504