xref: /onnv-gate/usr/src/uts/common/io/bge/bge_mii.c (revision 1865:c8b524cdb631)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51369Sdduvall  * Common Development and Distribution License (the "License").
61369Sdduvall  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211369Sdduvall 
220Sstevel@tonic-gate /*
231369Sdduvall  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
291369Sdduvall #include "sys/bge_impl2.h"
300Sstevel@tonic-gate 
310Sstevel@tonic-gate /*
320Sstevel@tonic-gate  * Bit test macros, returning boolean_t values
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate #define	BIS(w, b)	(((w) & (b)) ? B_TRUE : B_FALSE)
350Sstevel@tonic-gate #define	BIC(w, b)	(((w) & (b)) ? B_FALSE : B_TRUE)
360Sstevel@tonic-gate #define	UPORDOWN(x)	((x) ? "up" : "down")
370Sstevel@tonic-gate 
380Sstevel@tonic-gate /*
390Sstevel@tonic-gate  * ========== Copper (PHY) support ==========
400Sstevel@tonic-gate  */
410Sstevel@tonic-gate 
420Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_PHY	/* debug flag for this code	*/
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate  * #defines:
460Sstevel@tonic-gate  *	BGE_COPPER_WIRESPEED controls whether the Broadcom WireSpeed(tm)
470Sstevel@tonic-gate  *	feature is enabled.  We need to recheck whether this can be
480Sstevel@tonic-gate  *	enabled; at one time it seemed to interact unpleasantly with the
490Sstevel@tonic-gate  *	loopback modes.
500Sstevel@tonic-gate  *
510Sstevel@tonic-gate  *	BGE_COPPER_IDLEOFF controls whether the (copper) PHY power is
520Sstevel@tonic-gate  *	turned off when the PHY is idled i.e. during driver suspend().
530Sstevel@tonic-gate  *	For now this is disabled because the chip doesn't seem to
540Sstevel@tonic-gate  *	resume cleanly if the PHY power is turned off.
550Sstevel@tonic-gate  */
560Sstevel@tonic-gate #define	BGE_COPPER_WIRESPEED	B_TRUE
570Sstevel@tonic-gate #define	BGE_COPPER_IDLEOFF	B_FALSE
580Sstevel@tonic-gate 
590Sstevel@tonic-gate /*
600Sstevel@tonic-gate  * The arrays below can be indexed by the MODE bits from the Auxiliary
610Sstevel@tonic-gate  * Status register to determine the current speed/duplex settings.
620Sstevel@tonic-gate  */
630Sstevel@tonic-gate static const int16_t bge_copper_link_speed[] = {
640Sstevel@tonic-gate 	0,				/* MII_AUX_STATUS_MODE_NONE	*/
650Sstevel@tonic-gate 	10,				/* MII_AUX_STATUS_MODE_10_H	*/
660Sstevel@tonic-gate 	10,				/* MII_AUX_STATUS_MODE_10_F	*/
670Sstevel@tonic-gate 	100,				/* MII_AUX_STATUS_MODE_100_H	*/
680Sstevel@tonic-gate 	0,				/* MII_AUX_STATUS_MODE_100_4	*/
690Sstevel@tonic-gate 	100,				/* MII_AUX_STATUS_MODE_100_F	*/
700Sstevel@tonic-gate 	1000,				/* MII_AUX_STATUS_MODE_1000_H	*/
710Sstevel@tonic-gate 	1000				/* MII_AUX_STATUS_MODE_1000_F	*/
720Sstevel@tonic-gate };
730Sstevel@tonic-gate 
740Sstevel@tonic-gate static const int8_t bge_copper_link_duplex[] = {
750Sstevel@tonic-gate 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_NONE	*/
760Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_10_H	*/
770Sstevel@tonic-gate 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_10_F	*/
780Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_100_H	*/
790Sstevel@tonic-gate 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_100_4	*/
800Sstevel@tonic-gate 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_100_F	*/
810Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_1000_H	*/
820Sstevel@tonic-gate 	LINK_DUPLEX_FULL		/* MII_AUX_STATUS_MODE_1000_F	*/
830Sstevel@tonic-gate };
840Sstevel@tonic-gate 
850Sstevel@tonic-gate static const char * const bge_copper_link_text[] = {
860Sstevel@tonic-gate 	"down",				/* MII_AUX_STATUS_MODE_NONE	*/
870Sstevel@tonic-gate 	"up 10Mbps Half-Duplex",	/* MII_AUX_STATUS_MODE_10_H	*/
880Sstevel@tonic-gate 	"up 10Mbps Full-Duplex",	/* MII_AUX_STATUS_MODE_10_F	*/
890Sstevel@tonic-gate 	"up 100Mbps Half-Duplex",	/* MII_AUX_STATUS_MODE_100_H	*/
900Sstevel@tonic-gate 	"down",				/* MII_AUX_STATUS_MODE_100_4	*/
910Sstevel@tonic-gate 	"up 100Mbps Full-Duplex",	/* MII_AUX_STATUS_MODE_100_F	*/
920Sstevel@tonic-gate 	"up 1000Mbps Half-Duplex",	/* MII_AUX_STATUS_MODE_1000_H	*/
930Sstevel@tonic-gate 	"up 1000Mbps Full-Duplex"	/* MII_AUX_STATUS_MODE_1000_F	*/
940Sstevel@tonic-gate };
950Sstevel@tonic-gate 
960Sstevel@tonic-gate #if	BGE_DEBUGGING
970Sstevel@tonic-gate 
980Sstevel@tonic-gate static void
990Sstevel@tonic-gate bge_phydump(bge_t *bgep, uint16_t mii_status, uint16_t aux)
1000Sstevel@tonic-gate {
1010Sstevel@tonic-gate 	uint16_t regs[32];
1020Sstevel@tonic-gate 	int i;
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 	for (i = 0; i < 32; ++i)
1070Sstevel@tonic-gate 		switch (i) {
1080Sstevel@tonic-gate 		default:
1090Sstevel@tonic-gate 			regs[i] = bge_mii_get16(bgep, i);
1100Sstevel@tonic-gate 			break;
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 		case MII_STATUS:
1130Sstevel@tonic-gate 			regs[i] = mii_status;
1140Sstevel@tonic-gate 			break;
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 		case MII_AUX_STATUS:
1170Sstevel@tonic-gate 			regs[i] = aux;
1180Sstevel@tonic-gate 			break;
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 		case 0x0b: case 0x0c: case 0x0d: case 0x0e:
1210Sstevel@tonic-gate 		case 0x15: case 0x16: case 0x17:
1220Sstevel@tonic-gate 		case 0x1c:
1230Sstevel@tonic-gate 		case 0x1f:
1240Sstevel@tonic-gate 			/* reserved registers -- don't read these */
1250Sstevel@tonic-gate 			regs[i] = 0;
1260Sstevel@tonic-gate 			break;
1270Sstevel@tonic-gate 		}
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	for (i = 0; i < 32; i += 8)
1300Sstevel@tonic-gate 		BGE_DEBUG(("bge_phydump: "
1310Sstevel@tonic-gate 				"0x%04x %04x %04x %04x %04x %04x %04x %04x",
1320Sstevel@tonic-gate 			regs[i+0], regs[i+1], regs[i+2], regs[i+3],
1330Sstevel@tonic-gate 			regs[i+4], regs[i+5], regs[i+6], regs[i+7]));
1340Sstevel@tonic-gate }
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate #endif	/* BGE_DEBUGGING */
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate /*
1390Sstevel@tonic-gate  * Basic low-level function to probe for a PHY
1400Sstevel@tonic-gate  *
1410Sstevel@tonic-gate  * Returns TRUE if the PHY responds with valid data, FALSE otherwise
1420Sstevel@tonic-gate  */
1430Sstevel@tonic-gate static boolean_t
1440Sstevel@tonic-gate bge_phy_probe(bge_t *bgep)
1450Sstevel@tonic-gate {
1460Sstevel@tonic-gate 	uint16_t phy_status;
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_probe($%p)", (void *)bgep));
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 	/*
1530Sstevel@tonic-gate 	 * Read the MII_STATUS register twice, in
1540Sstevel@tonic-gate 	 * order to clear any sticky bits (but they should
1550Sstevel@tonic-gate 	 * have been cleared by the RESET, I think).
1560Sstevel@tonic-gate 	 */
1570Sstevel@tonic-gate 	phy_status = bge_mii_get16(bgep, MII_STATUS);
1580Sstevel@tonic-gate 	phy_status = bge_mii_get16(bgep, MII_STATUS);
1590Sstevel@tonic-gate 	BGE_DEBUG(("bge_phy_probe: status 0x%x", phy_status));
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	/*
1620Sstevel@tonic-gate 	 * Now check the value read; it should have at least one bit set
1630Sstevel@tonic-gate 	 * (for the device capabilities) and at least one clear (one of
1640Sstevel@tonic-gate 	 * the error bits). So if we see all 0s or all 1s, there's a
1650Sstevel@tonic-gate 	 * problem.  In particular, bge_mii_get16() returns all 1s if
1660Sstevel@tonic-gate 	 * communications fails ...
1670Sstevel@tonic-gate 	 */
1680Sstevel@tonic-gate 	switch (phy_status) {
1690Sstevel@tonic-gate 	case 0x0000:
1700Sstevel@tonic-gate 	case 0xffff:
1710Sstevel@tonic-gate 		return (B_FALSE);
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	default :
1740Sstevel@tonic-gate 		return (B_TRUE);
1750Sstevel@tonic-gate 	}
1760Sstevel@tonic-gate }
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate /*
1790Sstevel@tonic-gate  * Basic low-level function to reset the PHY.
1800Sstevel@tonic-gate  * Doesn't incorporate any special-case workarounds.
1810Sstevel@tonic-gate  *
1820Sstevel@tonic-gate  * Returns TRUE on success, FALSE if the RESET bit doesn't clear
1830Sstevel@tonic-gate  */
1840Sstevel@tonic-gate static boolean_t
1850Sstevel@tonic-gate bge_phy_reset(bge_t *bgep)
1860Sstevel@tonic-gate {
1870Sstevel@tonic-gate 	uint16_t control;
1880Sstevel@tonic-gate 	uint_t count;
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_reset($%p)", (void *)bgep));
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	/*
1950Sstevel@tonic-gate 	 * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear
1960Sstevel@tonic-gate 	 */
1970Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_RESET);
1980Sstevel@tonic-gate 	for (count = 0; ++count < 1000; ) {
1990Sstevel@tonic-gate 		drv_usecwait(5);
2000Sstevel@tonic-gate 		control = bge_mii_get16(bgep, MII_CONTROL);
2010Sstevel@tonic-gate 		if (BIC(control, MII_CONTROL_RESET))
2020Sstevel@tonic-gate 			return (B_TRUE);
2030Sstevel@tonic-gate 	}
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	BGE_DEBUG(("bge_phy_reset: FAILED, control now 0x%x", control));
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	return (B_FALSE);
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate /*
2110Sstevel@tonic-gate  * Basic low-level function to powerdown the PHY, if supported
2120Sstevel@tonic-gate  * If powerdown support is compiled out, this function does nothing.
2130Sstevel@tonic-gate  */
2140Sstevel@tonic-gate static void
2150Sstevel@tonic-gate bge_phy_powerdown(bge_t *bgep)
2160Sstevel@tonic-gate {
2170Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_powerdown"));
2180Sstevel@tonic-gate #if	BGE_COPPER_IDLEOFF
2190Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_PWRDN);
2200Sstevel@tonic-gate #endif	/* BGE_COPPER_IDLEOFF */
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate /*
2240Sstevel@tonic-gate  * The following functions are based on sample code provided by
2250Sstevel@tonic-gate  * Broadcom (20-June-2003), and implement workarounds said to be
2260Sstevel@tonic-gate  * required on the early revisions of the BCM5703/4C.
2270Sstevel@tonic-gate  *
2280Sstevel@tonic-gate  * The registers and values used are mostly UNDOCUMENTED, and
2290Sstevel@tonic-gate  * therefore don't have symbolic names ;-(
2300Sstevel@tonic-gate  *
2310Sstevel@tonic-gate  * Many of the comments are straight out of the Broadcom code:
2320Sstevel@tonic-gate  * even where the code has been restructured, the original
2330Sstevel@tonic-gate  * comments have been preserved in order to explain what these
2340Sstevel@tonic-gate  * undocumented registers & values are all about ...
2350Sstevel@tonic-gate  */
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate static void
2380Sstevel@tonic-gate bge_phy_macro_wait(bge_t *bgep)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate 	uint_t count;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	for (count = 100; --count; )
2430Sstevel@tonic-gate 		if ((bge_mii_get16(bgep, 0x16) & 0x1000) == 0)
2440Sstevel@tonic-gate 			break;
2450Sstevel@tonic-gate }
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate /*
2480Sstevel@tonic-gate  * PHY test data pattern:
2490Sstevel@tonic-gate  *
2500Sstevel@tonic-gate  * For 5703/04, each DFE TAP has 21-bits (low word 15, hi word 6)
2510Sstevel@tonic-gate  * For 5705,    each DFE TAP has 19-bits (low word 15, hi word 4)
2520Sstevel@tonic-gate  * For simplicity, we check only 19-bits, so we don't have to
2530Sstevel@tonic-gate  * distinguish which chip it is.
2540Sstevel@tonic-gate  * the LO word contains 15 bits, make sure pattern data is < 0x7fff
2550Sstevel@tonic-gate  * the HI word contains  6 bits, make sure pattern data is < 0x003f
2560Sstevel@tonic-gate  */
2570Sstevel@tonic-gate #define	N_CHANNELS	4
2580Sstevel@tonic-gate #define	N_TAPS		3
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate static struct {
2610Sstevel@tonic-gate 	uint16_t	lo;
2620Sstevel@tonic-gate 	uint16_t	hi;
2630Sstevel@tonic-gate } tap_data[N_CHANNELS][N_TAPS] = {
2640Sstevel@tonic-gate 	{
2650Sstevel@tonic-gate 		{ 0x5555, 0x0005 },	/* ch0, TAP 0, LO/HI pattern */
2660Sstevel@tonic-gate 		{ 0x2aaa, 0x000a },	/* ch0, TAP 1, LO/HI pattern */
2670Sstevel@tonic-gate 		{ 0x3456, 0x0003 }	/* ch0, TAP 2, LO/HI pattern */
2680Sstevel@tonic-gate 	},
2690Sstevel@tonic-gate 	{
2700Sstevel@tonic-gate 		{ 0x2aaa, 0x000a },	/* ch1, TAP 0, LO/HI pattern */
2710Sstevel@tonic-gate 		{ 0x3333, 0x0003 },	/* ch1, TAP 1, LO/HI pattern */
2720Sstevel@tonic-gate 		{ 0x789a, 0x0005 }	/* ch1, TAP 2, LO/HI pattern */
2730Sstevel@tonic-gate 	},
2740Sstevel@tonic-gate 	{
2750Sstevel@tonic-gate 		{ 0x5a5a, 0x0005 },	/* ch2, TAP 0, LO/HI pattern */
2760Sstevel@tonic-gate 		{ 0x2a6a, 0x000a },	/* ch2, TAP 1, LO/HI pattern */
2770Sstevel@tonic-gate 		{ 0x1bcd, 0x0003 }	/* ch2, TAP 2, LO/HI pattern */
2780Sstevel@tonic-gate 	},
2790Sstevel@tonic-gate 	{
2800Sstevel@tonic-gate 		{ 0x2a5a, 0x000a },	/* ch3, TAP 0, LO/HI pattern */
2810Sstevel@tonic-gate 		{ 0x33c3, 0x0003 },	/* ch3, TAP 1, LO/HI pattern */
2820Sstevel@tonic-gate 		{ 0x2ef1, 0x0005 }	/* ch3, TAP 2, LO/HI pattern */
2830Sstevel@tonic-gate 	}
2840Sstevel@tonic-gate };
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate /*
2870Sstevel@tonic-gate  * Check whether the PHY has locked up after a RESET.
2880Sstevel@tonic-gate  *
2890Sstevel@tonic-gate  * Returns TRUE if it did, FALSE is it's OK ;-)
2900Sstevel@tonic-gate  */
2910Sstevel@tonic-gate static boolean_t
2920Sstevel@tonic-gate bge_phy_locked_up(bge_t *bgep)
2930Sstevel@tonic-gate {
2940Sstevel@tonic-gate 	uint16_t dataLo;
2950Sstevel@tonic-gate 	uint16_t dataHi;
2960Sstevel@tonic-gate 	uint_t chan;
2970Sstevel@tonic-gate 	uint_t tap;
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	/*
3000Sstevel@tonic-gate 	 * Check TAPs for all 4 channels, as soon as we see a lockup
3010Sstevel@tonic-gate 	 * we'll stop checking.
3020Sstevel@tonic-gate 	 */
3030Sstevel@tonic-gate 	for (chan = 0; chan < N_CHANNELS; ++chan) {
3040Sstevel@tonic-gate 		/* Select channel and set TAP index to 0 */
3050Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
3060Sstevel@tonic-gate 		/* Freeze filter again just to be safe */
3070Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0002);
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 		/*
3100Sstevel@tonic-gate 		 * Write fixed pattern to the RAM, 3 TAPs for
3110Sstevel@tonic-gate 		 * each channel, each TAP have 2 WORDs (LO/HI)
3120Sstevel@tonic-gate 		 */
3130Sstevel@tonic-gate 		for (tap = 0; tap < N_TAPS; ++tap) {
3140Sstevel@tonic-gate 			bge_mii_put16(bgep, 0x15, tap_data[chan][tap].lo);
3150Sstevel@tonic-gate 			bge_mii_put16(bgep, 0x15, tap_data[chan][tap].hi);
3160Sstevel@tonic-gate 		}
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 		/*
3190Sstevel@tonic-gate 		 * Active PHY's Macro operation to write DFE
3200Sstevel@tonic-gate 		 * TAP from RAM, and wait for Macro to complete.
3210Sstevel@tonic-gate 		 */
3220Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0202);
3230Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 		/*
3260Sstevel@tonic-gate 		 * Done with write phase, now begin read phase.
3270Sstevel@tonic-gate 		 */
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 		/* Select channel and set TAP index to 0 */
3300Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 		/*
3330Sstevel@tonic-gate 		 * Active PHY's Macro operation to load DFE
3340Sstevel@tonic-gate 		 * TAP to RAM, and wait for Macro to complete
3350Sstevel@tonic-gate 		 */
3360Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0082);
3370Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 		/* Enable "pre-fetch" */
3400Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0802);
3410Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 		/*
3440Sstevel@tonic-gate 		 * Read back the TAP values.  3 TAPs for each
3450Sstevel@tonic-gate 		 * channel, each TAP have 2 WORDs (LO/HI)
3460Sstevel@tonic-gate 		 */
3470Sstevel@tonic-gate 		for (tap = 0; tap < N_TAPS; ++tap) {
3480Sstevel@tonic-gate 			/*
3490Sstevel@tonic-gate 			 * Read Lo/Hi then wait for 'done' is faster.
3500Sstevel@tonic-gate 			 * For DFE TAP, the HI word contains 6 bits,
3510Sstevel@tonic-gate 			 * LO word contains 15 bits
3520Sstevel@tonic-gate 			 */
3530Sstevel@tonic-gate 			dataLo = bge_mii_get16(bgep, 0x15) & 0x7fff;
3540Sstevel@tonic-gate 			dataHi = bge_mii_get16(bgep, 0x15) & 0x003f;
3550Sstevel@tonic-gate 			bge_phy_macro_wait(bgep);
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 			/*
3580Sstevel@tonic-gate 			 * Check if what we wrote is what we read back.
3590Sstevel@tonic-gate 			 * If failed, then the PHY is locked up, we need
3600Sstevel@tonic-gate 			 * to do PHY reset again
3610Sstevel@tonic-gate 			 */
3620Sstevel@tonic-gate 			if (dataLo != tap_data[chan][tap].lo)
3630Sstevel@tonic-gate 				return (B_TRUE);	/* wedged!	*/
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 			if (dataHi != tap_data[chan][tap].hi)
3660Sstevel@tonic-gate 				return (B_TRUE);	/* wedged!	*/
3670Sstevel@tonic-gate 		}
3680Sstevel@tonic-gate 	}
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	/*
3710Sstevel@tonic-gate 	 * The PHY isn't locked up ;-)
3720Sstevel@tonic-gate 	 */
3730Sstevel@tonic-gate 	return (B_FALSE);
3740Sstevel@tonic-gate }
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate /*
3770Sstevel@tonic-gate  * Special-case code to reset the PHY on the 5702/5703/5704C/5705/5782.
3780Sstevel@tonic-gate  * Tries up to 5 times to recover from failure to reset or PHY lockup.
3790Sstevel@tonic-gate  *
3800Sstevel@tonic-gate  * Returns TRUE on success, FALSE if there's an unrecoverable problem
3810Sstevel@tonic-gate  */
3820Sstevel@tonic-gate static boolean_t
3830Sstevel@tonic-gate bge_phy_reset_and_check(bge_t *bgep)
3840Sstevel@tonic-gate {
3850Sstevel@tonic-gate 	boolean_t reset_success;
3860Sstevel@tonic-gate 	boolean_t phy_locked;
3870Sstevel@tonic-gate 	uint16_t extctrl;
3880Sstevel@tonic-gate 	uint_t retries;
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	for (retries = 0; retries < 5; ++retries) {
3910Sstevel@tonic-gate 		/* Issue a phy reset, and wait for reset to complete */
3920Sstevel@tonic-gate 		/* Assuming reset is successful first */
3930Sstevel@tonic-gate 		reset_success = bge_phy_reset(bgep);
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate 		/*
3960Sstevel@tonic-gate 		 * Now go check the DFE TAPs to see if locked up, but
3970Sstevel@tonic-gate 		 * first, we need to set up PHY so we can read DFE
3980Sstevel@tonic-gate 		 * TAPs.
3990Sstevel@tonic-gate 		 */
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 		/*
4020Sstevel@tonic-gate 		 * Disable Transmitter and Interrupt, while we play
4030Sstevel@tonic-gate 		 * with the PHY registers, so the link partner won't
4040Sstevel@tonic-gate 		 * see any strange data and the Driver won't see any
4050Sstevel@tonic-gate 		 * interrupts.
4060Sstevel@tonic-gate 		 */
4070Sstevel@tonic-gate 		extctrl = bge_mii_get16(bgep, 0x10);
4080Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x10, extctrl | 0x3000);
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 		/* Setup Full-Duplex, 1000 mbps */
4110Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x0, 0x0140);
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 		/* Set to Master mode */
4140Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x9, 0x1800);
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 		/* Enable SM_DSP_CLOCK & 6dB */
4170Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x18, 0x0c00);	/* "the ADC fix" */
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 		/* Work-arounds */
4200Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x201f);
4210Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x2aaa);
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 		/* More workarounds */
4240Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x000a);
4250Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x0323);	/* "the Gamma fix" */
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 		/* Blocks the PHY control access */
4280Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x8005);
4290Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x0800);
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 		/* Test whether PHY locked up ;-( */
4320Sstevel@tonic-gate 		phy_locked = bge_phy_locked_up(bgep);
4330Sstevel@tonic-gate 		if (reset_success && !phy_locked)
4340Sstevel@tonic-gate 			break;
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 		/*
4370Sstevel@tonic-gate 		 * Some problem here ... log it & retry
4380Sstevel@tonic-gate 		 */
4390Sstevel@tonic-gate 		if (!reset_success)
4400Sstevel@tonic-gate 			BGE_REPORT((bgep, "PHY didn't reset!"));
4410Sstevel@tonic-gate 		if (phy_locked)
4420Sstevel@tonic-gate 			BGE_REPORT((bgep, "PHY locked up!"));
4430Sstevel@tonic-gate 	}
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	/* Remove block phy control */
4460Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x17, 0x8005);
4470Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x15, 0x0000);
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	/* Unfreeze DFE TAP filter for all channels */
4500Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x17, 0x8200);
4510Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x16, 0x0000);
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	/* Restore PHY back to operating state */
4540Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x18, 0x0400);
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	/* Enable transmitter and interrupt */
4570Sstevel@tonic-gate 	extctrl = bge_mii_get16(bgep, 0x10);
4580Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x10, extctrl & ~0x3000);
4590Sstevel@tonic-gate 
460*1865Sdilpreet 	if (!reset_success)
461*1865Sdilpreet 		bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
462*1865Sdilpreet 	else if (phy_locked)
463*1865Sdilpreet 		bge_fm_ereport(bgep, DDI_FM_DEVICE_INVAL_STATE);
4640Sstevel@tonic-gate 	return (reset_success && !phy_locked);
4650Sstevel@tonic-gate }
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate static void
4680Sstevel@tonic-gate bge_phy_tweak_gmii(bge_t *bgep)
4690Sstevel@tonic-gate {
4700Sstevel@tonic-gate 	/* Tweak GMII timing */
4710Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x1c, 0x8d68);
4720Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x1c, 0x8d68);
4730Sstevel@tonic-gate }
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate /*
4760Sstevel@tonic-gate  * End of Broadcom-derived workaround code				*
4770Sstevel@tonic-gate  */
4780Sstevel@tonic-gate 
479*1865Sdilpreet static int
4800Sstevel@tonic-gate bge_restart_copper(bge_t *bgep, boolean_t powerdown)
4810Sstevel@tonic-gate {
4820Sstevel@tonic-gate 	uint16_t phy_status;
4830Sstevel@tonic-gate 	boolean_t reset_ok;
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	BGE_TRACE(("bge_restart_copper($%p, %d)", (void *)bgep, powerdown));
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	switch (MHCR_CHIP_ASIC_REV(bgep->chipid.asic_rev)) {
4900Sstevel@tonic-gate 	default:
4910Sstevel@tonic-gate 		/*
4920Sstevel@tonic-gate 		 * Shouldn't happen; it means we don't recognise this chip.
4930Sstevel@tonic-gate 		 * It's probably a new one, so we'll try our best anyway ...
4940Sstevel@tonic-gate 		 */
4950Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5703:
4960Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5704:
4970Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5705:
4980Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5721_5751:
4990Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5714:
500388Sly149593 	case MHCR_CHIP_ASIC_REV_5715:
5010Sstevel@tonic-gate 		reset_ok = bge_phy_reset_and_check(bgep);
5020Sstevel@tonic-gate 		break;
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5700:
5050Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5701:
5060Sstevel@tonic-gate 		/*
5070Sstevel@tonic-gate 		 * Just a plain reset; the "check" code breaks these chips
5080Sstevel@tonic-gate 		 */
5090Sstevel@tonic-gate 		reset_ok = bge_phy_reset(bgep);
510*1865Sdilpreet 		if (!reset_ok)
511*1865Sdilpreet 			bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
5120Sstevel@tonic-gate 		break;
5130Sstevel@tonic-gate 	}
514*1865Sdilpreet 	if (!reset_ok) {
515*1865Sdilpreet 		BGE_REPORT((bgep, "PHY failed to reset correctly"));
516*1865Sdilpreet 		return (DDI_FAILURE);
517*1865Sdilpreet 	}
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 	/*
5200Sstevel@tonic-gate 	 * Step 5: disable WOL (not required after RESET)
5210Sstevel@tonic-gate 	 *
5220Sstevel@tonic-gate 	 * Step 6: refer to errata
5230Sstevel@tonic-gate 	 */
5240Sstevel@tonic-gate 	switch (bgep->chipid.asic_rev) {
5250Sstevel@tonic-gate 	default:
5260Sstevel@tonic-gate 		break;
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	case MHCR_CHIP_REV_5704_A0:
5290Sstevel@tonic-gate 		bge_phy_tweak_gmii(bgep);
5300Sstevel@tonic-gate 		break;
5310Sstevel@tonic-gate 	}
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	/*
5340Sstevel@tonic-gate 	 * Step 7: read the MII_INTR_STATUS register twice,
5350Sstevel@tonic-gate 	 * in order to clear any sticky bits (but they should
5360Sstevel@tonic-gate 	 * have been cleared by the RESET, I think), and we're
5370Sstevel@tonic-gate 	 * not using PHY interrupts anyway.
5380Sstevel@tonic-gate 	 *
5390Sstevel@tonic-gate 	 * Step 8: enable the PHY to interrupt on link status
5400Sstevel@tonic-gate 	 * change (not required)
5410Sstevel@tonic-gate 	 *
5420Sstevel@tonic-gate 	 * Step 9: configure PHY LED Mode - not applicable?
5430Sstevel@tonic-gate 	 *
5440Sstevel@tonic-gate 	 * Step 10: read the MII_STATUS register twice, in
5450Sstevel@tonic-gate 	 * order to clear any sticky bits (but they should
5460Sstevel@tonic-gate 	 * have been cleared by the RESET, I think).
5470Sstevel@tonic-gate 	 */
5480Sstevel@tonic-gate 	phy_status = bge_mii_get16(bgep, MII_STATUS);
5490Sstevel@tonic-gate 	phy_status = bge_mii_get16(bgep, MII_STATUS);
5500Sstevel@tonic-gate 	BGE_DEBUG(("bge_restart_copper: status 0x%x", phy_status));
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	/*
5530Sstevel@tonic-gate 	 * Finally, shut down the PHY, if required
5540Sstevel@tonic-gate 	 */
5550Sstevel@tonic-gate 	if (powerdown)
5560Sstevel@tonic-gate 		bge_phy_powerdown(bgep);
557*1865Sdilpreet 	return (DDI_SUCCESS);
5580Sstevel@tonic-gate }
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate /*
5610Sstevel@tonic-gate  * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities
5620Sstevel@tonic-gate  * and advertisements with the required settings as specified by the various
5630Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
5640Sstevel@tonic-gate  *
5650Sstevel@tonic-gate  * We always reset the PHY and reprogram *all* the relevant registers,
5660Sstevel@tonic-gate  * not just those changed.  This should cause the link to go down, and then
5670Sstevel@tonic-gate  * back up again once the link is stable and autonegotiation (if enabled)
5680Sstevel@tonic-gate  * is complete.  We should get a link state change interrupt somewhere along
5690Sstevel@tonic-gate  * the way ...
5700Sstevel@tonic-gate  *
5710Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
5720Sstevel@tonic-gate  */
573*1865Sdilpreet static int
5740Sstevel@tonic-gate bge_update_copper(bge_t *bgep)
5750Sstevel@tonic-gate {
5760Sstevel@tonic-gate 	boolean_t adv_autoneg;
5770Sstevel@tonic-gate 	boolean_t adv_pause;
5780Sstevel@tonic-gate 	boolean_t adv_asym_pause;
5790Sstevel@tonic-gate 	boolean_t adv_1000fdx;
5800Sstevel@tonic-gate 	boolean_t adv_1000hdx;
5810Sstevel@tonic-gate 	boolean_t adv_100fdx;
5820Sstevel@tonic-gate 	boolean_t adv_100hdx;
5830Sstevel@tonic-gate 	boolean_t adv_10fdx;
5840Sstevel@tonic-gate 	boolean_t adv_10hdx;
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	uint16_t control;
5870Sstevel@tonic-gate 	uint16_t gigctrl;
5880Sstevel@tonic-gate 	uint16_t auxctrl;
5890Sstevel@tonic-gate 	uint16_t anar;
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	BGE_TRACE(("bge_update_copper($%p)", (void *)bgep));
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: autoneg %d "
5960Sstevel@tonic-gate 			"pause %d asym_pause %d "
5970Sstevel@tonic-gate 			"1000fdx %d 1000hdx %d "
5980Sstevel@tonic-gate 			"100fdx %d 100hdx %d "
5990Sstevel@tonic-gate 			"10fdx %d 10hdx %d ",
6000Sstevel@tonic-gate 		bgep->param_adv_autoneg,
6010Sstevel@tonic-gate 		bgep->param_adv_pause, bgep->param_adv_asym_pause,
6020Sstevel@tonic-gate 		bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
6030Sstevel@tonic-gate 		bgep->param_adv_100fdx, bgep->param_adv_100hdx,
6040Sstevel@tonic-gate 		bgep->param_adv_10fdx, bgep->param_adv_10hdx));
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 	control = gigctrl = auxctrl = anar = 0;
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 	/*
6090Sstevel@tonic-gate 	 * PHY settings are normally based on the param_* variables,
6100Sstevel@tonic-gate 	 * but if any loopback mode is in effect, that takes precedence.
6110Sstevel@tonic-gate 	 *
6120Sstevel@tonic-gate 	 * BGE supports MAC-internal loopback, PHY-internal loopback,
6130Sstevel@tonic-gate 	 * and External loopback at a variety of speeds (with a special
6140Sstevel@tonic-gate 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
6150Sstevel@tonic-gate 	 * is turned ON, and the speed/mastership is forced.
6160Sstevel@tonic-gate 	 */
6170Sstevel@tonic-gate 	switch (bgep->param_loop_mode) {
6180Sstevel@tonic-gate 	case BGE_LOOP_NONE:
6190Sstevel@tonic-gate 	default:
6200Sstevel@tonic-gate 		adv_autoneg = bgep->param_adv_autoneg;
6210Sstevel@tonic-gate 		adv_pause = bgep->param_adv_pause;
6220Sstevel@tonic-gate 		adv_asym_pause = bgep->param_adv_asym_pause;
6230Sstevel@tonic-gate 		adv_1000fdx = bgep->param_adv_1000fdx;
6240Sstevel@tonic-gate 		adv_1000hdx = bgep->param_adv_1000hdx;
6250Sstevel@tonic-gate 		adv_100fdx = bgep->param_adv_100fdx;
6260Sstevel@tonic-gate 		adv_100hdx = bgep->param_adv_100hdx;
6270Sstevel@tonic-gate 		adv_10fdx = bgep->param_adv_10fdx;
6280Sstevel@tonic-gate 		adv_10hdx = bgep->param_adv_10hdx;
6290Sstevel@tonic-gate 		break;
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_1000:
6320Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_100:
6330Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_10:
6340Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_PHY:
6350Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_MAC:
6360Sstevel@tonic-gate 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
6370Sstevel@tonic-gate 		adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE;
6380Sstevel@tonic-gate 		adv_1000hdx = adv_100hdx = adv_10hdx = B_FALSE;
6390Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_FULL;
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 		switch (bgep->param_loop_mode) {
6420Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_1000:
6430Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
6440Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
6450Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
6460Sstevel@tonic-gate 			gigctrl |= MII_1000BT_CTL_MASTER_CFG;
6470Sstevel@tonic-gate 			gigctrl |= MII_1000BT_CTL_MASTER_SEL;
6480Sstevel@tonic-gate 			break;
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_100:
6510Sstevel@tonic-gate 			bgep->param_link_speed = 100;
6520Sstevel@tonic-gate 			adv_100fdx = B_TRUE;
6530Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
6540Sstevel@tonic-gate 			break;
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_10:
6570Sstevel@tonic-gate 			bgep->param_link_speed = 10;
6580Sstevel@tonic-gate 			adv_10fdx = B_TRUE;
6590Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
6600Sstevel@tonic-gate 			break;
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 		case BGE_LOOP_INTERNAL_PHY:
6630Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
6640Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
6650Sstevel@tonic-gate 			control = MII_CONTROL_LOOPBACK;
6660Sstevel@tonic-gate 			break;
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 		case BGE_LOOP_INTERNAL_MAC:
6690Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
6700Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
6710Sstevel@tonic-gate 			break;
6720Sstevel@tonic-gate 		}
6730Sstevel@tonic-gate 	}
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: autoneg %d "
6760Sstevel@tonic-gate 			"pause %d asym_pause %d "
6770Sstevel@tonic-gate 			"1000fdx %d 1000hdx %d "
6780Sstevel@tonic-gate 			"100fdx %d 100hdx %d "
6790Sstevel@tonic-gate 			"10fdx %d 10hdx %d ",
6800Sstevel@tonic-gate 		adv_autoneg,
6810Sstevel@tonic-gate 		adv_pause, adv_asym_pause,
6820Sstevel@tonic-gate 		adv_1000fdx, adv_1000hdx,
6830Sstevel@tonic-gate 		adv_100fdx, adv_100hdx,
6840Sstevel@tonic-gate 		adv_10fdx, adv_10hdx));
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	/*
6870Sstevel@tonic-gate 	 * We should have at least one technology capability set;
6880Sstevel@tonic-gate 	 * if not, we select a default of 1000Mb/s full-duplex
6890Sstevel@tonic-gate 	 */
6900Sstevel@tonic-gate 	if (!adv_1000fdx && !adv_100fdx && !adv_10fdx &&
6910Sstevel@tonic-gate 	    !adv_1000hdx && !adv_100hdx && !adv_10hdx)
6920Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 	/*
6950Sstevel@tonic-gate 	 * Now transform the adv_* variables into the proper settings
6960Sstevel@tonic-gate 	 * of the PHY registers ...
6970Sstevel@tonic-gate 	 *
6980Sstevel@tonic-gate 	 * If autonegotiation is (now) enabled, we want to trigger
6990Sstevel@tonic-gate 	 * a new autonegotiation cycle once the PHY has been
7000Sstevel@tonic-gate 	 * programmed with the capabilities to be advertised.
7010Sstevel@tonic-gate 	 */
7020Sstevel@tonic-gate 	if (adv_autoneg)
7030Sstevel@tonic-gate 		control |= MII_CONTROL_ANE|MII_CONTROL_RSAN;
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	if (adv_1000fdx)
7060Sstevel@tonic-gate 		control |= MII_CONTROL_1000MB|MII_CONTROL_FDUPLEX;
7070Sstevel@tonic-gate 	else if (adv_1000hdx)
7080Sstevel@tonic-gate 		control |= MII_CONTROL_1000MB;
7090Sstevel@tonic-gate 	else if (adv_100fdx)
7100Sstevel@tonic-gate 		control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX;
7110Sstevel@tonic-gate 	else if (adv_100hdx)
7120Sstevel@tonic-gate 		control |= MII_CONTROL_100MB;
7130Sstevel@tonic-gate 	else if (adv_10fdx)
7140Sstevel@tonic-gate 		control |= MII_CONTROL_FDUPLEX;
7150Sstevel@tonic-gate 	else if (adv_10hdx)
7160Sstevel@tonic-gate 		control |= 0;
7170Sstevel@tonic-gate 	else
7180Sstevel@tonic-gate 		{ _NOTE(EMPTY); }	/* Can't get here anyway ...	*/
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	if (adv_1000fdx)
7210Sstevel@tonic-gate 		gigctrl |= MII_1000BT_CTL_ADV_FDX;
7220Sstevel@tonic-gate 	if (adv_1000hdx)
7230Sstevel@tonic-gate 		gigctrl |= MII_1000BT_CTL_ADV_HDX;
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate 	if (adv_100fdx)
7260Sstevel@tonic-gate 		anar |= MII_ABILITY_100BASE_TX_FD;
7270Sstevel@tonic-gate 	if (adv_100hdx)
7280Sstevel@tonic-gate 		anar |= MII_ABILITY_100BASE_TX;
7290Sstevel@tonic-gate 	if (adv_10fdx)
7300Sstevel@tonic-gate 		anar |= MII_ABILITY_10BASE_T_FD;
7310Sstevel@tonic-gate 	if (adv_10hdx)
7320Sstevel@tonic-gate 		anar |= MII_ABILITY_10BASE_T;
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	if (adv_pause)
7350Sstevel@tonic-gate 		anar |= MII_ABILITY_PAUSE;
7360Sstevel@tonic-gate 	if (adv_asym_pause)
7370Sstevel@tonic-gate 		anar |= MII_ABILITY_ASYM_PAUSE;
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 	/*
7400Sstevel@tonic-gate 	 * Munge in any other fixed bits we require ...
7410Sstevel@tonic-gate 	 */
7420Sstevel@tonic-gate 	anar |= MII_AN_SELECTOR_8023;
7430Sstevel@tonic-gate 	auxctrl |= MII_AUX_CTRL_NORM_TX_MODE;
7440Sstevel@tonic-gate 	auxctrl |= MII_AUX_CTRL_NORMAL;
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	/*
7470Sstevel@tonic-gate 	 * Restart the PHY and write the new values.  Note the
7480Sstevel@tonic-gate 	 * time, so that we can say whether subsequent link state
7490Sstevel@tonic-gate 	 * changes can be attributed to our reprogramming the PHY
7500Sstevel@tonic-gate 	 */
7510Sstevel@tonic-gate 	bgep->phys_write_time = gethrtime();
752*1865Sdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) == DDI_FAILURE)
753*1865Sdilpreet 		return (DDI_FAILURE);
7540Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_AN_ADVERT, anar);
7550Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, control);
7560Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl);
7570Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_1000BASE_T_CONTROL, gigctrl);
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: anar <- 0x%x", anar));
7600Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: control <- 0x%x", control));
7610Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: auxctrl <- 0x%x", auxctrl));
7620Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: gigctrl <- 0x%x", gigctrl));
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate #if	BGE_COPPER_WIRESPEED
7650Sstevel@tonic-gate 	/*
7660Sstevel@tonic-gate 	 * Enable the 'wire-speed' feature, if the chip supports it
7670Sstevel@tonic-gate 	 * and we haven't got (any) loopback mode selected.
7680Sstevel@tonic-gate 	 */
7690Sstevel@tonic-gate 	switch (bgep->chipid.device) {
7700Sstevel@tonic-gate 	case DEVICE_ID_5700:
7710Sstevel@tonic-gate 	case DEVICE_ID_5700x:
7720Sstevel@tonic-gate 	case DEVICE_ID_5705C:
7730Sstevel@tonic-gate 	case DEVICE_ID_5782:
7740Sstevel@tonic-gate 		/*
7750Sstevel@tonic-gate 		 * These chips are known or assumed not to support it
7760Sstevel@tonic-gate 		 */
7770Sstevel@tonic-gate 		break;
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	default:
7800Sstevel@tonic-gate 		/*
7810Sstevel@tonic-gate 		 * All other Broadcom chips are expected to support it.
7820Sstevel@tonic-gate 		 */
7830Sstevel@tonic-gate 		if (bgep->param_loop_mode == BGE_LOOP_NONE)
7840Sstevel@tonic-gate 			bge_mii_put16(bgep, MII_AUX_CONTROL,
7850Sstevel@tonic-gate 					MII_AUX_CTRL_MISC_WRITE_ENABLE |
7860Sstevel@tonic-gate 					MII_AUX_CTRL_MISC_WIRE_SPEED |
7870Sstevel@tonic-gate 					MII_AUX_CTRL_MISC);
7880Sstevel@tonic-gate 		break;
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate #endif	/* BGE_COPPER_WIRESPEED */
791*1865Sdilpreet 	return (DDI_SUCCESS);
7920Sstevel@tonic-gate }
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate static boolean_t
7950Sstevel@tonic-gate bge_check_copper(bge_t *bgep, boolean_t recheck)
7960Sstevel@tonic-gate {
7970Sstevel@tonic-gate 	uint32_t emac_status;
7980Sstevel@tonic-gate 	uint16_t mii_status;
7990Sstevel@tonic-gate 	uint16_t aux;
8000Sstevel@tonic-gate 	uint_t mode;
8010Sstevel@tonic-gate 	boolean_t linkup;
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	/*
8040Sstevel@tonic-gate 	 * Step 10: read the status from the PHY (which is self-clearing
8050Sstevel@tonic-gate 	 * on read!); also read & clear the main (Ethernet) MAC status
8060Sstevel@tonic-gate 	 * (the relevant bits of this are write-one-to-clear).
8070Sstevel@tonic-gate 	 */
8080Sstevel@tonic-gate 	mii_status = bge_mii_get16(bgep, MII_STATUS);
8090Sstevel@tonic-gate 	emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG);
8100Sstevel@tonic-gate 	bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status);
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link %d/%s, MII status 0x%x "
8130Sstevel@tonic-gate 			"(was 0x%x), Ethernet MAC status 0x%x",
8140Sstevel@tonic-gate 		bgep->link_state, UPORDOWN(bgep->param_link_up), mii_status,
8150Sstevel@tonic-gate 		bgep->phy_gen_status, emac_status));
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 	/*
8180Sstevel@tonic-gate 	 * If the PHY status hasn't changed since last we looked, and
8190Sstevel@tonic-gate 	 * we not forcing a recheck (i.e. the link state was already
8200Sstevel@tonic-gate 	 * known), there's nothing to do.
8210Sstevel@tonic-gate 	 */
8220Sstevel@tonic-gate 	if (mii_status == bgep->phy_gen_status && !recheck)
8230Sstevel@tonic-gate 		return (B_FALSE);
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate 	do {
8260Sstevel@tonic-gate 		/*
8270Sstevel@tonic-gate 		 * If the PHY status changed, record the time
8280Sstevel@tonic-gate 		 */
8290Sstevel@tonic-gate 		if (mii_status != bgep->phy_gen_status)
8300Sstevel@tonic-gate 			bgep->phys_event_time = gethrtime();
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 		/*
8330Sstevel@tonic-gate 		 * Step 11: read AUX STATUS register to find speed/duplex
8340Sstevel@tonic-gate 		 */
8350Sstevel@tonic-gate 		aux = bge_mii_get16(bgep, MII_AUX_STATUS);
8360Sstevel@tonic-gate 		BGE_CDB(bge_phydump, (bgep, mii_status, aux));
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate 		/*
8390Sstevel@tonic-gate 		 * We will only consider the link UP if all the readings
8400Sstevel@tonic-gate 		 * are consistent and give meaningful results ...
8410Sstevel@tonic-gate 		 */
8420Sstevel@tonic-gate 		mode = aux & MII_AUX_STATUS_MODE_MASK;
8430Sstevel@tonic-gate 		mode >>= MII_AUX_STATUS_MODE_SHIFT;
8440Sstevel@tonic-gate 		linkup = bge_copper_link_speed[mode] > 0;
8450Sstevel@tonic-gate 		linkup &= bge_copper_link_duplex[mode] != LINK_DUPLEX_UNKNOWN;
8460Sstevel@tonic-gate 		linkup &= BIS(aux, MII_AUX_STATUS_LINKUP);
8470Sstevel@tonic-gate 		linkup &= BIS(mii_status, MII_STATUS_LINKUP);
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 		BGE_DEBUG(("bge_check_copper: MII status 0x%x aux 0x%x "
8500Sstevel@tonic-gate 				"=> mode %d (%s)",
8510Sstevel@tonic-gate 			mii_status, aux,
8520Sstevel@tonic-gate 			mode, UPORDOWN(linkup)));
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 		/*
8550Sstevel@tonic-gate 		 * Record current register values, then reread status
8560Sstevel@tonic-gate 		 * register & loop until it stabilises ...
8570Sstevel@tonic-gate 		 */
8580Sstevel@tonic-gate 		bgep->phy_aux_status = aux;
8590Sstevel@tonic-gate 		bgep->phy_gen_status = mii_status;
8600Sstevel@tonic-gate 		mii_status = bge_mii_get16(bgep, MII_STATUS);
8610Sstevel@tonic-gate 	} while (mii_status != bgep->phy_gen_status);
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 	/*
8640Sstevel@tonic-gate 	 * Assume very little ...
8650Sstevel@tonic-gate 	 */
8660Sstevel@tonic-gate 	bgep->param_lp_autoneg = B_FALSE;
8670Sstevel@tonic-gate 	bgep->param_lp_1000fdx = B_FALSE;
8680Sstevel@tonic-gate 	bgep->param_lp_1000hdx = B_FALSE;
8690Sstevel@tonic-gate 	bgep->param_lp_100fdx = B_FALSE;
8700Sstevel@tonic-gate 	bgep->param_lp_100hdx = B_FALSE;
8710Sstevel@tonic-gate 	bgep->param_lp_10fdx = B_FALSE;
8720Sstevel@tonic-gate 	bgep->param_lp_10hdx = B_FALSE;
8730Sstevel@tonic-gate 	bgep->param_lp_pause = B_FALSE;
8740Sstevel@tonic-gate 	bgep->param_lp_asym_pause = B_FALSE;
8750Sstevel@tonic-gate 	bgep->param_link_autoneg = B_FALSE;
8760Sstevel@tonic-gate 	bgep->param_link_tx_pause = B_FALSE;
8770Sstevel@tonic-gate 	if (bgep->param_adv_autoneg)
8780Sstevel@tonic-gate 		bgep->param_link_rx_pause = B_FALSE;
8790Sstevel@tonic-gate 	else
8800Sstevel@tonic-gate 		bgep->param_link_rx_pause = bgep->param_adv_pause;
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 	/*
8830Sstevel@tonic-gate 	 * Discover all the link partner's abilities.
8840Sstevel@tonic-gate 	 * These are scattered through various registters ...
8850Sstevel@tonic-gate 	 */
8860Sstevel@tonic-gate 	if (BIS(aux, MII_AUX_STATUS_LP_ANEG_ABLE)) {
8870Sstevel@tonic-gate 		bgep->param_lp_autoneg = B_TRUE;
8880Sstevel@tonic-gate 		bgep->param_link_autoneg = B_TRUE;
8890Sstevel@tonic-gate 		bgep->param_link_tx_pause = BIS(aux, MII_AUX_STATUS_TX_PAUSE);
8900Sstevel@tonic-gate 		bgep->param_link_rx_pause = BIS(aux, MII_AUX_STATUS_RX_PAUSE);
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 		aux = bge_mii_get16(bgep, MII_1000BASE_T_STATUS);
8930Sstevel@tonic-gate 		bgep->param_lp_1000fdx = BIS(aux, MII_1000BT_STAT_LP_FDX_CAP);
8940Sstevel@tonic-gate 		bgep->param_lp_1000hdx = BIS(aux, MII_1000BT_STAT_LP_HDX_CAP);
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 		aux = bge_mii_get16(bgep, MII_AN_LPABLE);
8970Sstevel@tonic-gate 		bgep->param_lp_100fdx = BIS(aux, MII_ABILITY_100BASE_TX_FD);
8980Sstevel@tonic-gate 		bgep->param_lp_100hdx = BIS(aux, MII_ABILITY_100BASE_TX);
8990Sstevel@tonic-gate 		bgep->param_lp_10fdx = BIS(aux, MII_ABILITY_10BASE_T_FD);
9000Sstevel@tonic-gate 		bgep->param_lp_10hdx = BIS(aux, MII_ABILITY_10BASE_T);
9010Sstevel@tonic-gate 		bgep->param_lp_pause = BIS(aux, MII_ABILITY_PAUSE);
9020Sstevel@tonic-gate 		bgep->param_lp_asym_pause = BIS(aux, MII_ABILITY_ASYM_PAUSE);
9030Sstevel@tonic-gate 	}
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 	/*
9060Sstevel@tonic-gate 	 * Step 12: update ndd-visible state parameters, BUT!
9070Sstevel@tonic-gate 	 * we don't transfer the new state to <link_state> just yet;
9080Sstevel@tonic-gate 	 * instead we mark the <link_state> as UNKNOWN, and our caller
9090Sstevel@tonic-gate 	 * will resolve it once the status has stopped changing and
9100Sstevel@tonic-gate 	 * been stable for several seconds.
9110Sstevel@tonic-gate 	 */
9120Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link was %s speed %d duplex %d",
9130Sstevel@tonic-gate 		UPORDOWN(bgep->param_link_up),
9140Sstevel@tonic-gate 		bgep->param_link_speed,
9150Sstevel@tonic-gate 		bgep->param_link_duplex));
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	if (!linkup)
9180Sstevel@tonic-gate 		mode = MII_AUX_STATUS_MODE_NONE;
9190Sstevel@tonic-gate 	bgep->param_link_up = linkup;
9200Sstevel@tonic-gate 	bgep->param_link_speed = bge_copper_link_speed[mode];
9210Sstevel@tonic-gate 	bgep->param_link_duplex = bge_copper_link_duplex[mode];
9220Sstevel@tonic-gate 	bgep->link_mode_msg = bge_copper_link_text[mode];
9230Sstevel@tonic-gate 	bgep->link_state = LINK_STATE_UNKNOWN;
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link now %s speed %d duplex %d",
9260Sstevel@tonic-gate 		UPORDOWN(bgep->param_link_up),
9270Sstevel@tonic-gate 		bgep->param_link_speed,
9280Sstevel@tonic-gate 		bgep->param_link_duplex));
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 	return (B_TRUE);
9310Sstevel@tonic-gate }
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate static const phys_ops_t copper_ops = {
9340Sstevel@tonic-gate 	bge_restart_copper,
9350Sstevel@tonic-gate 	bge_update_copper,
9360Sstevel@tonic-gate 	bge_check_copper
9370Sstevel@tonic-gate };
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate /*
9410Sstevel@tonic-gate  * ========== SerDes support ==========
9420Sstevel@tonic-gate  */
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate #undef	BGE_DBG
9450Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_SERDES	/* debug flag for this code	*/
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate /*
9480Sstevel@tonic-gate  * Reinitialise the SerDes interface.  Note that it normally powers
9490Sstevel@tonic-gate  * up in the disabled state, so we need to explicitly activate it.
9500Sstevel@tonic-gate  */
951*1865Sdilpreet static int
9520Sstevel@tonic-gate bge_restart_serdes(bge_t *bgep, boolean_t powerdown)
9530Sstevel@tonic-gate {
9540Sstevel@tonic-gate 	uint32_t macmode;
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 	BGE_TRACE(("bge_restart_serdes($%p, %d)", (void *)bgep, powerdown));
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
9590Sstevel@tonic-gate 
9600Sstevel@tonic-gate 	/*
9610Sstevel@tonic-gate 	 * Ensure that the main Ethernet MAC mode register is programmed
9620Sstevel@tonic-gate 	 * appropriately for the SerDes interface ...
9630Sstevel@tonic-gate 	 */
9640Sstevel@tonic-gate 	macmode = bge_reg_get32(bgep, ETHERNET_MAC_MODE_REG);
9650Sstevel@tonic-gate 	macmode &= ~ETHERNET_MODE_LINK_POLARITY;
9660Sstevel@tonic-gate 	macmode &= ~ETHERNET_MODE_PORTMODE_MASK;
9670Sstevel@tonic-gate 	macmode |= ETHERNET_MODE_PORTMODE_TBI;
9680Sstevel@tonic-gate 	bge_reg_put32(bgep, ETHERNET_MAC_MODE_REG, macmode);
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	/*
9710Sstevel@tonic-gate 	 * Ensure that loopback is OFF and comma detection is enabled.  Then
9720Sstevel@tonic-gate 	 * disable the SerDes output (the first time through, it may/will
9730Sstevel@tonic-gate 	 * already be disabled).  If we're shutting down, leave it disabled.
9740Sstevel@tonic-gate 	 */
9750Sstevel@tonic-gate 	bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TBI_LOOPBACK);
9760Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_COMMA_DETECT);
9770Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
9780Sstevel@tonic-gate 	if (powerdown)
979*1865Sdilpreet 		return (DDI_SUCCESS);
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	/*
9820Sstevel@tonic-gate 	 * Otherwise, pause, (re-)enable the SerDes output, and send
9830Sstevel@tonic-gate 	 * all-zero config words in order to force autoneg restart.
9840Sstevel@tonic-gate 	 * Invalidate the saved "link partners received configs", as
9850Sstevel@tonic-gate 	 * we're starting over ...
9860Sstevel@tonic-gate 	 */
9870Sstevel@tonic-gate 	drv_usecwait(10000);
9880Sstevel@tonic-gate 	bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
9890Sstevel@tonic-gate 	bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG, 0);
9900Sstevel@tonic-gate 	bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
9910Sstevel@tonic-gate 	drv_usecwait(10);
9920Sstevel@tonic-gate 	bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
9930Sstevel@tonic-gate 	bgep->serdes_lpadv = AUTONEG_CODE_FAULT_ANEG_ERR;
9940Sstevel@tonic-gate 	bgep->serdes_status = ~0U;
995*1865Sdilpreet 	return (DDI_SUCCESS);
9960Sstevel@tonic-gate }
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate /*
9990Sstevel@tonic-gate  * Synchronise the SerDes speed/duplex/autonegotiation capabilities and
10000Sstevel@tonic-gate  * advertisements with the required settings as specified by the various
10010Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
10020Sstevel@tonic-gate  *
10030Sstevel@tonic-gate  * We always reinitalise the SerDes; this should cause the link to go down,
10040Sstevel@tonic-gate  * and then back up again once the link is stable and autonegotiation
10050Sstevel@tonic-gate  * (if enabled) is complete.  We should get a link state change interrupt
10060Sstevel@tonic-gate  * somewhere along the way ...
10070Sstevel@tonic-gate  *
10080Sstevel@tonic-gate  * NOTE: SerDes only supports 1000FDX/HDX (with or without pause) so the
10090Sstevel@tonic-gate  * param_* variables relating to lower speeds are ignored.
10100Sstevel@tonic-gate  *
10110Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
10120Sstevel@tonic-gate  */
1013*1865Sdilpreet static int
10140Sstevel@tonic-gate bge_update_serdes(bge_t *bgep)
10150Sstevel@tonic-gate {
10160Sstevel@tonic-gate 	boolean_t adv_autoneg;
10170Sstevel@tonic-gate 	boolean_t adv_pause;
10180Sstevel@tonic-gate 	boolean_t adv_asym_pause;
10190Sstevel@tonic-gate 	boolean_t adv_1000fdx;
10200Sstevel@tonic-gate 	boolean_t adv_1000hdx;
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	uint32_t serdes;
10230Sstevel@tonic-gate 	uint32_t advert;
10240Sstevel@tonic-gate 
10250Sstevel@tonic-gate 	BGE_TRACE(("bge_update_serdes($%p)", (void *)bgep));
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: autoneg %d "
10300Sstevel@tonic-gate 			"pause %d asym_pause %d "
10310Sstevel@tonic-gate 			"1000fdx %d 1000hdx %d "
10320Sstevel@tonic-gate 			"100fdx %d 100hdx %d "
10330Sstevel@tonic-gate 			"10fdx %d 10hdx %d ",
10340Sstevel@tonic-gate 		bgep->param_adv_autoneg,
10350Sstevel@tonic-gate 		bgep->param_adv_pause, bgep->param_adv_asym_pause,
10360Sstevel@tonic-gate 		bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
10370Sstevel@tonic-gate 		bgep->param_adv_100fdx, bgep->param_adv_100hdx,
10380Sstevel@tonic-gate 		bgep->param_adv_10fdx, bgep->param_adv_10hdx));
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 	serdes = advert = 0;
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 	/*
10430Sstevel@tonic-gate 	 * SerDes settings are normally based on the param_* variables,
10440Sstevel@tonic-gate 	 * but if any loopback mode is in effect, that takes precedence.
10450Sstevel@tonic-gate 	 *
10460Sstevel@tonic-gate 	 * BGE supports MAC-internal loopback, PHY-internal loopback,
10470Sstevel@tonic-gate 	 * and External loopback at a variety of speeds (with a special
10480Sstevel@tonic-gate 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
10490Sstevel@tonic-gate 	 * is turned ON, and the speed/mastership is forced.
10500Sstevel@tonic-gate 	 *
10510Sstevel@tonic-gate 	 * Note: for the SerDes interface, "PHY" internal loopback is
10520Sstevel@tonic-gate 	 * interpreted as SerDes internal loopback, and all external
10530Sstevel@tonic-gate 	 * loopback modes are treated equivalently, as 1Gb/external.
10540Sstevel@tonic-gate 	 */
10550Sstevel@tonic-gate 	switch (bgep->param_loop_mode) {
10560Sstevel@tonic-gate 	case BGE_LOOP_NONE:
10570Sstevel@tonic-gate 	default:
10580Sstevel@tonic-gate 		adv_autoneg = bgep->param_adv_autoneg;
10590Sstevel@tonic-gate 		adv_pause = bgep->param_adv_pause;
10600Sstevel@tonic-gate 		adv_asym_pause = bgep->param_adv_asym_pause;
10610Sstevel@tonic-gate 		adv_1000fdx = bgep->param_adv_1000fdx;
10620Sstevel@tonic-gate 		adv_1000hdx = bgep->param_adv_1000hdx;
10630Sstevel@tonic-gate 		break;
10640Sstevel@tonic-gate 
10650Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_PHY:
10660Sstevel@tonic-gate 		serdes |= SERDES_CONTROL_TBI_LOOPBACK;
10670Sstevel@tonic-gate 		/* FALLTHRU */
10680Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_MAC:
10690Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_1000:
10700Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_100:
10710Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_10:
10720Sstevel@tonic-gate 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
10730Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
10740Sstevel@tonic-gate 		adv_1000hdx = B_FALSE;
10750Sstevel@tonic-gate 		break;
10760Sstevel@tonic-gate 	}
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: autoneg %d "
10790Sstevel@tonic-gate 			"pause %d asym_pause %d "
10800Sstevel@tonic-gate 			"1000fdx %d 1000hdx %d ",
10810Sstevel@tonic-gate 		adv_autoneg,
10820Sstevel@tonic-gate 		adv_pause, adv_asym_pause,
10830Sstevel@tonic-gate 		adv_1000fdx, adv_1000hdx));
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 	/*
10860Sstevel@tonic-gate 	 * We should have at least one gigabit technology capability
10870Sstevel@tonic-gate 	 * set; if not, we select a default of 1000Mb/s full-duplex
10880Sstevel@tonic-gate 	 */
10890Sstevel@tonic-gate 	if (!adv_1000fdx && !adv_1000hdx)
10900Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 	/*
10930Sstevel@tonic-gate 	 * Now transform the adv_* variables into the proper settings
10940Sstevel@tonic-gate 	 * of the SerDes registers ...
10950Sstevel@tonic-gate 	 *
10960Sstevel@tonic-gate 	 * If autonegotiation is (now) not enabled, pretend it's been
10970Sstevel@tonic-gate 	 * done and failed ...
10980Sstevel@tonic-gate 	 */
10990Sstevel@tonic-gate 	if (!adv_autoneg)
11000Sstevel@tonic-gate 		advert |= AUTONEG_CODE_FAULT_ANEG_ERR;
11010Sstevel@tonic-gate 
11020Sstevel@tonic-gate 	if (adv_1000fdx) {
11030Sstevel@tonic-gate 		advert |= AUTONEG_CODE_FULL_DUPLEX;
11040Sstevel@tonic-gate 		bgep->param_adv_1000fdx = adv_1000fdx;
11050Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_FULL;
11060Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
11070Sstevel@tonic-gate 	}
11080Sstevel@tonic-gate 	if (adv_1000hdx) {
11090Sstevel@tonic-gate 		advert |= AUTONEG_CODE_HALF_DUPLEX;
11100Sstevel@tonic-gate 		bgep->param_adv_1000hdx = adv_1000hdx;
11110Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_HALF;
11120Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
11130Sstevel@tonic-gate 	}
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	if (adv_pause)
11160Sstevel@tonic-gate 		advert |= AUTONEG_CODE_PAUSE;
11170Sstevel@tonic-gate 	if (adv_asym_pause)
11180Sstevel@tonic-gate 		advert |= AUTONEG_CODE_ASYM_PAUSE;
11190Sstevel@tonic-gate 
11200Sstevel@tonic-gate 	/*
11210Sstevel@tonic-gate 	 * Restart the SerDes and write the new values.  Note the
11220Sstevel@tonic-gate 	 * time, so that we can say whether subsequent link state
11230Sstevel@tonic-gate 	 * changes can be attributed to our reprogramming the SerDes
11240Sstevel@tonic-gate 	 */
11250Sstevel@tonic-gate 	bgep->serdes_advert = advert;
11260Sstevel@tonic-gate 	bgep->phys_write_time = gethrtime();
1127*1865Sdilpreet 	(void) bge_restart_serdes(bgep, B_FALSE);
11280Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, serdes);
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: serdes |= 0x%x, advert 0x%x",
11310Sstevel@tonic-gate 		serdes, advert));
1132*1865Sdilpreet 	return (DDI_SUCCESS);
11330Sstevel@tonic-gate }
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate /*
11360Sstevel@tonic-gate  * Bare-minimum autoneg protocol
11370Sstevel@tonic-gate  *
11380Sstevel@tonic-gate  * This code is only called when the link is up and we're receiving config
11390Sstevel@tonic-gate  * words, which implies that the link partner wants to autonegotiate
11400Sstevel@tonic-gate  * (otherwise, we wouldn't see configs and wouldn't reach this code).
11410Sstevel@tonic-gate  */
11420Sstevel@tonic-gate static void
11430Sstevel@tonic-gate bge_autoneg_serdes(bge_t *bgep)
11440Sstevel@tonic-gate {
11450Sstevel@tonic-gate 	boolean_t ack;
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate 	bgep->serdes_lpadv = bge_reg_get32(bgep, RX_1000BASEX_AUTONEG_REG);
11480Sstevel@tonic-gate 	ack = BIS(bgep->serdes_lpadv, AUTONEG_CODE_ACKNOWLEDGE);
11490Sstevel@tonic-gate 
11500Sstevel@tonic-gate 	if (!ack) {
11510Sstevel@tonic-gate 		/*
11520Sstevel@tonic-gate 		 * Phase 1: after SerDes reset, we send a few zero configs
11530Sstevel@tonic-gate 		 * but then stop.  Here the partner is sending configs, but
11540Sstevel@tonic-gate 		 * not ACKing ours; we assume that's 'cos we're not sending
11550Sstevel@tonic-gate 		 * any.  So here we send ours, with ACK already set.
11560Sstevel@tonic-gate 		 */
11570Sstevel@tonic-gate 		bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG,
11580Sstevel@tonic-gate 			bgep->serdes_advert | AUTONEG_CODE_ACKNOWLEDGE);
11590Sstevel@tonic-gate 		bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG,
11600Sstevel@tonic-gate 			ETHERNET_MODE_SEND_CFGS);
11610Sstevel@tonic-gate 	} else {
11620Sstevel@tonic-gate 		/*
11630Sstevel@tonic-gate 		 * Phase 2: partner has ACKed our configs, so now we can
11640Sstevel@tonic-gate 		 * stop sending; once our partner also stops sending, we
11650Sstevel@tonic-gate 		 * can resolve the Tx/Rx configs.
11660Sstevel@tonic-gate 		 */
11670Sstevel@tonic-gate 		bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG,
11680Sstevel@tonic-gate 			ETHERNET_MODE_SEND_CFGS);
11690Sstevel@tonic-gate 	}
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate 	BGE_DEBUG(("bge_autoneg_serdes: Rx 0x%x %s Tx 0x%x",
11720Sstevel@tonic-gate 		bgep->serdes_lpadv,
11730Sstevel@tonic-gate 		ack ? "stop" : "send",
11740Sstevel@tonic-gate 		bgep->serdes_advert));
11750Sstevel@tonic-gate }
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate static boolean_t
11780Sstevel@tonic-gate bge_check_serdes(bge_t *bgep, boolean_t recheck)
11790Sstevel@tonic-gate {
11800Sstevel@tonic-gate 	uint32_t emac_status;
11810Sstevel@tonic-gate 	uint32_t lpadv;
11820Sstevel@tonic-gate 	boolean_t linkup;
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 	for (;;) {
11850Sstevel@tonic-gate 		/*
11860Sstevel@tonic-gate 		 * Step 10: read & clear the main (Ethernet) MAC status
11870Sstevel@tonic-gate 		 * (the relevant bits of this are write-one-to-clear).
11880Sstevel@tonic-gate 		 */
11890Sstevel@tonic-gate 		emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG);
11900Sstevel@tonic-gate 		bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status);
11910Sstevel@tonic-gate 
11920Sstevel@tonic-gate 		BGE_DEBUG(("bge_check_serdes: link %d/%s, "
11930Sstevel@tonic-gate 				"MAC status 0x%x (was 0x%x)",
11940Sstevel@tonic-gate 			bgep->link_state, UPORDOWN(bgep->param_link_up),
11950Sstevel@tonic-gate 			emac_status, bgep->serdes_status));
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate 		/*
11980Sstevel@tonic-gate 		 * We will only consider the link UP if all the readings
11990Sstevel@tonic-gate 		 * are consistent and give meaningful results ...
12000Sstevel@tonic-gate 		 */
12010Sstevel@tonic-gate 		bgep->serdes_status = emac_status;
12020Sstevel@tonic-gate 		linkup = BIS(emac_status, ETHERNET_STATUS_SIGNAL_DETECT);
12030Sstevel@tonic-gate 		linkup &= BIS(emac_status, ETHERNET_STATUS_PCS_SYNCHED);
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 		/*
12060Sstevel@tonic-gate 		 * Now some fiddling with the interpretation:
12070Sstevel@tonic-gate 		 *	if there's been an error at the PCS level, treat
12080Sstevel@tonic-gate 		 *	it as a link change (the h/w doesn't do this)
12090Sstevel@tonic-gate 		 *
12100Sstevel@tonic-gate 		 *	if there's been a change, but it's only a PCS sync
12110Sstevel@tonic-gate 		 *	change (not a config change), AND the link already
12120Sstevel@tonic-gate 		 *	was & is still UP, then ignore the change
12130Sstevel@tonic-gate 		 */
12140Sstevel@tonic-gate 		if (BIS(emac_status, ETHERNET_STATUS_PCS_ERROR))
12150Sstevel@tonic-gate 			emac_status |= ETHERNET_STATUS_LINK_CHANGED;
12160Sstevel@tonic-gate 		else if (BIC(emac_status, ETHERNET_STATUS_CFG_CHANGED))
12170Sstevel@tonic-gate 			if (bgep->param_link_up && linkup)
12180Sstevel@tonic-gate 				emac_status &= ~ETHERNET_STATUS_LINK_CHANGED;
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 		BGE_DEBUG(("bge_check_serdes: status 0x%x => 0x%x %s",
12210Sstevel@tonic-gate 			bgep->serdes_status, emac_status, UPORDOWN(linkup)));
12220Sstevel@tonic-gate 
12230Sstevel@tonic-gate 		/*
12240Sstevel@tonic-gate 		 * If we're receiving configs, run the autoneg protocol
12250Sstevel@tonic-gate 		 */
12260Sstevel@tonic-gate 		if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG))
12270Sstevel@tonic-gate 			bge_autoneg_serdes(bgep);
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate 		/*
12300Sstevel@tonic-gate 		 * If the SerDes status hasn't changed, we're done ...
12310Sstevel@tonic-gate 		 */
12320Sstevel@tonic-gate 		if (BIC(emac_status, ETHERNET_STATUS_LINK_CHANGED))
12330Sstevel@tonic-gate 			break;
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate 		/*
12360Sstevel@tonic-gate 		 * Record when the SerDes status changed, then go
12370Sstevel@tonic-gate 		 * round again until we no longer see a change ...
12380Sstevel@tonic-gate 		 */
12390Sstevel@tonic-gate 		bgep->phys_event_time = gethrtime();
12400Sstevel@tonic-gate 		recheck = B_TRUE;
12410Sstevel@tonic-gate 	}
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	/*
12440Sstevel@tonic-gate 	 * If we're not forcing a recheck (i.e. the link state was already
12450Sstevel@tonic-gate 	 * known), and we didn't see the hardware flag a change, there's
12460Sstevel@tonic-gate 	 * no more to do (and we tell the caller nothing happened).
12470Sstevel@tonic-gate 	 */
12480Sstevel@tonic-gate 	if (!recheck)
12490Sstevel@tonic-gate 		return (B_FALSE);
12500Sstevel@tonic-gate 
12510Sstevel@tonic-gate 	/*
12520Sstevel@tonic-gate 	 * Don't resolve autoneg until we're no longer receiving configs
12530Sstevel@tonic-gate 	 */
12540Sstevel@tonic-gate 	if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG))
12550Sstevel@tonic-gate 		return (B_FALSE);
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	/*
12580Sstevel@tonic-gate 	 * Assume very little ...
12590Sstevel@tonic-gate 	 */
12600Sstevel@tonic-gate 	bgep->param_lp_autoneg = B_FALSE;
12610Sstevel@tonic-gate 	bgep->param_lp_1000fdx = B_FALSE;
12620Sstevel@tonic-gate 	bgep->param_lp_1000hdx = B_FALSE;
12630Sstevel@tonic-gate 	bgep->param_lp_100fdx = B_FALSE;
12640Sstevel@tonic-gate 	bgep->param_lp_100hdx = B_FALSE;
12650Sstevel@tonic-gate 	bgep->param_lp_10fdx = B_FALSE;
12660Sstevel@tonic-gate 	bgep->param_lp_10hdx = B_FALSE;
12670Sstevel@tonic-gate 	bgep->param_lp_pause = B_FALSE;
12680Sstevel@tonic-gate 	bgep->param_lp_asym_pause = B_FALSE;
12690Sstevel@tonic-gate 	bgep->param_link_autoneg = B_FALSE;
12700Sstevel@tonic-gate 	bgep->param_link_tx_pause = B_FALSE;
12710Sstevel@tonic-gate 	if (bgep->param_adv_autoneg)
12720Sstevel@tonic-gate 		bgep->param_link_rx_pause = B_FALSE;
12730Sstevel@tonic-gate 	else
12740Sstevel@tonic-gate 		bgep->param_link_rx_pause = bgep->param_adv_pause;
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 	/*
12770Sstevel@tonic-gate 	 * Discover all the link partner's abilities.
12780Sstevel@tonic-gate 	 */
12790Sstevel@tonic-gate 	lpadv = bgep->serdes_lpadv;
12800Sstevel@tonic-gate 	if (lpadv != 0 && BIC(lpadv, AUTONEG_CODE_FAULT_MASK)) {
12810Sstevel@tonic-gate 		/*
12820Sstevel@tonic-gate 		 * No fault, so derive partner's capabilities
12830Sstevel@tonic-gate 		 */
12840Sstevel@tonic-gate 		bgep->param_lp_autoneg = B_TRUE;
12850Sstevel@tonic-gate 		bgep->param_lp_1000fdx = BIS(lpadv, AUTONEG_CODE_FULL_DUPLEX);
12860Sstevel@tonic-gate 		bgep->param_lp_1000hdx = BIS(lpadv, AUTONEG_CODE_HALF_DUPLEX);
12870Sstevel@tonic-gate 		bgep->param_lp_pause = BIS(lpadv, AUTONEG_CODE_PAUSE);
12880Sstevel@tonic-gate 		bgep->param_lp_asym_pause = BIS(lpadv, AUTONEG_CODE_ASYM_PAUSE);
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate 		/*
12910Sstevel@tonic-gate 		 * Pause direction resolution
12920Sstevel@tonic-gate 		 */
12930Sstevel@tonic-gate 		bgep->param_link_autoneg = B_TRUE;
12940Sstevel@tonic-gate 		if (bgep->param_adv_pause &&
12950Sstevel@tonic-gate 		    bgep->param_lp_pause) {
12960Sstevel@tonic-gate 			bgep->param_link_tx_pause = B_TRUE;
12970Sstevel@tonic-gate 			bgep->param_link_rx_pause = B_TRUE;
12980Sstevel@tonic-gate 		}
12990Sstevel@tonic-gate 		if (bgep->param_adv_asym_pause &&
13000Sstevel@tonic-gate 		    bgep->param_lp_asym_pause) {
13010Sstevel@tonic-gate 			if (bgep->param_adv_pause)
13020Sstevel@tonic-gate 				bgep->param_link_rx_pause = B_TRUE;
13030Sstevel@tonic-gate 			if (bgep->param_lp_pause)
13040Sstevel@tonic-gate 				bgep->param_link_tx_pause = B_TRUE;
13050Sstevel@tonic-gate 		}
13060Sstevel@tonic-gate 	}
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate 	/*
13090Sstevel@tonic-gate 	 * Step 12: update ndd-visible state parameters, BUT!
13100Sstevel@tonic-gate 	 * we don't transfer the new state to <link_state> just yet;
13110Sstevel@tonic-gate 	 * instead we mark the <link_state> as UNKNOWN, and our caller
13120Sstevel@tonic-gate 	 * will resolve it once the status has stopped changing and
13130Sstevel@tonic-gate 	 * been stable for several seconds.
13140Sstevel@tonic-gate 	 */
13150Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_serdes: link was %s speed %d duplex %d",
13160Sstevel@tonic-gate 		UPORDOWN(bgep->param_link_up),
13170Sstevel@tonic-gate 		bgep->param_link_speed,
13180Sstevel@tonic-gate 		bgep->param_link_duplex));
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 	if (linkup) {
13210Sstevel@tonic-gate 		bgep->param_link_up = B_TRUE;
13220Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
13230Sstevel@tonic-gate 		if (bgep->param_adv_1000fdx)
13240Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_FULL;
13250Sstevel@tonic-gate 		else
13260Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_HALF;
13270Sstevel@tonic-gate 		if (bgep->param_lp_autoneg && !bgep->param_lp_1000fdx)
13280Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_HALF;
13290Sstevel@tonic-gate 	} else {
13300Sstevel@tonic-gate 		bgep->param_link_up = B_FALSE;
13310Sstevel@tonic-gate 		bgep->param_link_speed = 0;
13320Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_UNKNOWN;
13330Sstevel@tonic-gate 	}
13340Sstevel@tonic-gate 	switch (bgep->param_link_duplex) {
13350Sstevel@tonic-gate 	default:
13360Sstevel@tonic-gate 	case LINK_DUPLEX_UNKNOWN:
13370Sstevel@tonic-gate 		bgep->link_mode_msg = "down";
13380Sstevel@tonic-gate 		break;
13390Sstevel@tonic-gate 
13400Sstevel@tonic-gate 	case LINK_DUPLEX_HALF:
13410Sstevel@tonic-gate 		bgep->link_mode_msg = "up 1000Mbps Half-Duplex";
13420Sstevel@tonic-gate 		break;
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate 	case LINK_DUPLEX_FULL:
13450Sstevel@tonic-gate 		bgep->link_mode_msg = "up 1000Mbps Full-Duplex";
13460Sstevel@tonic-gate 		break;
13470Sstevel@tonic-gate 	}
13480Sstevel@tonic-gate 	bgep->link_state = LINK_STATE_UNKNOWN;
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_serdes: link now %s speed %d duplex %d",
13510Sstevel@tonic-gate 		UPORDOWN(bgep->param_link_up),
13520Sstevel@tonic-gate 		bgep->param_link_speed,
13530Sstevel@tonic-gate 		bgep->param_link_duplex));
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 	return (B_TRUE);
13560Sstevel@tonic-gate }
13570Sstevel@tonic-gate 
13580Sstevel@tonic-gate static const phys_ops_t serdes_ops = {
13590Sstevel@tonic-gate 	bge_restart_serdes,
13600Sstevel@tonic-gate 	bge_update_serdes,
13610Sstevel@tonic-gate 	bge_check_serdes
13620Sstevel@tonic-gate };
13630Sstevel@tonic-gate 
13640Sstevel@tonic-gate /*
13650Sstevel@tonic-gate  * ========== Exported physical layer control routines ==========
13660Sstevel@tonic-gate  */
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate #undef	BGE_DBG
13690Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_PHYS	/* debug flag for this code	*/
13700Sstevel@tonic-gate 
13710Sstevel@tonic-gate /*
13720Sstevel@tonic-gate  * Here we have to determine which media we're using (copper or serdes).
13730Sstevel@tonic-gate  * Once that's done, we can initialise the physical layer appropriately.
13740Sstevel@tonic-gate  */
1375*1865Sdilpreet int
13760Sstevel@tonic-gate bge_phys_init(bge_t *bgep)
13770Sstevel@tonic-gate {
13780Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_init($%p)", (void *)bgep));
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 	mutex_enter(bgep->genlock);
13810Sstevel@tonic-gate 
13820Sstevel@tonic-gate 	/*
13830Sstevel@tonic-gate 	 * Probe for the (internal) PHY.  If it's not there, we'll assume
1384388Sly149593 	 * that this is a 5703/4S, with a SerDes interface rather than
1385388Sly149593 	 * a PHY. BCM5714S/BCM5715S are not supported.It are based on
1386388Sly149593 	 * BCM800x PHY.
13870Sstevel@tonic-gate 	 */
13880Sstevel@tonic-gate 	bgep->phy_mii_addr = 1;
13890Sstevel@tonic-gate 	if (bge_phy_probe(bgep)) {
13900Sstevel@tonic-gate 		bgep->chipid.flags &= ~CHIP_FLAG_SERDES;
13910Sstevel@tonic-gate 		bgep->phys_delta_time = BGE_PHY_STABLE_TIME;
13920Sstevel@tonic-gate 		bgep->physops = &copper_ops;
13930Sstevel@tonic-gate 	} else {
13940Sstevel@tonic-gate 		bgep->chipid.flags |= CHIP_FLAG_SERDES;
13950Sstevel@tonic-gate 		bgep->phys_delta_time = BGE_SERDES_STABLE_TIME;
13960Sstevel@tonic-gate 		bgep->physops = &serdes_ops;
13970Sstevel@tonic-gate 	}
13980Sstevel@tonic-gate 
1399*1865Sdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS) {
1400*1865Sdilpreet 		mutex_exit(bgep->genlock);
1401*1865Sdilpreet 		return (EIO);
1402*1865Sdilpreet 	}
1403*1865Sdilpreet 	if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK) {
1404*1865Sdilpreet 		mutex_exit(bgep->genlock);
1405*1865Sdilpreet 		return (EIO);
1406*1865Sdilpreet 	}
14070Sstevel@tonic-gate 	mutex_exit(bgep->genlock);
1408*1865Sdilpreet 	return (0);
14090Sstevel@tonic-gate }
14100Sstevel@tonic-gate 
14110Sstevel@tonic-gate /*
14120Sstevel@tonic-gate  * Reset the physical layer
14130Sstevel@tonic-gate  */
14140Sstevel@tonic-gate void
14150Sstevel@tonic-gate bge_phys_reset(bge_t *bgep)
14160Sstevel@tonic-gate {
14170Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_reset($%p)", (void *)bgep));
14180Sstevel@tonic-gate 
14190Sstevel@tonic-gate 	mutex_enter(bgep->genlock);
1420*1865Sdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS)
1421*1865Sdilpreet 		ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED);
1422*1865Sdilpreet 	if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK)
1423*1865Sdilpreet 		ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED);
14240Sstevel@tonic-gate 	mutex_exit(bgep->genlock);
14250Sstevel@tonic-gate }
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate /*
14280Sstevel@tonic-gate  * Reset and power off the physical layer.
14290Sstevel@tonic-gate  *
14300Sstevel@tonic-gate  * Another RESET should get it back to working, but it may take a few
14310Sstevel@tonic-gate  * seconds it may take a few moments to return to normal operation ...
14320Sstevel@tonic-gate  */
1433*1865Sdilpreet int
14340Sstevel@tonic-gate bge_phys_idle(bge_t *bgep)
14350Sstevel@tonic-gate {
14360Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_idle($%p)", (void *)bgep));
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1439*1865Sdilpreet 	return ((*bgep->physops->phys_restart)(bgep, B_TRUE));
14400Sstevel@tonic-gate }
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate /*
14430Sstevel@tonic-gate  * Synchronise the PHYSICAL layer's speed/duplex/autonegotiation capabilities
14440Sstevel@tonic-gate  * and advertisements with the required settings as specified by the various
14450Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
14460Sstevel@tonic-gate  *
14470Sstevel@tonic-gate  * We always reset the PHYSICAL layer and reprogram *all* relevant registers.
14480Sstevel@tonic-gate  * This is expected to cause the link to go down, and then back up again once
14490Sstevel@tonic-gate  * the link is stable and autonegotiation (if enabled) is complete.  We should
14500Sstevel@tonic-gate  * get a link state change interrupt somewhere along the way ...
14510Sstevel@tonic-gate  *
14520Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
14530Sstevel@tonic-gate  */
1454*1865Sdilpreet int
14550Sstevel@tonic-gate bge_phys_update(bge_t *bgep)
14560Sstevel@tonic-gate {
14570Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_update($%p)", (void *)bgep));
14580Sstevel@tonic-gate 
14590Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1460*1865Sdilpreet 	return ((*bgep->physops->phys_update)(bgep));
14610Sstevel@tonic-gate }
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate #undef	BGE_DBG
14640Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_LINK	/* debug flag for this code	*/
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate /*
14670Sstevel@tonic-gate  * Read the link status and determine whether anything's changed ...
14680Sstevel@tonic-gate  *
14690Sstevel@tonic-gate  * This routine should be called whenever the chip flags a change
14700Sstevel@tonic-gate  * in the hardware link state, and repeatedly for several seconds
14710Sstevel@tonic-gate  * afterwards, until we're sure the state has stabilised (sometimes
14720Sstevel@tonic-gate  * it goes up and down several times during autonegotiation before
14730Sstevel@tonic-gate  * settling on the proper configuration).  This routine applies
14740Sstevel@tonic-gate  * timing-based heuristics to determine when the state is stable.
14750Sstevel@tonic-gate  *
14760Sstevel@tonic-gate  * This routine returns B_FALSE if the link state has not changed,
14770Sstevel@tonic-gate  * or if it has changed, but hasn't settled for long enough yet.  It
14780Sstevel@tonic-gate  * returns B_TRUE when the change to the new state should be accepted.
14790Sstevel@tonic-gate  * In such a case, the param_* variables give the new hardware state,
14800Sstevel@tonic-gate  * which the caller should use to update link_state etc.
14810Sstevel@tonic-gate  *
14820Sstevel@tonic-gate  * The caller must already hold <genlock>
14830Sstevel@tonic-gate  */
14840Sstevel@tonic-gate boolean_t
14850Sstevel@tonic-gate bge_phys_check(bge_t *bgep)
14860Sstevel@tonic-gate {
14870Sstevel@tonic-gate 	int32_t orig_state;
14880Sstevel@tonic-gate 	boolean_t recheck;
14890Sstevel@tonic-gate 	boolean_t linkup;
14900Sstevel@tonic-gate 	hrtime_t deltat;
14910Sstevel@tonic-gate 	hrtime_t now;
14920Sstevel@tonic-gate 
14930Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_check($%p)", (void *)bgep));
14940Sstevel@tonic-gate 
14950Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
14960Sstevel@tonic-gate 
14970Sstevel@tonic-gate 	linkup = bgep->param_link_up;
14980Sstevel@tonic-gate 	orig_state = bgep->link_state;
14990Sstevel@tonic-gate 	recheck = orig_state == LINK_STATE_UNKNOWN;
15000Sstevel@tonic-gate 	recheck = (*bgep->physops->phys_check)(bgep, recheck);
15010Sstevel@tonic-gate 	if (!recheck)
15020Sstevel@tonic-gate 		return (B_FALSE);
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate 	/*
15050Sstevel@tonic-gate 	 * At this point, the check_*_link() function above has detected
15060Sstevel@tonic-gate 	 * a change and updated the param_* variables to show what the
15070Sstevel@tonic-gate 	 * latest hardware state seems to be -- but it might still be
15080Sstevel@tonic-gate 	 * changing.
15090Sstevel@tonic-gate 	 *
15100Sstevel@tonic-gate 	 * The link_state must now be UNKNOWN, but if it was previously
15110Sstevel@tonic-gate 	 * UP, we want to recognise this immediately, whereas in any other
15120Sstevel@tonic-gate 	 * case (e.g. DOWN->UP) we don't accept it until a few seconds have
15130Sstevel@tonic-gate 	 * elapsed, to give the hardware time to settle.
15140Sstevel@tonic-gate 	 */
15150Sstevel@tonic-gate 	now = gethrtime();
15160Sstevel@tonic-gate 	deltat = now - bgep->phys_event_time;
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 	BGE_DEBUG(("bge_phys_check: link was %d/%s now %d/%s",
15190Sstevel@tonic-gate 		orig_state, UPORDOWN(linkup),
15200Sstevel@tonic-gate 		bgep->link_state, UPORDOWN(bgep->param_link_up)));
15210Sstevel@tonic-gate 	BGE_DEBUG(("bge_phys_check: update %lld change %lld "
15220Sstevel@tonic-gate 			"now %lld delta %lld",
15230Sstevel@tonic-gate 		bgep->phys_write_time, bgep->phys_event_time, now, deltat));
15240Sstevel@tonic-gate 
15250Sstevel@tonic-gate 	if (orig_state == LINK_STATE_UP)
15260Sstevel@tonic-gate 		return (B_TRUE);
15270Sstevel@tonic-gate 	else
15280Sstevel@tonic-gate 		return (deltat > bgep->phys_delta_time);
15290Sstevel@tonic-gate }
1530