xref: /titanic_52/usr/src/uts/common/io/bge/bge_mii.c (revision 087a28d18c24cf4938e8a2617b5127a2fd29ddf4)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
562387023Sdduvall  * Common Development and Distribution License (the "License").
662387023Sdduvall  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2162387023Sdduvall 
227c478bd9Sstevel@tonic-gate /*
23*087a28d1SDavid Gwynne  * Copyright (c) 2010-2013, by Broadcom, Inc.
24*087a28d1SDavid Gwynne  * All Rights Reserved.
25*087a28d1SDavid Gwynne  */
26*087a28d1SDavid Gwynne 
27*087a28d1SDavid Gwynne /*
28*087a28d1SDavid Gwynne  * Copyright (c) 2002, 2010, Oracle and/or its affiliates.
29*087a28d1SDavid Gwynne  * All rights reserved.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
32f724721bSzh199473 #include "bge_impl.h"
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate /*
357c478bd9Sstevel@tonic-gate  * Bit test macros, returning boolean_t values
367c478bd9Sstevel@tonic-gate  */
377c478bd9Sstevel@tonic-gate #define	BIS(w, b)	(((w) & (b)) ? B_TRUE : B_FALSE)
387c478bd9Sstevel@tonic-gate #define	BIC(w, b)	(((w) & (b)) ? B_FALSE : B_TRUE)
397c478bd9Sstevel@tonic-gate #define	UPORDOWN(x)	((x) ? "up" : "down")
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate  * ========== Copper (PHY) support ==========
437c478bd9Sstevel@tonic-gate  */
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_PHY	/* debug flag for this code	*/
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate /*
487c478bd9Sstevel@tonic-gate  * #defines:
497c478bd9Sstevel@tonic-gate  *	BGE_COPPER_WIRESPEED controls whether the Broadcom WireSpeed(tm)
507c478bd9Sstevel@tonic-gate  *	feature is enabled.  We need to recheck whether this can be
517c478bd9Sstevel@tonic-gate  *	enabled; at one time it seemed to interact unpleasantly with the
527c478bd9Sstevel@tonic-gate  *	loopback modes.
537c478bd9Sstevel@tonic-gate  *
547c478bd9Sstevel@tonic-gate  *	BGE_COPPER_IDLEOFF controls whether the (copper) PHY power is
557c478bd9Sstevel@tonic-gate  *	turned off when the PHY is idled i.e. during driver suspend().
567c478bd9Sstevel@tonic-gate  *	For now this is disabled because the chip doesn't seem to
577c478bd9Sstevel@tonic-gate  *	resume cleanly if the PHY power is turned off.
587c478bd9Sstevel@tonic-gate  */
597c478bd9Sstevel@tonic-gate #define	BGE_COPPER_WIRESPEED	B_TRUE
607c478bd9Sstevel@tonic-gate #define	BGE_COPPER_IDLEOFF	B_FALSE
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate /*
637c478bd9Sstevel@tonic-gate  * The arrays below can be indexed by the MODE bits from the Auxiliary
647c478bd9Sstevel@tonic-gate  * Status register to determine the current speed/duplex settings.
657c478bd9Sstevel@tonic-gate  */
667c478bd9Sstevel@tonic-gate static const int16_t bge_copper_link_speed[] = {
677c478bd9Sstevel@tonic-gate 	0,				/* MII_AUX_STATUS_MODE_NONE	*/
687c478bd9Sstevel@tonic-gate 	10,				/* MII_AUX_STATUS_MODE_10_H	*/
697c478bd9Sstevel@tonic-gate 	10,				/* MII_AUX_STATUS_MODE_10_F	*/
707c478bd9Sstevel@tonic-gate 	100,				/* MII_AUX_STATUS_MODE_100_H	*/
717c478bd9Sstevel@tonic-gate 	0,				/* MII_AUX_STATUS_MODE_100_4	*/
727c478bd9Sstevel@tonic-gate 	100,				/* MII_AUX_STATUS_MODE_100_F	*/
737c478bd9Sstevel@tonic-gate 	1000,				/* MII_AUX_STATUS_MODE_1000_H	*/
747c478bd9Sstevel@tonic-gate 	1000				/* MII_AUX_STATUS_MODE_1000_F	*/
757c478bd9Sstevel@tonic-gate };
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate static const int8_t bge_copper_link_duplex[] = {
787c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_NONE	*/
797c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_10_H	*/
807c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_10_F	*/
817c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_100_H	*/
827c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_100_4	*/
837c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_100_F	*/
847c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_1000_H	*/
857c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_FULL		/* MII_AUX_STATUS_MODE_1000_F	*/
867c478bd9Sstevel@tonic-gate };
877c478bd9Sstevel@tonic-gate 
885a506a18Syong tan - Sun Microsystems - Beijing China static const int16_t bge_copper_link_speed_5906[] = {
895a506a18Syong tan - Sun Microsystems - Beijing China 	0,				/* MII_AUX_STATUS_MODE_NONE	*/
905a506a18Syong tan - Sun Microsystems - Beijing China 	10,				/* MII_AUX_STATUS_MODE_10_H	*/
915a506a18Syong tan - Sun Microsystems - Beijing China 	10,				/* MII_AUX_STATUS_MODE_10_F	*/
925a506a18Syong tan - Sun Microsystems - Beijing China 	100,				/* MII_AUX_STATUS_MODE_100_H	*/
935a506a18Syong tan - Sun Microsystems - Beijing China 	0,				/* MII_AUX_STATUS_MODE_100_4	*/
945a506a18Syong tan - Sun Microsystems - Beijing China 	100,				/* MII_AUX_STATUS_MODE_100_F	*/
955a506a18Syong tan - Sun Microsystems - Beijing China 	0,				/* MII_AUX_STATUS_MODE_1000_H	*/
965a506a18Syong tan - Sun Microsystems - Beijing China 	0				/* MII_AUX_STATUS_MODE_1000_F	*/
975a506a18Syong tan - Sun Microsystems - Beijing China };
985a506a18Syong tan - Sun Microsystems - Beijing China 
995a506a18Syong tan - Sun Microsystems - Beijing China static const int8_t bge_copper_link_duplex_5906[] = {
1005a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_NONE	*/
1015a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_10_H	*/
1025a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_10_F	*/
1035a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_100_H	*/
1045a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_100_4	*/
1055a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_100_F	*/
1065a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_1000_H	*/
1075a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_UNKNOWN		/* MII_AUX_STATUS_MODE_1000_F	*/
1085a506a18Syong tan - Sun Microsystems - Beijing China };
1095a506a18Syong tan - Sun Microsystems - Beijing China 
1107c478bd9Sstevel@tonic-gate #if	BGE_DEBUGGING
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate static void
1137c478bd9Sstevel@tonic-gate bge_phydump(bge_t *bgep, uint16_t mii_status, uint16_t aux)
1147c478bd9Sstevel@tonic-gate {
1157c478bd9Sstevel@tonic-gate 	uint16_t regs[32];
1167c478bd9Sstevel@tonic-gate 	int i;
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 	for (i = 0; i < 32; ++i)
1217c478bd9Sstevel@tonic-gate 		switch (i) {
1227c478bd9Sstevel@tonic-gate 		default:
1237c478bd9Sstevel@tonic-gate 			regs[i] = bge_mii_get16(bgep, i);
1247c478bd9Sstevel@tonic-gate 			break;
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 		case MII_STATUS:
1277c478bd9Sstevel@tonic-gate 			regs[i] = mii_status;
1287c478bd9Sstevel@tonic-gate 			break;
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 		case MII_AUX_STATUS:
1317c478bd9Sstevel@tonic-gate 			regs[i] = aux;
1327c478bd9Sstevel@tonic-gate 			break;
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 		case 0x0b: case 0x0c: case 0x0d: case 0x0e:
1357c478bd9Sstevel@tonic-gate 		case 0x15: case 0x16: case 0x17:
1367c478bd9Sstevel@tonic-gate 		case 0x1c:
1377c478bd9Sstevel@tonic-gate 		case 0x1f:
1387c478bd9Sstevel@tonic-gate 			/* reserved registers -- don't read these */
1397c478bd9Sstevel@tonic-gate 			regs[i] = 0;
1407c478bd9Sstevel@tonic-gate 			break;
1417c478bd9Sstevel@tonic-gate 		}
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	for (i = 0; i < 32; i += 8)
1447c478bd9Sstevel@tonic-gate 		BGE_DEBUG(("bge_phydump: "
1457c478bd9Sstevel@tonic-gate 		    "0x%04x %04x %04x %04x %04x %04x %04x %04x",
1467c478bd9Sstevel@tonic-gate 		    regs[i+0], regs[i+1], regs[i+2], regs[i+3],
1477c478bd9Sstevel@tonic-gate 		    regs[i+4], regs[i+5], regs[i+6], regs[i+7]));
1487c478bd9Sstevel@tonic-gate }
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate #endif	/* BGE_DEBUGGING */
1517c478bd9Sstevel@tonic-gate 
152*087a28d1SDavid Gwynne static void
153*087a28d1SDavid Gwynne bge_phy_toggle_auxctl_smdsp(bge_t *bgep,
154*087a28d1SDavid Gwynne                             boolean_t enable)
155*087a28d1SDavid Gwynne {
156*087a28d1SDavid Gwynne 	uint16_t val;
157*087a28d1SDavid Gwynne 
158*087a28d1SDavid Gwynne 	val = bge_mii_get16(bgep, MII_AUX_CONTROL);
159*087a28d1SDavid Gwynne 
160*087a28d1SDavid Gwynne 	if (enable) {
161*087a28d1SDavid Gwynne 		val |= MII_AUX_CTRL_SMDSP_ENA;
162*087a28d1SDavid Gwynne 	} else {
163*087a28d1SDavid Gwynne 		val &= ~MII_AUX_CTRL_SMDSP_ENA;
164*087a28d1SDavid Gwynne 	}
165*087a28d1SDavid Gwynne 
166*087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_AUX_CONTROL, (val | MII_AUX_CTRL_TX_6DB));
167*087a28d1SDavid Gwynne }
168*087a28d1SDavid Gwynne 
1697c478bd9Sstevel@tonic-gate /*
1707c478bd9Sstevel@tonic-gate  * Basic low-level function to probe for a PHY
1717c478bd9Sstevel@tonic-gate  *
1727c478bd9Sstevel@tonic-gate  * Returns TRUE if the PHY responds with valid data, FALSE otherwise
1737c478bd9Sstevel@tonic-gate  */
1747c478bd9Sstevel@tonic-gate static boolean_t
1757c478bd9Sstevel@tonic-gate bge_phy_probe(bge_t *bgep)
1767c478bd9Sstevel@tonic-gate {
1770c50e2bcSgh162552 	uint16_t miicfg;
1780c50e2bcSgh162552 	uint32_t nicsig, niccfg;
179*087a28d1SDavid Gwynne 	int i;
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_probe($%p)", (void *)bgep));
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1847c478bd9Sstevel@tonic-gate 
1850c50e2bcSgh162552 	nicsig = bge_nic_read32(bgep, BGE_NIC_DATA_SIG_ADDR);
1860c50e2bcSgh162552 	if (nicsig == BGE_NIC_DATA_SIG) {
1870c50e2bcSgh162552 		niccfg = bge_nic_read32(bgep, BGE_NIC_DATA_NIC_CFG_ADDR);
1880c50e2bcSgh162552 		switch (niccfg & BGE_NIC_CFG_PHY_TYPE_MASK) {
1890c50e2bcSgh162552 		default:
1900c50e2bcSgh162552 		case BGE_NIC_CFG_PHY_TYPE_COPPER:
1910c50e2bcSgh162552 			return (B_TRUE);
1920c50e2bcSgh162552 		case BGE_NIC_CFG_PHY_TYPE_FIBER:
1930c50e2bcSgh162552 			return (B_FALSE);
1940c50e2bcSgh162552 		}
1950c50e2bcSgh162552 	} else {
1967c478bd9Sstevel@tonic-gate 		/*
1977c478bd9Sstevel@tonic-gate 		 * Read the MII_STATUS register twice, in
1987c478bd9Sstevel@tonic-gate 		 * order to clear any sticky bits (but they should
1997c478bd9Sstevel@tonic-gate 		 * have been cleared by the RESET, I think).
2007c478bd9Sstevel@tonic-gate 		 */
201*087a28d1SDavid Gwynne 		for (i = 0; i < 100; i++) {
202*087a28d1SDavid Gwynne 			drv_usecwait(40);
2030c50e2bcSgh162552 			miicfg = bge_mii_get16(bgep, MII_STATUS);
204*087a28d1SDavid Gwynne 		}
2050c50e2bcSgh162552 		BGE_DEBUG(("bge_phy_probe: status 0x%x", miicfg));
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 		/*
2087c478bd9Sstevel@tonic-gate 		 * Now check the value read; it should have at least one bit set
2097c478bd9Sstevel@tonic-gate 		 * (for the device capabilities) and at least one clear (one of
2107c478bd9Sstevel@tonic-gate 		 * the error bits). So if we see all 0s or all 1s, there's a
2117c478bd9Sstevel@tonic-gate 		 * problem.  In particular, bge_mii_get16() returns all 1s if
2127c478bd9Sstevel@tonic-gate 		 * communications fails ...
2137c478bd9Sstevel@tonic-gate 		 */
2140c50e2bcSgh162552 		switch (miicfg) {
2157c478bd9Sstevel@tonic-gate 		case 0x0000:
2167c478bd9Sstevel@tonic-gate 		case 0xffff:
2177c478bd9Sstevel@tonic-gate 			return (B_FALSE);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 		default:
2207c478bd9Sstevel@tonic-gate 			return (B_TRUE);
2217c478bd9Sstevel@tonic-gate 		}
2227c478bd9Sstevel@tonic-gate 	}
2230c50e2bcSgh162552 }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate /*
2267c478bd9Sstevel@tonic-gate  * Basic low-level function to reset the PHY.
2277c478bd9Sstevel@tonic-gate  * Doesn't incorporate any special-case workarounds.
2287c478bd9Sstevel@tonic-gate  *
2297c478bd9Sstevel@tonic-gate  * Returns TRUE on success, FALSE if the RESET bit doesn't clear
2307c478bd9Sstevel@tonic-gate  */
2317c478bd9Sstevel@tonic-gate static boolean_t
2327c478bd9Sstevel@tonic-gate bge_phy_reset(bge_t *bgep)
2337c478bd9Sstevel@tonic-gate {
2347c478bd9Sstevel@tonic-gate 	uint16_t control;
2357c478bd9Sstevel@tonic-gate 	uint_t count;
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_reset($%p)", (void *)bgep));
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
2407c478bd9Sstevel@tonic-gate 
2415a506a18Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
2425a506a18Syong tan - Sun Microsystems - Beijing China 		drv_usecwait(40);
2435a506a18Syong tan - Sun Microsystems - Beijing China 		/* put PHY into ready state */
2445a506a18Syong tan - Sun Microsystems - Beijing China 		bge_reg_clr32(bgep, MISC_CONFIG_REG, MISC_CONFIG_EPHY_IDDQ);
2455a506a18Syong tan - Sun Microsystems - Beijing China 		(void) bge_reg_get32(bgep, MISC_CONFIG_REG); /* flush */
2465a506a18Syong tan - Sun Microsystems - Beijing China 		drv_usecwait(40);
2475a506a18Syong tan - Sun Microsystems - Beijing China 	}
2485a506a18Syong tan - Sun Microsystems - Beijing China 
2497c478bd9Sstevel@tonic-gate 	/*
2507c478bd9Sstevel@tonic-gate 	 * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear
2517c478bd9Sstevel@tonic-gate 	 */
2527c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_RESET);
2537c478bd9Sstevel@tonic-gate 	for (count = 0; ++count < 1000; ) {
2547c478bd9Sstevel@tonic-gate 		drv_usecwait(5);
2557c478bd9Sstevel@tonic-gate 		control = bge_mii_get16(bgep, MII_CONTROL);
2567c478bd9Sstevel@tonic-gate 		if (BIC(control, MII_CONTROL_RESET))
2577c478bd9Sstevel@tonic-gate 			return (B_TRUE);
2587c478bd9Sstevel@tonic-gate 	}
2597c478bd9Sstevel@tonic-gate 
2605a506a18Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5906_SERIES_CHIPSETS(bgep))
2612fec4481SCarson Tan 		(void) bge_adj_volt_5906(bgep);
2625a506a18Syong tan - Sun Microsystems - Beijing China 
2637c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_phy_reset: FAILED, control now 0x%x", control));
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	return (B_FALSE);
2667c478bd9Sstevel@tonic-gate }
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate /*
2697c478bd9Sstevel@tonic-gate  * Basic low-level function to powerdown the PHY, if supported
2707c478bd9Sstevel@tonic-gate  * If powerdown support is compiled out, this function does nothing.
2717c478bd9Sstevel@tonic-gate  */
2727c478bd9Sstevel@tonic-gate static void
2737c478bd9Sstevel@tonic-gate bge_phy_powerdown(bge_t *bgep)
2747c478bd9Sstevel@tonic-gate {
2757c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_powerdown"));
2767c478bd9Sstevel@tonic-gate #if	BGE_COPPER_IDLEOFF
2777c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_PWRDN);
2787c478bd9Sstevel@tonic-gate #endif	/* BGE_COPPER_IDLEOFF */
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  * The following functions are based on sample code provided by
2837c478bd9Sstevel@tonic-gate  * Broadcom (20-June-2003), and implement workarounds said to be
2847c478bd9Sstevel@tonic-gate  * required on the early revisions of the BCM5703/4C.
2857c478bd9Sstevel@tonic-gate  *
2867c478bd9Sstevel@tonic-gate  * The registers and values used are mostly UNDOCUMENTED, and
2877c478bd9Sstevel@tonic-gate  * therefore don't have symbolic names ;-(
2887c478bd9Sstevel@tonic-gate  *
2897c478bd9Sstevel@tonic-gate  * Many of the comments are straight out of the Broadcom code:
2907c478bd9Sstevel@tonic-gate  * even where the code has been restructured, the original
2917c478bd9Sstevel@tonic-gate  * comments have been preserved in order to explain what these
2927c478bd9Sstevel@tonic-gate  * undocumented registers & values are all about ...
2937c478bd9Sstevel@tonic-gate  */
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate static void
2967c478bd9Sstevel@tonic-gate bge_phy_macro_wait(bge_t *bgep)
2977c478bd9Sstevel@tonic-gate {
2987c478bd9Sstevel@tonic-gate 	uint_t count;
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	for (count = 100; --count; )
3017c478bd9Sstevel@tonic-gate 		if ((bge_mii_get16(bgep, 0x16) & 0x1000) == 0)
3027c478bd9Sstevel@tonic-gate 			break;
3037c478bd9Sstevel@tonic-gate }
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate /*
3067c478bd9Sstevel@tonic-gate  * PHY test data pattern:
3077c478bd9Sstevel@tonic-gate  *
3087c478bd9Sstevel@tonic-gate  * For 5703/04, each DFE TAP has 21-bits (low word 15, hi word 6)
3097c478bd9Sstevel@tonic-gate  * For 5705,    each DFE TAP has 19-bits (low word 15, hi word 4)
3107c478bd9Sstevel@tonic-gate  * For simplicity, we check only 19-bits, so we don't have to
3117c478bd9Sstevel@tonic-gate  * distinguish which chip it is.
3127c478bd9Sstevel@tonic-gate  * the LO word contains 15 bits, make sure pattern data is < 0x7fff
3137c478bd9Sstevel@tonic-gate  * the HI word contains  6 bits, make sure pattern data is < 0x003f
3147c478bd9Sstevel@tonic-gate  */
3157c478bd9Sstevel@tonic-gate #define	N_CHANNELS	4
3167c478bd9Sstevel@tonic-gate #define	N_TAPS		3
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate static struct {
3197c478bd9Sstevel@tonic-gate 	uint16_t	lo;
3207c478bd9Sstevel@tonic-gate 	uint16_t	hi;
3217c478bd9Sstevel@tonic-gate } tap_data[N_CHANNELS][N_TAPS] = {
3227c478bd9Sstevel@tonic-gate 	{
3237c478bd9Sstevel@tonic-gate 		{ 0x5555, 0x0005 },	/* ch0, TAP 0, LO/HI pattern */
3247c478bd9Sstevel@tonic-gate 		{ 0x2aaa, 0x000a },	/* ch0, TAP 1, LO/HI pattern */
3257c478bd9Sstevel@tonic-gate 		{ 0x3456, 0x0003 }	/* ch0, TAP 2, LO/HI pattern */
3267c478bd9Sstevel@tonic-gate 	},
3277c478bd9Sstevel@tonic-gate 	{
3287c478bd9Sstevel@tonic-gate 		{ 0x2aaa, 0x000a },	/* ch1, TAP 0, LO/HI pattern */
3297c478bd9Sstevel@tonic-gate 		{ 0x3333, 0x0003 },	/* ch1, TAP 1, LO/HI pattern */
3307c478bd9Sstevel@tonic-gate 		{ 0x789a, 0x0005 }	/* ch1, TAP 2, LO/HI pattern */
3317c478bd9Sstevel@tonic-gate 	},
3327c478bd9Sstevel@tonic-gate 	{
3337c478bd9Sstevel@tonic-gate 		{ 0x5a5a, 0x0005 },	/* ch2, TAP 0, LO/HI pattern */
3347c478bd9Sstevel@tonic-gate 		{ 0x2a6a, 0x000a },	/* ch2, TAP 1, LO/HI pattern */
3357c478bd9Sstevel@tonic-gate 		{ 0x1bcd, 0x0003 }	/* ch2, TAP 2, LO/HI pattern */
3367c478bd9Sstevel@tonic-gate 	},
3377c478bd9Sstevel@tonic-gate 	{
3387c478bd9Sstevel@tonic-gate 		{ 0x2a5a, 0x000a },	/* ch3, TAP 0, LO/HI pattern */
3397c478bd9Sstevel@tonic-gate 		{ 0x33c3, 0x0003 },	/* ch3, TAP 1, LO/HI pattern */
3407c478bd9Sstevel@tonic-gate 		{ 0x2ef1, 0x0005 }	/* ch3, TAP 2, LO/HI pattern */
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate };
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate /*
3457c478bd9Sstevel@tonic-gate  * Check whether the PHY has locked up after a RESET.
3467c478bd9Sstevel@tonic-gate  *
3477c478bd9Sstevel@tonic-gate  * Returns TRUE if it did, FALSE is it's OK ;-)
3487c478bd9Sstevel@tonic-gate  */
3497c478bd9Sstevel@tonic-gate static boolean_t
3507c478bd9Sstevel@tonic-gate bge_phy_locked_up(bge_t *bgep)
3517c478bd9Sstevel@tonic-gate {
3527c478bd9Sstevel@tonic-gate 	uint16_t dataLo;
3537c478bd9Sstevel@tonic-gate 	uint16_t dataHi;
3547c478bd9Sstevel@tonic-gate 	uint_t chan;
3557c478bd9Sstevel@tonic-gate 	uint_t tap;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	/*
3587c478bd9Sstevel@tonic-gate 	 * Check TAPs for all 4 channels, as soon as we see a lockup
3597c478bd9Sstevel@tonic-gate 	 * we'll stop checking.
3607c478bd9Sstevel@tonic-gate 	 */
3617c478bd9Sstevel@tonic-gate 	for (chan = 0; chan < N_CHANNELS; ++chan) {
3627c478bd9Sstevel@tonic-gate 		/* Select channel and set TAP index to 0 */
3637c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
3647c478bd9Sstevel@tonic-gate 		/* Freeze filter again just to be safe */
3657c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0002);
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 		/*
3687c478bd9Sstevel@tonic-gate 		 * Write fixed pattern to the RAM, 3 TAPs for
3697c478bd9Sstevel@tonic-gate 		 * each channel, each TAP have 2 WORDs (LO/HI)
3707c478bd9Sstevel@tonic-gate 		 */
3717c478bd9Sstevel@tonic-gate 		for (tap = 0; tap < N_TAPS; ++tap) {
3727c478bd9Sstevel@tonic-gate 			bge_mii_put16(bgep, 0x15, tap_data[chan][tap].lo);
3737c478bd9Sstevel@tonic-gate 			bge_mii_put16(bgep, 0x15, tap_data[chan][tap].hi);
3747c478bd9Sstevel@tonic-gate 		}
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 		/*
3777c478bd9Sstevel@tonic-gate 		 * Active PHY's Macro operation to write DFE
3787c478bd9Sstevel@tonic-gate 		 * TAP from RAM, and wait for Macro to complete.
3797c478bd9Sstevel@tonic-gate 		 */
3807c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0202);
3817c478bd9Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 		/*
3847c478bd9Sstevel@tonic-gate 		 * Done with write phase, now begin read phase.
3857c478bd9Sstevel@tonic-gate 		 */
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 		/* Select channel and set TAP index to 0 */
3887c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 		/*
3917c478bd9Sstevel@tonic-gate 		 * Active PHY's Macro operation to load DFE
3927c478bd9Sstevel@tonic-gate 		 * TAP to RAM, and wait for Macro to complete
3937c478bd9Sstevel@tonic-gate 		 */
3947c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0082);
3957c478bd9Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 		/* Enable "pre-fetch" */
3987c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0802);
3997c478bd9Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 		/*
4027c478bd9Sstevel@tonic-gate 		 * Read back the TAP values.  3 TAPs for each
4037c478bd9Sstevel@tonic-gate 		 * channel, each TAP have 2 WORDs (LO/HI)
4047c478bd9Sstevel@tonic-gate 		 */
4057c478bd9Sstevel@tonic-gate 		for (tap = 0; tap < N_TAPS; ++tap) {
4067c478bd9Sstevel@tonic-gate 			/*
4077c478bd9Sstevel@tonic-gate 			 * Read Lo/Hi then wait for 'done' is faster.
4087c478bd9Sstevel@tonic-gate 			 * For DFE TAP, the HI word contains 6 bits,
4097c478bd9Sstevel@tonic-gate 			 * LO word contains 15 bits
4107c478bd9Sstevel@tonic-gate 			 */
4117c478bd9Sstevel@tonic-gate 			dataLo = bge_mii_get16(bgep, 0x15) & 0x7fff;
4127c478bd9Sstevel@tonic-gate 			dataHi = bge_mii_get16(bgep, 0x15) & 0x003f;
4137c478bd9Sstevel@tonic-gate 			bge_phy_macro_wait(bgep);
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 			/*
4167c478bd9Sstevel@tonic-gate 			 * Check if what we wrote is what we read back.
4177c478bd9Sstevel@tonic-gate 			 * If failed, then the PHY is locked up, we need
4187c478bd9Sstevel@tonic-gate 			 * to do PHY reset again
4197c478bd9Sstevel@tonic-gate 			 */
4207c478bd9Sstevel@tonic-gate 			if (dataLo != tap_data[chan][tap].lo)
4217c478bd9Sstevel@tonic-gate 				return (B_TRUE);	/* wedged!	*/
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 			if (dataHi != tap_data[chan][tap].hi)
4247c478bd9Sstevel@tonic-gate 				return (B_TRUE);	/* wedged!	*/
4257c478bd9Sstevel@tonic-gate 		}
4267c478bd9Sstevel@tonic-gate 	}
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 	/*
4297c478bd9Sstevel@tonic-gate 	 * The PHY isn't locked up ;-)
4307c478bd9Sstevel@tonic-gate 	 */
4317c478bd9Sstevel@tonic-gate 	return (B_FALSE);
4327c478bd9Sstevel@tonic-gate }
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate  * Special-case code to reset the PHY on the 5702/5703/5704C/5705/5782.
4367c478bd9Sstevel@tonic-gate  * Tries up to 5 times to recover from failure to reset or PHY lockup.
4377c478bd9Sstevel@tonic-gate  *
4387c478bd9Sstevel@tonic-gate  * Returns TRUE on success, FALSE if there's an unrecoverable problem
4397c478bd9Sstevel@tonic-gate  */
4407c478bd9Sstevel@tonic-gate static boolean_t
4417c478bd9Sstevel@tonic-gate bge_phy_reset_and_check(bge_t *bgep)
4427c478bd9Sstevel@tonic-gate {
4437c478bd9Sstevel@tonic-gate 	boolean_t reset_success;
4447c478bd9Sstevel@tonic-gate 	boolean_t phy_locked;
4457c478bd9Sstevel@tonic-gate 	uint16_t extctrl;
4461eff5f77SRijawanemohammadhusen Nadaf 	uint16_t gigctrl;
4477c478bd9Sstevel@tonic-gate 	uint_t retries;
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	for (retries = 0; retries < 5; ++retries) {
4507c478bd9Sstevel@tonic-gate 		/* Issue a phy reset, and wait for reset to complete */
4517c478bd9Sstevel@tonic-gate 		/* Assuming reset is successful first */
4527c478bd9Sstevel@tonic-gate 		reset_success = bge_phy_reset(bgep);
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 		/*
4557c478bd9Sstevel@tonic-gate 		 * Now go check the DFE TAPs to see if locked up, but
4567c478bd9Sstevel@tonic-gate 		 * first, we need to set up PHY so we can read DFE
4577c478bd9Sstevel@tonic-gate 		 * TAPs.
4587c478bd9Sstevel@tonic-gate 		 */
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 		/*
4617c478bd9Sstevel@tonic-gate 		 * Disable Transmitter and Interrupt, while we play
4627c478bd9Sstevel@tonic-gate 		 * with the PHY registers, so the link partner won't
4637c478bd9Sstevel@tonic-gate 		 * see any strange data and the Driver won't see any
4647c478bd9Sstevel@tonic-gate 		 * interrupts.
4657c478bd9Sstevel@tonic-gate 		 */
4667c478bd9Sstevel@tonic-gate 		extctrl = bge_mii_get16(bgep, 0x10);
4677c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x10, extctrl | 0x3000);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 		/* Setup Full-Duplex, 1000 mbps */
4707c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x0, 0x0140);
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 		/* Set to Master mode */
4731eff5f77SRijawanemohammadhusen Nadaf 		gigctrl = bge_mii_get16(bgep, 0x9);
4747c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x9, 0x1800);
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 		/* Enable SM_DSP_CLOCK & 6dB */
4777c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x18, 0x0c00);	/* "the ADC fix" */
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 		/* Work-arounds */
4807c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x201f);
4817c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x2aaa);
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 		/* More workarounds */
4847c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x000a);
4857c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x0323);	/* "the Gamma fix" */
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 		/* Blocks the PHY control access */
4887c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x8005);
4897c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x0800);
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 		/* Test whether PHY locked up ;-( */
4927c478bd9Sstevel@tonic-gate 		phy_locked = bge_phy_locked_up(bgep);
4937c478bd9Sstevel@tonic-gate 		if (reset_success && !phy_locked)
4947c478bd9Sstevel@tonic-gate 			break;
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 		/*
4977c478bd9Sstevel@tonic-gate 		 * Some problem here ... log it & retry
4987c478bd9Sstevel@tonic-gate 		 */
4997c478bd9Sstevel@tonic-gate 		if (!reset_success)
5007c478bd9Sstevel@tonic-gate 			BGE_REPORT((bgep, "PHY didn't reset!"));
5017c478bd9Sstevel@tonic-gate 		if (phy_locked)
5027c478bd9Sstevel@tonic-gate 			BGE_REPORT((bgep, "PHY locked up!"));
5037c478bd9Sstevel@tonic-gate 	}
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	/* Remove block phy control */
5067c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x17, 0x8005);
5077c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x15, 0x0000);
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	/* Unfreeze DFE TAP filter for all channels */
5107c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x17, 0x8200);
5117c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x16, 0x0000);
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	/* Restore PHY back to operating state */
5147c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x18, 0x0400);
5157c478bd9Sstevel@tonic-gate 
5161eff5f77SRijawanemohammadhusen Nadaf 	/* Restore 1000BASE-T Control Register */
5171eff5f77SRijawanemohammadhusen Nadaf 	bge_mii_put16(bgep, 0x9, gigctrl);
5181eff5f77SRijawanemohammadhusen Nadaf 
5197c478bd9Sstevel@tonic-gate 	/* Enable transmitter and interrupt */
5207c478bd9Sstevel@tonic-gate 	extctrl = bge_mii_get16(bgep, 0x10);
5217c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x10, extctrl & ~0x3000);
5227c478bd9Sstevel@tonic-gate 
5235a506a18Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5906_SERIES_CHIPSETS(bgep))
5242fec4481SCarson Tan 		(void) bge_adj_volt_5906(bgep);
5255a506a18Syong tan - Sun Microsystems - Beijing China 
52600d0963fSdilpreet 	if (!reset_success)
52700d0963fSdilpreet 		bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
52800d0963fSdilpreet 	else if (phy_locked)
52900d0963fSdilpreet 		bge_fm_ereport(bgep, DDI_FM_DEVICE_INVAL_STATE);
5307c478bd9Sstevel@tonic-gate 	return (reset_success && !phy_locked);
5317c478bd9Sstevel@tonic-gate }
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate static void
5347c478bd9Sstevel@tonic-gate bge_phy_tweak_gmii(bge_t *bgep)
5357c478bd9Sstevel@tonic-gate {
5367c478bd9Sstevel@tonic-gate 	/* Tweak GMII timing */
5377c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x1c, 0x8d68);
5387c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x1c, 0x8d68);
5397c478bd9Sstevel@tonic-gate }
5407c478bd9Sstevel@tonic-gate 
541256e438eSzh199473 /* Bit Error Rate reduction fix */
542256e438eSzh199473 static void
543256e438eSzh199473 bge_phy_bit_err_fix(bge_t *bgep)
544256e438eSzh199473 {
545256e438eSzh199473 	bge_mii_put16(bgep, 0x18, 0x0c00);
546256e438eSzh199473 	bge_mii_put16(bgep, 0x17, 0x000a);
547256e438eSzh199473 	bge_mii_put16(bgep, 0x15, 0x310b);
548256e438eSzh199473 	bge_mii_put16(bgep, 0x17, 0x201f);
549256e438eSzh199473 	bge_mii_put16(bgep, 0x15, 0x9506);
550256e438eSzh199473 	bge_mii_put16(bgep, 0x17, 0x401f);
551256e438eSzh199473 	bge_mii_put16(bgep, 0x15, 0x14e2);
552256e438eSzh199473 	bge_mii_put16(bgep, 0x18, 0x0400);
553256e438eSzh199473 }
554256e438eSzh199473 
5557c478bd9Sstevel@tonic-gate /*
556*087a28d1SDavid Gwynne  * End of Broadcom-derived workaround code
5577c478bd9Sstevel@tonic-gate  */
5587c478bd9Sstevel@tonic-gate 
55900d0963fSdilpreet static int
5607c478bd9Sstevel@tonic-gate bge_restart_copper(bge_t *bgep, boolean_t powerdown)
5617c478bd9Sstevel@tonic-gate {
5627c478bd9Sstevel@tonic-gate 	uint16_t phy_status;
5637c478bd9Sstevel@tonic-gate 	boolean_t reset_ok;
56435bff3c2Syong tan - Sun Microsystems - Beijing China 	uint16_t extctrl, auxctrl;
565*087a28d1SDavid Gwynne 	int i;
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_restart_copper($%p, %d)", (void *)bgep, powerdown));
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
5707c478bd9Sstevel@tonic-gate 
571*087a28d1SDavid Gwynne 	switch (MHCR_CHIP_ASIC_REV(bgep)) {
5727c478bd9Sstevel@tonic-gate 	default:
5737c478bd9Sstevel@tonic-gate 		/*
5747c478bd9Sstevel@tonic-gate 		 * Shouldn't happen; it means we don't recognise this chip.
5757c478bd9Sstevel@tonic-gate 		 * It's probably a new one, so we'll try our best anyway ...
5767c478bd9Sstevel@tonic-gate 		 */
5777c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5703:
5787c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5704:
5797c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5705:
580f724721bSzh199473 	case MHCR_CHIP_ASIC_REV_5752:
5817c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5714:
58210843bc4Sly149593 	case MHCR_CHIP_ASIC_REV_5715:
5837c478bd9Sstevel@tonic-gate 		reset_ok = bge_phy_reset_and_check(bgep);
5847c478bd9Sstevel@tonic-gate 		break;
5857c478bd9Sstevel@tonic-gate 
5865a506a18Syong tan - Sun Microsystems - Beijing China 	case MHCR_CHIP_ASIC_REV_5906:
5877c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5700:
5887c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5701:
589*087a28d1SDavid Gwynne 	case MHCR_CHIP_ASIC_REV_5723: /* 5717 and 5725 series as well */
5904d6eaea5Syong tan - Sun Microsystems - Beijing China 	case MHCR_CHIP_ASIC_REV_5721_5751:
5917c478bd9Sstevel@tonic-gate 		/*
5927c478bd9Sstevel@tonic-gate 		 * Just a plain reset; the "check" code breaks these chips
5937c478bd9Sstevel@tonic-gate 		 */
5947c478bd9Sstevel@tonic-gate 		reset_ok = bge_phy_reset(bgep);
59500d0963fSdilpreet 		if (!reset_ok)
59600d0963fSdilpreet 			bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
5977c478bd9Sstevel@tonic-gate 		break;
5987c478bd9Sstevel@tonic-gate 	}
59900d0963fSdilpreet 	if (!reset_ok) {
60000d0963fSdilpreet 		BGE_REPORT((bgep, "PHY failed to reset correctly"));
60100d0963fSdilpreet 		return (DDI_FAILURE);
60200d0963fSdilpreet 	}
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	/*
6057c478bd9Sstevel@tonic-gate 	 * Step 5: disable WOL (not required after RESET)
6067c478bd9Sstevel@tonic-gate 	 *
6077c478bd9Sstevel@tonic-gate 	 * Step 6: refer to errata
6087c478bd9Sstevel@tonic-gate 	 */
6097c478bd9Sstevel@tonic-gate 	switch (bgep->chipid.asic_rev) {
6107c478bd9Sstevel@tonic-gate 	default:
6117c478bd9Sstevel@tonic-gate 		break;
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_REV_5704_A0:
6147c478bd9Sstevel@tonic-gate 		bge_phy_tweak_gmii(bgep);
6157c478bd9Sstevel@tonic-gate 		break;
6167c478bd9Sstevel@tonic-gate 	}
6177c478bd9Sstevel@tonic-gate 
618*087a28d1SDavid Gwynne 	switch (MHCR_CHIP_ASIC_REV(bgep)) {
619256e438eSzh199473 	case MHCR_CHIP_ASIC_REV_5705:
620256e438eSzh199473 	case MHCR_CHIP_ASIC_REV_5721_5751:
621256e438eSzh199473 		bge_phy_bit_err_fix(bgep);
622256e438eSzh199473 		break;
623256e438eSzh199473 	}
624256e438eSzh199473 
6256e6ed1baSyong tan - Sun Microsystems - Beijing China 	if (!(bgep->chipid.flags & CHIP_FLAG_NO_JUMBO) &&
6266e6ed1baSyong tan - Sun Microsystems - Beijing China 	    (bgep->chipid.default_mtu > BGE_DEFAULT_MTU)) {
62735bff3c2Syong tan - Sun Microsystems - Beijing China 		/* Set the GMII Fifo Elasticity to high latency */
62835bff3c2Syong tan - Sun Microsystems - Beijing China 		extctrl = bge_mii_get16(bgep, 0x10);
62935bff3c2Syong tan - Sun Microsystems - Beijing China 		bge_mii_put16(bgep, 0x10, extctrl | 0x1);
63035bff3c2Syong tan - Sun Microsystems - Beijing China 
63135bff3c2Syong tan - Sun Microsystems - Beijing China 		/* Allow reception of extended length packets */
63235bff3c2Syong tan - Sun Microsystems - Beijing China 		bge_mii_put16(bgep, MII_AUX_CONTROL, 0x0007);
63335bff3c2Syong tan - Sun Microsystems - Beijing China 		auxctrl = bge_mii_get16(bgep, MII_AUX_CONTROL);
63435bff3c2Syong tan - Sun Microsystems - Beijing China 		auxctrl |= 0x4000;
63535bff3c2Syong tan - Sun Microsystems - Beijing China 		bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl);
63635bff3c2Syong tan - Sun Microsystems - Beijing China 	}
63735bff3c2Syong tan - Sun Microsystems - Beijing China 
6387c478bd9Sstevel@tonic-gate 	/*
6397c478bd9Sstevel@tonic-gate 	 * Step 7: read the MII_INTR_STATUS register twice,
6407c478bd9Sstevel@tonic-gate 	 * in order to clear any sticky bits (but they should
6417c478bd9Sstevel@tonic-gate 	 * have been cleared by the RESET, I think), and we're
6427c478bd9Sstevel@tonic-gate 	 * not using PHY interrupts anyway.
6437c478bd9Sstevel@tonic-gate 	 *
6447c478bd9Sstevel@tonic-gate 	 * Step 8: enable the PHY to interrupt on link status
6457c478bd9Sstevel@tonic-gate 	 * change (not required)
6467c478bd9Sstevel@tonic-gate 	 *
6477c478bd9Sstevel@tonic-gate 	 * Step 9: configure PHY LED Mode - not applicable?
6487c478bd9Sstevel@tonic-gate 	 *
6497c478bd9Sstevel@tonic-gate 	 * Step 10: read the MII_STATUS register twice, in
6507c478bd9Sstevel@tonic-gate 	 * order to clear any sticky bits (but they should
6517c478bd9Sstevel@tonic-gate 	 * have been cleared by the RESET, I think).
6527c478bd9Sstevel@tonic-gate 	 */
653*087a28d1SDavid Gwynne 	for (i = 0; i < 100; i++) {
654*087a28d1SDavid Gwynne 		drv_usecwait(40);
6557c478bd9Sstevel@tonic-gate 		phy_status = bge_mii_get16(bgep, MII_STATUS);
656*087a28d1SDavid Gwynne 	}
6577c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_restart_copper: status 0x%x", phy_status));
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	/*
6607c478bd9Sstevel@tonic-gate 	 * Finally, shut down the PHY, if required
6617c478bd9Sstevel@tonic-gate 	 */
6627c478bd9Sstevel@tonic-gate 	if (powerdown)
6637c478bd9Sstevel@tonic-gate 		bge_phy_powerdown(bgep);
66400d0963fSdilpreet 	return (DDI_SUCCESS);
6657c478bd9Sstevel@tonic-gate }
6667c478bd9Sstevel@tonic-gate 
667*087a28d1SDavid Gwynne boolean_t
668*087a28d1SDavid Gwynne bge_eee_cap(bge_t * bgep)
669*087a28d1SDavid Gwynne {
670*087a28d1SDavid Gwynne 	if (!(DEVICE_5717_SERIES_CHIPSETS(bgep) ||
671*087a28d1SDavid Gwynne 	    DEVICE_5725_SERIES_CHIPSETS(bgep))) {
672*087a28d1SDavid Gwynne 		/* EEE is not supported on this chip */
673*087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_eee: eee not supported (device 0x%x)",
674*087a28d1SDavid Gwynne 		    bgep->chipid.device));
675*087a28d1SDavid Gwynne 		return (B_FALSE);
676*087a28d1SDavid Gwynne 	}
677*087a28d1SDavid Gwynne 
678*087a28d1SDavid Gwynne 	switch (CHIP_ASIC_REV_PROD_ID(bgep)) {
679*087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5717_B0: /* = CHIP_ASIC_REV_5718_B0 */
680*087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5717_C0:
681*087a28d1SDavid Gwynne 	/* case CHIP_ASIC_REV_5718_B0: */
682*087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5719_A0:
683*087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5719_A1:
684*087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5720_A0:
685*087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5725_A0:
686*087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5727_B0:
687*087a28d1SDavid Gwynne 		return (B_TRUE);
688*087a28d1SDavid Gwynne 
689*087a28d1SDavid Gwynne 	default:
690*087a28d1SDavid Gwynne 		/* EEE is not supported on this asic rev */
691*087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_eee: eee not supported (asic rev 0x%08x)",
692*087a28d1SDavid Gwynne 		    bgep->chipid.asic_rev));
693*087a28d1SDavid Gwynne 		return (B_FALSE);
694*087a28d1SDavid Gwynne 	}
695*087a28d1SDavid Gwynne }
696*087a28d1SDavid Gwynne 
697*087a28d1SDavid Gwynne void
698*087a28d1SDavid Gwynne bge_eee_init(bge_t * bgep)
699*087a28d1SDavid Gwynne {
700*087a28d1SDavid Gwynne 	uint32_t val;
701*087a28d1SDavid Gwynne 
702*087a28d1SDavid Gwynne 	BGE_TRACE(("bge_eee_init($%p)", (void *)bgep));
703*087a28d1SDavid Gwynne 
704*087a28d1SDavid Gwynne 	ASSERT(mutex_owned(bgep->genlock));
705*087a28d1SDavid Gwynne 
706*087a28d1SDavid Gwynne 	if (!bge_eee_cap(bgep)) {
707*087a28d1SDavid Gwynne 		return;
708*087a28d1SDavid Gwynne 	}
709*087a28d1SDavid Gwynne 
710*087a28d1SDavid Gwynne 	/* Enable MAC control of LPI */
711*087a28d1SDavid Gwynne 
712*087a28d1SDavid Gwynne 	val = (EEE_LINK_IDLE_PCIE_NL0 | EEE_LINK_IDLE_UART_IDL);
713*087a28d1SDavid Gwynne 	if (DEVICE_5725_SERIES_CHIPSETS(bgep))
714*087a28d1SDavid Gwynne 		val |= EEE_LINK_IDLE_APE_TX_MT;
715*087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_LINK_IDLE_CONTROL_REG, val);
716*087a28d1SDavid Gwynne 
717*087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_CONTROL_REG, EEE_CONTROL_EXIT_20_1_US);
718*087a28d1SDavid Gwynne 
719*087a28d1SDavid Gwynne 	val = EEE_MODE_ERLY_L1_XIT_DET | EEE_MODE_LPI_IN_TX |
720*087a28d1SDavid Gwynne 	    EEE_MODE_LPI_IN_RX | EEE_MODE_EEE_ENABLE;
721*087a28d1SDavid Gwynne 
722*087a28d1SDavid Gwynne 	if (bgep->chipid.device != DEVICE_ID_5717)
723*087a28d1SDavid Gwynne 		val |= EEE_MODE_SND_IDX_DET_EN;
724*087a28d1SDavid Gwynne 
725*087a28d1SDavid Gwynne 	//val |= EEE_MODE_APE_TX_DET_EN;
726*087a28d1SDavid Gwynne 
727*087a28d1SDavid Gwynne 	if (!bgep->chipid.eee) {
728*087a28d1SDavid Gwynne 		val = 0;
729*087a28d1SDavid Gwynne 	}
730*087a28d1SDavid Gwynne 
731*087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_MODE_REG, val);
732*087a28d1SDavid Gwynne 
733*087a28d1SDavid Gwynne 	/* Set EEE timer debounce values */
734*087a28d1SDavid Gwynne 
735*087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_DEBOUNCE_T1_CONTROL_REG,
736*087a28d1SDavid Gwynne 	    EEE_DEBOUNCE_T1_PCIEXIT_2047US | EEE_DEBOUNCE_T1_LNKIDLE_2047US);
737*087a28d1SDavid Gwynne 
738*087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_DEBOUNCE_T2_CONTROL_REG,
739*087a28d1SDavid Gwynne 	    EEE_DEBOUNCE_T2_APE_TX_2047US | EEE_DEBOUNCE_T2_TXIDXEQ_2047US);
740*087a28d1SDavid Gwynne }
741*087a28d1SDavid Gwynne 
742*087a28d1SDavid Gwynne void
743*087a28d1SDavid Gwynne bge_eee_autoneg(bge_t * bgep, boolean_t adv_100fdx, boolean_t adv_1000fdx)
744*087a28d1SDavid Gwynne {
745*087a28d1SDavid Gwynne 	uint32_t val;
746*087a28d1SDavid Gwynne 	uint16_t mii_val;
747*087a28d1SDavid Gwynne 
748*087a28d1SDavid Gwynne 	BGE_TRACE(("bge_eee_autoneg($%p)", (void *)bgep));
749*087a28d1SDavid Gwynne 
750*087a28d1SDavid Gwynne 	ASSERT(mutex_owned(bgep->genlock));
751*087a28d1SDavid Gwynne 
752*087a28d1SDavid Gwynne 	if (!bge_eee_cap(bgep)) {
753*087a28d1SDavid Gwynne 		return;
754*087a28d1SDavid Gwynne 	}
755*087a28d1SDavid Gwynne 
756*087a28d1SDavid Gwynne 	/* Disable LPI Requests */
757*087a28d1SDavid Gwynne 	val = bge_reg_get32(bgep, EEE_MODE_REG);
758*087a28d1SDavid Gwynne 	val &= ~EEE_MODE_LPI_ENABLE;
759*087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_MODE_REG, val);
760*087a28d1SDavid Gwynne 
761*087a28d1SDavid Gwynne 	bge_phy_toggle_auxctl_smdsp(bgep, B_TRUE);
762*087a28d1SDavid Gwynne 
763*087a28d1SDavid Gwynne 	mii_val = 0;
764*087a28d1SDavid Gwynne 
765*087a28d1SDavid Gwynne 	if (bgep->chipid.eee) {
766*087a28d1SDavid Gwynne 		if (adv_100fdx) {
767*087a28d1SDavid Gwynne 			mii_val |= EEE_CL45_D7_RESULT_STAT_LP_100TX;
768*087a28d1SDavid Gwynne 		}
769*087a28d1SDavid Gwynne 		if (adv_1000fdx) {
770*087a28d1SDavid Gwynne 			mii_val |= EEE_CL45_D7_RESULT_STAT_LP_1000T;
771*087a28d1SDavid Gwynne 		}
772*087a28d1SDavid Gwynne 	}
773*087a28d1SDavid Gwynne 
774*087a28d1SDavid Gwynne 	/* Enable EEE advertisement for the specified mode(s)... */
775*087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_MMD_CTRL, MDIO_MMD_AN);
776*087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_MMD_ADDRESS_DATA, MDIO_AN_EEE_ADV);
777*087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_MMD_CTRL,
778*087a28d1SDavid Gwynne 	    MII_MMD_CTRL_DATA_NOINC | MDIO_MMD_AN);
779*087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_MMD_ADDRESS_DATA, mii_val);
780*087a28d1SDavid Gwynne 
781*087a28d1SDavid Gwynne 	/* Setup PHY DSP for EEE */
782*087a28d1SDavid Gwynne 	switch (bgep->chipid.device) {
783*087a28d1SDavid Gwynne 	case DEVICE_ID_5717:
784*087a28d1SDavid Gwynne 	case DEVICE_ID_5718:
785*087a28d1SDavid Gwynne 	case DEVICE_ID_5719:
786*087a28d1SDavid Gwynne 		/* If we advertised any EEE advertisements above... */
787*087a28d1SDavid Gwynne 		if (mii_val) {
788*087a28d1SDavid Gwynne 			mii_val = (MII_DSP_TAP26_ALNOKO |
789*087a28d1SDavid Gwynne 			    MII_DSP_TAP26_RMRXSTO |
790*087a28d1SDavid Gwynne 			    MII_DSP_TAP26_OPCSINPT);
791*087a28d1SDavid Gwynne 		}
792*087a28d1SDavid Gwynne 		bge_phydsp_write(bgep, MII_DSP_TAP26, mii_val);
793*087a28d1SDavid Gwynne 		/* fall through */
794*087a28d1SDavid Gwynne 	case DEVICE_ID_5720:
795*087a28d1SDavid Gwynne 	case DEVICE_ID_5725:
796*087a28d1SDavid Gwynne 	case DEVICE_ID_5727:
797*087a28d1SDavid Gwynne 		mii_val = bge_phydsp_read(bgep, MII_DSP_CH34TP2);
798*087a28d1SDavid Gwynne 		bge_phydsp_write(bgep, MII_DSP_CH34TP2,
799*087a28d1SDavid Gwynne 		    (mii_val | MII_DSP_CH34TP2_HIBW01));
800*087a28d1SDavid Gwynne 	}
801*087a28d1SDavid Gwynne 
802*087a28d1SDavid Gwynne 	bge_phy_toggle_auxctl_smdsp(bgep, B_FALSE);
803*087a28d1SDavid Gwynne }
804*087a28d1SDavid Gwynne 
805*087a28d1SDavid Gwynne void
806*087a28d1SDavid Gwynne bge_eee_adjust(bge_t * bgep)
807*087a28d1SDavid Gwynne {
808*087a28d1SDavid Gwynne 	uint32_t val;
809*087a28d1SDavid Gwynne 	uint16_t mii_val;
810*087a28d1SDavid Gwynne 
811*087a28d1SDavid Gwynne 	BGE_TRACE(("bge_eee_adjust($%p, %d)", (void *)bgep));
812*087a28d1SDavid Gwynne 
813*087a28d1SDavid Gwynne 	ASSERT(mutex_owned(bgep->genlock));
814*087a28d1SDavid Gwynne 
815*087a28d1SDavid Gwynne 	if (!bge_eee_cap(bgep)) {
816*087a28d1SDavid Gwynne 		return;
817*087a28d1SDavid Gwynne 	}
818*087a28d1SDavid Gwynne 
819*087a28d1SDavid Gwynne 	bgep->eee_lpi_wait = 0;
820*087a28d1SDavid Gwynne 
821*087a28d1SDavid Gwynne 	/* Check for PHY link status */
822*087a28d1SDavid Gwynne 	if (bgep->param_link_up) {
823*087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_eee_adjust: link status up"));
824*087a28d1SDavid Gwynne 
825*087a28d1SDavid Gwynne 		/*
826*087a28d1SDavid Gwynne 		 * XXX if duplex full and speed is 1000 or 100 then do the
827*087a28d1SDavid Gwynne 		 * following...
828*087a28d1SDavid Gwynne 		 */
829*087a28d1SDavid Gwynne 
830*087a28d1SDavid Gwynne 		if (bgep->param_link_speed == 1000) {
831*087a28d1SDavid Gwynne 			BGE_DEBUG(("bge_eee_adjust: eee timing for 1000Mb"));
832*087a28d1SDavid Gwynne 			bge_reg_put32(bgep, EEE_CONTROL_REG,
833*087a28d1SDavid Gwynne 			    EEE_CONTROL_EXIT_16_5_US);
834*087a28d1SDavid Gwynne 		} else if (bgep->param_link_speed == 100) {
835*087a28d1SDavid Gwynne 			BGE_DEBUG(("bge_eee_adjust: eee timing for 100Mb"));
836*087a28d1SDavid Gwynne 			bge_reg_put32(bgep, EEE_CONTROL_REG,
837*087a28d1SDavid Gwynne 			    EEE_CONTROL_EXIT_36_US);
838*087a28d1SDavid Gwynne 		}
839*087a28d1SDavid Gwynne 
840*087a28d1SDavid Gwynne 		/* Read PHY's EEE negotiation status */
841*087a28d1SDavid Gwynne 		bge_mii_put16(bgep, MII_MMD_CTRL, MDIO_MMD_AN);
842*087a28d1SDavid Gwynne 		bge_mii_put16(bgep, MII_MMD_ADDRESS_DATA,
843*087a28d1SDavid Gwynne 		    EEE_CL45_D7_RESULT_STAT);
844*087a28d1SDavid Gwynne 		bge_mii_put16(bgep, MII_MMD_CTRL,
845*087a28d1SDavid Gwynne 		    MII_MMD_CTRL_DATA_NOINC | MDIO_MMD_AN);
846*087a28d1SDavid Gwynne 		mii_val = bge_mii_get16(bgep, MII_MMD_ADDRESS_DATA);
847*087a28d1SDavid Gwynne 
848*087a28d1SDavid Gwynne 		/* Enable EEE LPI request if EEE negotiated */
849*087a28d1SDavid Gwynne 		if ((mii_val == EEE_CL45_D7_RESULT_STAT_LP_1000T) ||
850*087a28d1SDavid Gwynne 		    (mii_val == EEE_CL45_D7_RESULT_STAT_LP_100TX)) {
851*087a28d1SDavid Gwynne 			BGE_DEBUG(("bge_eee_adjust: eee negotiaton success, lpi scheduled"));
852*087a28d1SDavid Gwynne 			bgep->eee_lpi_wait = 2;
853*087a28d1SDavid Gwynne 		} else {
854*087a28d1SDavid Gwynne 			BGE_DEBUG(("bge_eee_adjust: eee negotiation failed"));
855*087a28d1SDavid Gwynne 		}
856*087a28d1SDavid Gwynne 	} else {
857*087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_eee_adjust: link status down"));
858*087a28d1SDavid Gwynne 	}
859*087a28d1SDavid Gwynne 
860*087a28d1SDavid Gwynne 	if (!bgep->eee_lpi_wait) {
861*087a28d1SDavid Gwynne 		if (bgep->param_link_up) {
862*087a28d1SDavid Gwynne 			bge_phy_toggle_auxctl_smdsp(bgep, B_TRUE);
863*087a28d1SDavid Gwynne 			bge_phydsp_write(bgep, MII_DSP_TAP26, 0);
864*087a28d1SDavid Gwynne 			bge_phy_toggle_auxctl_smdsp(bgep, B_FALSE);
865*087a28d1SDavid Gwynne 		}
866*087a28d1SDavid Gwynne 
867*087a28d1SDavid Gwynne 		/* Disable LPI requests */
868*087a28d1SDavid Gwynne 		val = bge_reg_get32(bgep, EEE_MODE_REG);
869*087a28d1SDavid Gwynne 		val &= ~EEE_MODE_LPI_ENABLE;
870*087a28d1SDavid Gwynne 		bge_reg_put32(bgep, EEE_MODE_REG, val);
871*087a28d1SDavid Gwynne 	}
872*087a28d1SDavid Gwynne }
873*087a28d1SDavid Gwynne 
874*087a28d1SDavid Gwynne void
875*087a28d1SDavid Gwynne bge_eee_enable(bge_t * bgep)
876*087a28d1SDavid Gwynne {
877*087a28d1SDavid Gwynne 	uint32_t val;
878*087a28d1SDavid Gwynne 
879*087a28d1SDavid Gwynne 	/* XXX check for EEE for 5717 family... */
880*087a28d1SDavid Gwynne 
881*087a28d1SDavid Gwynne 	if (bgep->param_link_speed == 1000) {
882*087a28d1SDavid Gwynne 		bge_phy_toggle_auxctl_smdsp(bgep, B_TRUE);
883*087a28d1SDavid Gwynne 		bge_phydsp_write(bgep, MII_DSP_TAP26,
884*087a28d1SDavid Gwynne 		    MII_DSP_TAP26_ALNOKO | MII_DSP_TAP26_RMRXSTO);
885*087a28d1SDavid Gwynne 		bge_phy_toggle_auxctl_smdsp(bgep, B_FALSE);
886*087a28d1SDavid Gwynne 	}
887*087a28d1SDavid Gwynne 
888*087a28d1SDavid Gwynne 	val = bge_reg_get32(bgep, EEE_MODE_REG);
889*087a28d1SDavid Gwynne 	val |= EEE_MODE_LPI_ENABLE;
890*087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_MODE_REG, val);
891*087a28d1SDavid Gwynne }
892*087a28d1SDavid Gwynne 
8937c478bd9Sstevel@tonic-gate /*
8947c478bd9Sstevel@tonic-gate  * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities
8957c478bd9Sstevel@tonic-gate  * and advertisements with the required settings as specified by the various
8967c478bd9Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
8977c478bd9Sstevel@tonic-gate  *
8987c478bd9Sstevel@tonic-gate  * We always reset the PHY and reprogram *all* the relevant registers,
8997c478bd9Sstevel@tonic-gate  * not just those changed.  This should cause the link to go down, and then
9007c478bd9Sstevel@tonic-gate  * back up again once the link is stable and autonegotiation (if enabled)
9017c478bd9Sstevel@tonic-gate  * is complete.  We should get a link state change interrupt somewhere along
9027c478bd9Sstevel@tonic-gate  * the way ...
9037c478bd9Sstevel@tonic-gate  *
9047c478bd9Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
9057c478bd9Sstevel@tonic-gate  */
90600d0963fSdilpreet static int
9077c478bd9Sstevel@tonic-gate bge_update_copper(bge_t *bgep)
9087c478bd9Sstevel@tonic-gate {
9097c478bd9Sstevel@tonic-gate 	boolean_t adv_autoneg;
9107c478bd9Sstevel@tonic-gate 	boolean_t adv_pause;
9117c478bd9Sstevel@tonic-gate 	boolean_t adv_asym_pause;
9127c478bd9Sstevel@tonic-gate 	boolean_t adv_1000fdx;
9137c478bd9Sstevel@tonic-gate 	boolean_t adv_1000hdx;
9147c478bd9Sstevel@tonic-gate 	boolean_t adv_100fdx;
9157c478bd9Sstevel@tonic-gate 	boolean_t adv_100hdx;
9167c478bd9Sstevel@tonic-gate 	boolean_t adv_10fdx;
9177c478bd9Sstevel@tonic-gate 	boolean_t adv_10hdx;
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	uint16_t control;
9207c478bd9Sstevel@tonic-gate 	uint16_t gigctrl;
9217c478bd9Sstevel@tonic-gate 	uint16_t auxctrl;
9227c478bd9Sstevel@tonic-gate 	uint16_t anar;
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_update_copper($%p)", (void *)bgep));
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: autoneg %d "
9297c478bd9Sstevel@tonic-gate 	    "pause %d asym_pause %d "
9307c478bd9Sstevel@tonic-gate 	    "1000fdx %d 1000hdx %d "
9317c478bd9Sstevel@tonic-gate 	    "100fdx %d 100hdx %d "
9327c478bd9Sstevel@tonic-gate 	    "10fdx %d 10hdx %d ",
9337c478bd9Sstevel@tonic-gate 	    bgep->param_adv_autoneg,
9347c478bd9Sstevel@tonic-gate 	    bgep->param_adv_pause, bgep->param_adv_asym_pause,
9357c478bd9Sstevel@tonic-gate 	    bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
9367c478bd9Sstevel@tonic-gate 	    bgep->param_adv_100fdx, bgep->param_adv_100hdx,
9377c478bd9Sstevel@tonic-gate 	    bgep->param_adv_10fdx, bgep->param_adv_10hdx));
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 	control = gigctrl = auxctrl = anar = 0;
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	/*
9427c478bd9Sstevel@tonic-gate 	 * PHY settings are normally based on the param_* variables,
9437c478bd9Sstevel@tonic-gate 	 * but if any loopback mode is in effect, that takes precedence.
9447c478bd9Sstevel@tonic-gate 	 *
9457c478bd9Sstevel@tonic-gate 	 * BGE supports MAC-internal loopback, PHY-internal loopback,
9467c478bd9Sstevel@tonic-gate 	 * and External loopback at a variety of speeds (with a special
9477c478bd9Sstevel@tonic-gate 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
9487c478bd9Sstevel@tonic-gate 	 * is turned ON, and the speed/mastership is forced.
9497c478bd9Sstevel@tonic-gate 	 */
9507c478bd9Sstevel@tonic-gate 	switch (bgep->param_loop_mode) {
9517c478bd9Sstevel@tonic-gate 	case BGE_LOOP_NONE:
9527c478bd9Sstevel@tonic-gate 	default:
9537c478bd9Sstevel@tonic-gate 		adv_autoneg = bgep->param_adv_autoneg;
9547c478bd9Sstevel@tonic-gate 		adv_pause = bgep->param_adv_pause;
9557c478bd9Sstevel@tonic-gate 		adv_asym_pause = bgep->param_adv_asym_pause;
9567c478bd9Sstevel@tonic-gate 		adv_1000fdx = bgep->param_adv_1000fdx;
9577c478bd9Sstevel@tonic-gate 		adv_1000hdx = bgep->param_adv_1000hdx;
9587c478bd9Sstevel@tonic-gate 		adv_100fdx = bgep->param_adv_100fdx;
9597c478bd9Sstevel@tonic-gate 		adv_100hdx = bgep->param_adv_100hdx;
9607c478bd9Sstevel@tonic-gate 		adv_10fdx = bgep->param_adv_10fdx;
9617c478bd9Sstevel@tonic-gate 		adv_10hdx = bgep->param_adv_10hdx;
9627c478bd9Sstevel@tonic-gate 		break;
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_1000:
9657c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_100:
9667c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_10:
9677c478bd9Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_PHY:
9687c478bd9Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_MAC:
9697c478bd9Sstevel@tonic-gate 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
9707c478bd9Sstevel@tonic-gate 		adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE;
9717c478bd9Sstevel@tonic-gate 		adv_1000hdx = adv_100hdx = adv_10hdx = B_FALSE;
9727c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_FULL;
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 		switch (bgep->param_loop_mode) {
9757c478bd9Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_1000:
9767c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
9777c478bd9Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
9787c478bd9Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
979bdb9230aSGarrett D'Amore 			gigctrl |= MII_MSCONTROL_MANUAL;
980bdb9230aSGarrett D'Amore 			gigctrl |= MII_MSCONTROL_MASTER;
9817c478bd9Sstevel@tonic-gate 			break;
9827c478bd9Sstevel@tonic-gate 
9837c478bd9Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_100:
9847c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 100;
9857c478bd9Sstevel@tonic-gate 			adv_100fdx = B_TRUE;
9867c478bd9Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
9877c478bd9Sstevel@tonic-gate 			break;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_10:
9907c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 10;
9917c478bd9Sstevel@tonic-gate 			adv_10fdx = B_TRUE;
9927c478bd9Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
9937c478bd9Sstevel@tonic-gate 			break;
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 		case BGE_LOOP_INTERNAL_PHY:
9967c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
9977c478bd9Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
9987c478bd9Sstevel@tonic-gate 			control = MII_CONTROL_LOOPBACK;
9997c478bd9Sstevel@tonic-gate 			break;
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 		case BGE_LOOP_INTERNAL_MAC:
10027c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
10037c478bd9Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
10047c478bd9Sstevel@tonic-gate 			break;
10057c478bd9Sstevel@tonic-gate 		}
10067c478bd9Sstevel@tonic-gate 	}
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: autoneg %d "
10097c478bd9Sstevel@tonic-gate 	    "pause %d asym_pause %d "
10107c478bd9Sstevel@tonic-gate 	    "1000fdx %d 1000hdx %d "
10117c478bd9Sstevel@tonic-gate 	    "100fdx %d 100hdx %d "
10127c478bd9Sstevel@tonic-gate 	    "10fdx %d 10hdx %d ",
10137c478bd9Sstevel@tonic-gate 	    adv_autoneg,
10147c478bd9Sstevel@tonic-gate 	    adv_pause, adv_asym_pause,
10157c478bd9Sstevel@tonic-gate 	    adv_1000fdx, adv_1000hdx,
10167c478bd9Sstevel@tonic-gate 	    adv_100fdx, adv_100hdx,
10177c478bd9Sstevel@tonic-gate 	    adv_10fdx, adv_10hdx));
10187c478bd9Sstevel@tonic-gate 
10197c478bd9Sstevel@tonic-gate 	/*
10207c478bd9Sstevel@tonic-gate 	 * We should have at least one technology capability set;
10217c478bd9Sstevel@tonic-gate 	 * if not, we select a default of 1000Mb/s full-duplex
10227c478bd9Sstevel@tonic-gate 	 */
10237c478bd9Sstevel@tonic-gate 	if (!adv_1000fdx && !adv_100fdx && !adv_10fdx &&
10247c478bd9Sstevel@tonic-gate 	    !adv_1000hdx && !adv_100hdx && !adv_10hdx)
10257c478bd9Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	/*
10287c478bd9Sstevel@tonic-gate 	 * Now transform the adv_* variables into the proper settings
10297c478bd9Sstevel@tonic-gate 	 * of the PHY registers ...
10307c478bd9Sstevel@tonic-gate 	 *
10317c478bd9Sstevel@tonic-gate 	 * If autonegotiation is (now) enabled, we want to trigger
10327c478bd9Sstevel@tonic-gate 	 * a new autonegotiation cycle once the PHY has been
10337c478bd9Sstevel@tonic-gate 	 * programmed with the capabilities to be advertised.
10347c478bd9Sstevel@tonic-gate 	 */
10357c478bd9Sstevel@tonic-gate 	if (adv_autoneg)
10367c478bd9Sstevel@tonic-gate 		control |= MII_CONTROL_ANE|MII_CONTROL_RSAN;
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	if (adv_1000fdx)
1039bdb9230aSGarrett D'Amore 		control |= MII_CONTROL_1GB|MII_CONTROL_FDUPLEX;
10407c478bd9Sstevel@tonic-gate 	else if (adv_1000hdx)
1041bdb9230aSGarrett D'Amore 		control |= MII_CONTROL_1GB;
10427c478bd9Sstevel@tonic-gate 	else if (adv_100fdx)
10437c478bd9Sstevel@tonic-gate 		control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX;
10447c478bd9Sstevel@tonic-gate 	else if (adv_100hdx)
10457c478bd9Sstevel@tonic-gate 		control |= MII_CONTROL_100MB;
10467c478bd9Sstevel@tonic-gate 	else if (adv_10fdx)
10477c478bd9Sstevel@tonic-gate 		control |= MII_CONTROL_FDUPLEX;
10487c478bd9Sstevel@tonic-gate 	else if (adv_10hdx)
10497c478bd9Sstevel@tonic-gate 		control |= 0;
10507c478bd9Sstevel@tonic-gate 	else
10517c478bd9Sstevel@tonic-gate 		{ _NOTE(EMPTY); }	/* Can't get here anyway ...	*/
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	if (adv_1000fdx)
1054bdb9230aSGarrett D'Amore 		gigctrl |= MII_MSCONTROL_1000T_FD;
10557c478bd9Sstevel@tonic-gate 	if (adv_1000hdx)
1056bdb9230aSGarrett D'Amore 		gigctrl |= MII_MSCONTROL_1000T;
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	if (adv_100fdx)
10597c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_100BASE_TX_FD;
10607c478bd9Sstevel@tonic-gate 	if (adv_100hdx)
10617c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_100BASE_TX;
10627c478bd9Sstevel@tonic-gate 	if (adv_10fdx)
10637c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_10BASE_T_FD;
10647c478bd9Sstevel@tonic-gate 	if (adv_10hdx)
10657c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_10BASE_T;
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate 	if (adv_pause)
10687c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_PAUSE;
10697c478bd9Sstevel@tonic-gate 	if (adv_asym_pause)
1070bdb9230aSGarrett D'Amore 		anar |= MII_ABILITY_ASMPAUSE;
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	/*
10737c478bd9Sstevel@tonic-gate 	 * Munge in any other fixed bits we require ...
10747c478bd9Sstevel@tonic-gate 	 */
10757c478bd9Sstevel@tonic-gate 	anar |= MII_AN_SELECTOR_8023;
10767c478bd9Sstevel@tonic-gate 	auxctrl |= MII_AUX_CTRL_NORM_TX_MODE;
10777c478bd9Sstevel@tonic-gate 	auxctrl |= MII_AUX_CTRL_NORMAL;
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	/*
10807c478bd9Sstevel@tonic-gate 	 * Restart the PHY and write the new values.  Note the
10817c478bd9Sstevel@tonic-gate 	 * time, so that we can say whether subsequent link state
10827c478bd9Sstevel@tonic-gate 	 * changes can be attributed to our reprogramming the PHY
10837c478bd9Sstevel@tonic-gate 	 */
108400d0963fSdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) == DDI_FAILURE)
108500d0963fSdilpreet 		return (DDI_FAILURE);
10867c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_AN_ADVERT, anar);
108735bff3c2Syong tan - Sun Microsystems - Beijing China 	if (auxctrl & MII_AUX_CTRL_NORM_EXT_LOOPBACK)
10887c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl);
1089bdb9230aSGarrett D'Amore 	bge_mii_put16(bgep, MII_MSCONTROL, gigctrl);
10901eff5f77SRijawanemohammadhusen Nadaf 	bge_mii_put16(bgep, MII_CONTROL, control);
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: anar <- 0x%x", anar));
10937c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: auxctrl <- 0x%x", auxctrl));
10947c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: gigctrl <- 0x%x", gigctrl));
10951eff5f77SRijawanemohammadhusen Nadaf 	BGE_DEBUG(("bge_update_copper: control <- 0x%x", control));
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate #if	BGE_COPPER_WIRESPEED
10987c478bd9Sstevel@tonic-gate 	/*
10997c478bd9Sstevel@tonic-gate 	 * Enable the 'wire-speed' feature, if the chip supports it
11007c478bd9Sstevel@tonic-gate 	 * and we haven't got (any) loopback mode selected.
11017c478bd9Sstevel@tonic-gate 	 */
11027c478bd9Sstevel@tonic-gate 	switch (bgep->chipid.device) {
11037c478bd9Sstevel@tonic-gate 	case DEVICE_ID_5700:
11047c478bd9Sstevel@tonic-gate 	case DEVICE_ID_5700x:
11057c478bd9Sstevel@tonic-gate 	case DEVICE_ID_5705C:
11067c478bd9Sstevel@tonic-gate 	case DEVICE_ID_5782:
11077c478bd9Sstevel@tonic-gate 		/*
11087c478bd9Sstevel@tonic-gate 		 * These chips are known or assumed not to support it
11097c478bd9Sstevel@tonic-gate 		 */
11107c478bd9Sstevel@tonic-gate 		break;
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate 	default:
11137c478bd9Sstevel@tonic-gate 		/*
11147c478bd9Sstevel@tonic-gate 		 * All other Broadcom chips are expected to support it.
11157c478bd9Sstevel@tonic-gate 		 */
11167c478bd9Sstevel@tonic-gate 		if (bgep->param_loop_mode == BGE_LOOP_NONE)
11177c478bd9Sstevel@tonic-gate 			bge_mii_put16(bgep, MII_AUX_CONTROL,
11187c478bd9Sstevel@tonic-gate 			    MII_AUX_CTRL_MISC_WRITE_ENABLE |
11197c478bd9Sstevel@tonic-gate 			    MII_AUX_CTRL_MISC_WIRE_SPEED |
11207c478bd9Sstevel@tonic-gate 			    MII_AUX_CTRL_MISC);
11217c478bd9Sstevel@tonic-gate 		break;
11227c478bd9Sstevel@tonic-gate 	}
11237c478bd9Sstevel@tonic-gate #endif	/* BGE_COPPER_WIRESPEED */
1124*087a28d1SDavid Gwynne 
1125*087a28d1SDavid Gwynne 	/* enable EEE on those chips that support it */
1126*087a28d1SDavid Gwynne 	bge_eee_autoneg(bgep, adv_100fdx, adv_1000fdx);
1127*087a28d1SDavid Gwynne 
112800d0963fSdilpreet 	return (DDI_SUCCESS);
11297c478bd9Sstevel@tonic-gate }
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate static boolean_t
11327c478bd9Sstevel@tonic-gate bge_check_copper(bge_t *bgep, boolean_t recheck)
11337c478bd9Sstevel@tonic-gate {
11347c478bd9Sstevel@tonic-gate 	uint32_t emac_status;
11357c478bd9Sstevel@tonic-gate 	uint16_t mii_status;
11367c478bd9Sstevel@tonic-gate 	uint16_t aux;
11377c478bd9Sstevel@tonic-gate 	uint_t mode;
11387c478bd9Sstevel@tonic-gate 	boolean_t linkup;
1139*087a28d1SDavid Gwynne 	int i;
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	/*
11427c478bd9Sstevel@tonic-gate 	 * Step 10: read the status from the PHY (which is self-clearing
11437c478bd9Sstevel@tonic-gate 	 * on read!); also read & clear the main (Ethernet) MAC status
11447c478bd9Sstevel@tonic-gate 	 * (the relevant bits of this are write-one-to-clear).
11457c478bd9Sstevel@tonic-gate 	 */
1146*087a28d1SDavid Gwynne 	for (i = 0; i < 100; i++) {
1147*087a28d1SDavid Gwynne 		drv_usecwait(40);
11487c478bd9Sstevel@tonic-gate 		mii_status = bge_mii_get16(bgep, MII_STATUS);
1149*087a28d1SDavid Gwynne 	}
11507c478bd9Sstevel@tonic-gate 	emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG);
11517c478bd9Sstevel@tonic-gate 	bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status);
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link %d/%s, MII status 0x%x "
11547c478bd9Sstevel@tonic-gate 	    "(was 0x%x), Ethernet MAC status 0x%x",
11557c478bd9Sstevel@tonic-gate 	    bgep->link_state, UPORDOWN(bgep->param_link_up), mii_status,
11567c478bd9Sstevel@tonic-gate 	    bgep->phy_gen_status, emac_status));
11577c478bd9Sstevel@tonic-gate 
11587c478bd9Sstevel@tonic-gate 	/*
11597c478bd9Sstevel@tonic-gate 	 * If the PHY status hasn't changed since last we looked, and
11607c478bd9Sstevel@tonic-gate 	 * we not forcing a recheck (i.e. the link state was already
11617c478bd9Sstevel@tonic-gate 	 * known), there's nothing to do.
11627c478bd9Sstevel@tonic-gate 	 */
1163*087a28d1SDavid Gwynne 	if (mii_status == bgep->phy_gen_status && !recheck) {
1164*087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_check_copper: no link change"));
11657c478bd9Sstevel@tonic-gate 		return (B_FALSE);
1166*087a28d1SDavid Gwynne 	}
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate 	do {
11697c478bd9Sstevel@tonic-gate 		/*
11707c478bd9Sstevel@tonic-gate 		 * Step 11: read AUX STATUS register to find speed/duplex
11717c478bd9Sstevel@tonic-gate 		 */
1172*087a28d1SDavid Gwynne 		for (i = 0; i < 2000; i++) {
1173*087a28d1SDavid Gwynne 			drv_usecwait(10);
11747c478bd9Sstevel@tonic-gate 			aux = bge_mii_get16(bgep, MII_AUX_STATUS);
1175*087a28d1SDavid Gwynne 		}
11767c478bd9Sstevel@tonic-gate 		BGE_CDB(bge_phydump, (bgep, mii_status, aux));
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 		/*
11797c478bd9Sstevel@tonic-gate 		 * We will only consider the link UP if all the readings
11807c478bd9Sstevel@tonic-gate 		 * are consistent and give meaningful results ...
11817c478bd9Sstevel@tonic-gate 		 */
11827c478bd9Sstevel@tonic-gate 		mode = aux & MII_AUX_STATUS_MODE_MASK;
11837c478bd9Sstevel@tonic-gate 		mode >>= MII_AUX_STATUS_MODE_SHIFT;
11845a506a18Syong tan - Sun Microsystems - Beijing China 		if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
11855a506a18Syong tan - Sun Microsystems - Beijing China 			linkup = BIS(aux, MII_AUX_STATUS_LINKUP);
11865a506a18Syong tan - Sun Microsystems - Beijing China 			linkup &= BIS(mii_status, MII_STATUS_LINKUP);
11875a506a18Syong tan - Sun Microsystems - Beijing China 		} else {
11887c478bd9Sstevel@tonic-gate 			linkup = bge_copper_link_speed[mode] > 0;
11895a506a18Syong tan - Sun Microsystems - Beijing China 			linkup &= bge_copper_link_duplex[mode] !=
11905a506a18Syong tan - Sun Microsystems - Beijing China 			    LINK_DUPLEX_UNKNOWN;
11917c478bd9Sstevel@tonic-gate 			linkup &= BIS(aux, MII_AUX_STATUS_LINKUP);
11927c478bd9Sstevel@tonic-gate 			linkup &= BIS(mii_status, MII_STATUS_LINKUP);
11935a506a18Syong tan - Sun Microsystems - Beijing China 		}
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate 		BGE_DEBUG(("bge_check_copper: MII status 0x%x aux 0x%x "
11967c478bd9Sstevel@tonic-gate 		    "=> mode %d (%s)",
11977c478bd9Sstevel@tonic-gate 		    mii_status, aux,
11987c478bd9Sstevel@tonic-gate 		    mode, UPORDOWN(linkup)));
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate 		/*
12017c478bd9Sstevel@tonic-gate 		 * Record current register values, then reread status
12027c478bd9Sstevel@tonic-gate 		 * register & loop until it stabilises ...
12037c478bd9Sstevel@tonic-gate 		 */
12047c478bd9Sstevel@tonic-gate 		bgep->phy_aux_status = aux;
12057c478bd9Sstevel@tonic-gate 		bgep->phy_gen_status = mii_status;
1206*087a28d1SDavid Gwynne 
1207*087a28d1SDavid Gwynne 		for (i = 0; i < 100; i++)
1208*087a28d1SDavid Gwynne 		{
1209*087a28d1SDavid Gwynne 			drv_usecwait(40);
12107c478bd9Sstevel@tonic-gate 			mii_status = bge_mii_get16(bgep, MII_STATUS);
1211*087a28d1SDavid Gwynne 		}
12127c478bd9Sstevel@tonic-gate 	} while (mii_status != bgep->phy_gen_status);
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	/*
12157c478bd9Sstevel@tonic-gate 	 * Assume very little ...
12167c478bd9Sstevel@tonic-gate 	 */
12177c478bd9Sstevel@tonic-gate 	bgep->param_lp_autoneg = B_FALSE;
12187c478bd9Sstevel@tonic-gate 	bgep->param_lp_1000fdx = B_FALSE;
12197c478bd9Sstevel@tonic-gate 	bgep->param_lp_1000hdx = B_FALSE;
12207c478bd9Sstevel@tonic-gate 	bgep->param_lp_100fdx = B_FALSE;
12217c478bd9Sstevel@tonic-gate 	bgep->param_lp_100hdx = B_FALSE;
12227c478bd9Sstevel@tonic-gate 	bgep->param_lp_10fdx = B_FALSE;
12237c478bd9Sstevel@tonic-gate 	bgep->param_lp_10hdx = B_FALSE;
12247c478bd9Sstevel@tonic-gate 	bgep->param_lp_pause = B_FALSE;
12257c478bd9Sstevel@tonic-gate 	bgep->param_lp_asym_pause = B_FALSE;
12267c478bd9Sstevel@tonic-gate 	bgep->param_link_autoneg = B_FALSE;
12277c478bd9Sstevel@tonic-gate 	bgep->param_link_tx_pause = B_FALSE;
12287c478bd9Sstevel@tonic-gate 	if (bgep->param_adv_autoneg)
12297c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = B_FALSE;
12307c478bd9Sstevel@tonic-gate 	else
12317c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = bgep->param_adv_pause;
12327c478bd9Sstevel@tonic-gate 
12337c478bd9Sstevel@tonic-gate 	/*
12347c478bd9Sstevel@tonic-gate 	 * Discover all the link partner's abilities.
1235256e438eSzh199473 	 * These are scattered through various registers ...
12367c478bd9Sstevel@tonic-gate 	 */
12377c478bd9Sstevel@tonic-gate 	if (BIS(aux, MII_AUX_STATUS_LP_ANEG_ABLE)) {
12387c478bd9Sstevel@tonic-gate 		bgep->param_lp_autoneg = B_TRUE;
12397c478bd9Sstevel@tonic-gate 		bgep->param_link_autoneg = B_TRUE;
12407c478bd9Sstevel@tonic-gate 		bgep->param_link_tx_pause = BIS(aux, MII_AUX_STATUS_TX_PAUSE);
12417c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = BIS(aux, MII_AUX_STATUS_RX_PAUSE);
12427c478bd9Sstevel@tonic-gate 
1243bdb9230aSGarrett D'Amore 		aux = bge_mii_get16(bgep, MII_MSSTATUS);
1244bdb9230aSGarrett D'Amore 		bgep->param_lp_1000fdx = BIS(aux, MII_MSSTATUS_LP1000T_FD);
1245bdb9230aSGarrett D'Amore 		bgep->param_lp_1000hdx = BIS(aux, MII_MSSTATUS_LP1000T);
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate 		aux = bge_mii_get16(bgep, MII_AN_LPABLE);
12487c478bd9Sstevel@tonic-gate 		bgep->param_lp_100fdx = BIS(aux, MII_ABILITY_100BASE_TX_FD);
12497c478bd9Sstevel@tonic-gate 		bgep->param_lp_100hdx = BIS(aux, MII_ABILITY_100BASE_TX);
12507c478bd9Sstevel@tonic-gate 		bgep->param_lp_10fdx = BIS(aux, MII_ABILITY_10BASE_T_FD);
12517c478bd9Sstevel@tonic-gate 		bgep->param_lp_10hdx = BIS(aux, MII_ABILITY_10BASE_T);
12527c478bd9Sstevel@tonic-gate 		bgep->param_lp_pause = BIS(aux, MII_ABILITY_PAUSE);
1253bdb9230aSGarrett D'Amore 		bgep->param_lp_asym_pause = BIS(aux, MII_ABILITY_ASMPAUSE);
12547c478bd9Sstevel@tonic-gate 	}
12557c478bd9Sstevel@tonic-gate 
12567c478bd9Sstevel@tonic-gate 	/*
12577c478bd9Sstevel@tonic-gate 	 * Step 12: update ndd-visible state parameters, BUT!
12587c478bd9Sstevel@tonic-gate 	 * we don't transfer the new state to <link_state> just yet;
12597c478bd9Sstevel@tonic-gate 	 * instead we mark the <link_state> as UNKNOWN, and our caller
12607c478bd9Sstevel@tonic-gate 	 * will resolve it once the status has stopped changing and
12617c478bd9Sstevel@tonic-gate 	 * been stable for several seconds.
12627c478bd9Sstevel@tonic-gate 	 */
12637c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link was %s speed %d duplex %d",
12647c478bd9Sstevel@tonic-gate 	    UPORDOWN(bgep->param_link_up),
12657c478bd9Sstevel@tonic-gate 	    bgep->param_link_speed,
12667c478bd9Sstevel@tonic-gate 	    bgep->param_link_duplex));
12677c478bd9Sstevel@tonic-gate 
12687c478bd9Sstevel@tonic-gate 	if (!linkup)
12697c478bd9Sstevel@tonic-gate 		mode = MII_AUX_STATUS_MODE_NONE;
12707c478bd9Sstevel@tonic-gate 	bgep->param_link_up = linkup;
12715a506a18Syong tan - Sun Microsystems - Beijing China 	bgep->link_state = LINK_STATE_UNKNOWN;
12725a506a18Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
12735a506a18Syong tan - Sun Microsystems - Beijing China 		if (bgep->phy_aux_status & MII_AUX_STATUS_NEG_ENABLED_5906) {
12745a506a18Syong tan - Sun Microsystems - Beijing China 			bgep->param_link_speed =
12755a506a18Syong tan - Sun Microsystems - Beijing China 			    bge_copper_link_speed_5906[mode];
12765a506a18Syong tan - Sun Microsystems - Beijing China 			bgep->param_link_duplex =
12775a506a18Syong tan - Sun Microsystems - Beijing China 			    bge_copper_link_duplex_5906[mode];
12785a506a18Syong tan - Sun Microsystems - Beijing China 		} else {
12795a506a18Syong tan - Sun Microsystems - Beijing China 			bgep->param_link_speed = (bgep->phy_aux_status &
12805a506a18Syong tan - Sun Microsystems - Beijing China 			    MII_AUX_STATUS_SPEED_IND_5906) ?  100 : 10;
12815a506a18Syong tan - Sun Microsystems - Beijing China 			bgep->param_link_duplex = (bgep->phy_aux_status &
12825a506a18Syong tan - Sun Microsystems - Beijing China 			    MII_AUX_STATUS_DUPLEX_IND_5906) ? LINK_DUPLEX_FULL :
12835a506a18Syong tan - Sun Microsystems - Beijing China 			    LINK_DUPLEX_HALF;
12845a506a18Syong tan - Sun Microsystems - Beijing China 		}
12855a506a18Syong tan - Sun Microsystems - Beijing China 	} else {
12867c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = bge_copper_link_speed[mode];
12877c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = bge_copper_link_duplex[mode];
12885a506a18Syong tan - Sun Microsystems - Beijing China 	}
12897c478bd9Sstevel@tonic-gate 
1290*087a28d1SDavid Gwynne 	bge_eee_adjust(bgep);
1291*087a28d1SDavid Gwynne 
1292*087a28d1SDavid Gwynne 	bge_log(bgep, "bge_check_copper: link now %s speed %d duplex %d",
12937c478bd9Sstevel@tonic-gate 	        UPORDOWN(bgep->param_link_up),
12947c478bd9Sstevel@tonic-gate 	        bgep->param_link_speed,
1295*087a28d1SDavid Gwynne 	        bgep->param_link_duplex);
12967c478bd9Sstevel@tonic-gate 
12977c478bd9Sstevel@tonic-gate 	return (B_TRUE);
12987c478bd9Sstevel@tonic-gate }
12997c478bd9Sstevel@tonic-gate 
13007c478bd9Sstevel@tonic-gate static const phys_ops_t copper_ops = {
13017c478bd9Sstevel@tonic-gate 	bge_restart_copper,
13027c478bd9Sstevel@tonic-gate 	bge_update_copper,
13037c478bd9Sstevel@tonic-gate 	bge_check_copper
13047c478bd9Sstevel@tonic-gate };
13057c478bd9Sstevel@tonic-gate 
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate /*
13087c478bd9Sstevel@tonic-gate  * ========== SerDes support ==========
13097c478bd9Sstevel@tonic-gate  */
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate #undef	BGE_DBG
13127c478bd9Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_SERDES	/* debug flag for this code	*/
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate /*
13157c478bd9Sstevel@tonic-gate  * Reinitialise the SerDes interface.  Note that it normally powers
13167c478bd9Sstevel@tonic-gate  * up in the disabled state, so we need to explicitly activate it.
13177c478bd9Sstevel@tonic-gate  */
131800d0963fSdilpreet static int
13197c478bd9Sstevel@tonic-gate bge_restart_serdes(bge_t *bgep, boolean_t powerdown)
13207c478bd9Sstevel@tonic-gate {
13217c478bd9Sstevel@tonic-gate 	uint32_t macmode;
13227c478bd9Sstevel@tonic-gate 
13237c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_restart_serdes($%p, %d)", (void *)bgep, powerdown));
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate 	/*
13287c478bd9Sstevel@tonic-gate 	 * Ensure that the main Ethernet MAC mode register is programmed
13297c478bd9Sstevel@tonic-gate 	 * appropriately for the SerDes interface ...
13307c478bd9Sstevel@tonic-gate 	 */
13317c478bd9Sstevel@tonic-gate 	macmode = bge_reg_get32(bgep, ETHERNET_MAC_MODE_REG);
13327c478bd9Sstevel@tonic-gate 	macmode &= ~ETHERNET_MODE_LINK_POLARITY;
13337c478bd9Sstevel@tonic-gate 	macmode &= ~ETHERNET_MODE_PORTMODE_MASK;
1334*087a28d1SDavid Gwynne 	if (DEVICE_5717_SERIES_CHIPSETS(bgep) ||
1335*087a28d1SDavid Gwynne 	    DEVICE_5725_SERIES_CHIPSETS(bgep) ||
1336*087a28d1SDavid Gwynne 	    DEVICE_5714_SERIES_CHIPSETS(bgep)) {
1337*087a28d1SDavid Gwynne 		macmode |= ETHERNET_MODE_PORTMODE_GMII;
1338*087a28d1SDavid Gwynne 	} else {
13397c478bd9Sstevel@tonic-gate 		macmode |= ETHERNET_MODE_PORTMODE_TBI;
13401a719488SCrisson Guanghao Hu 	}
13417c478bd9Sstevel@tonic-gate 	bge_reg_put32(bgep, ETHERNET_MAC_MODE_REG, macmode);
13427c478bd9Sstevel@tonic-gate 
13437c478bd9Sstevel@tonic-gate 	/*
13447c478bd9Sstevel@tonic-gate 	 * Ensure that loopback is OFF and comma detection is enabled.  Then
13457c478bd9Sstevel@tonic-gate 	 * disable the SerDes output (the first time through, it may/will
13467c478bd9Sstevel@tonic-gate 	 * already be disabled).  If we're shutting down, leave it disabled.
13477c478bd9Sstevel@tonic-gate 	 */
13487c478bd9Sstevel@tonic-gate 	bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TBI_LOOPBACK);
13497c478bd9Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_COMMA_DETECT);
13507c478bd9Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
13517c478bd9Sstevel@tonic-gate 	if (powerdown)
135200d0963fSdilpreet 		return (DDI_SUCCESS);
13537c478bd9Sstevel@tonic-gate 
13547c478bd9Sstevel@tonic-gate 	/*
13557c478bd9Sstevel@tonic-gate 	 * Otherwise, pause, (re-)enable the SerDes output, and send
13567c478bd9Sstevel@tonic-gate 	 * all-zero config words in order to force autoneg restart.
13577c478bd9Sstevel@tonic-gate 	 * Invalidate the saved "link partners received configs", as
13587c478bd9Sstevel@tonic-gate 	 * we're starting over ...
13597c478bd9Sstevel@tonic-gate 	 */
13607c478bd9Sstevel@tonic-gate 	drv_usecwait(10000);
13617c478bd9Sstevel@tonic-gate 	bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
13627c478bd9Sstevel@tonic-gate 	bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG, 0);
13637c478bd9Sstevel@tonic-gate 	bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
13647c478bd9Sstevel@tonic-gate 	drv_usecwait(10);
13657c478bd9Sstevel@tonic-gate 	bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
13667c478bd9Sstevel@tonic-gate 	bgep->serdes_lpadv = AUTONEG_CODE_FAULT_ANEG_ERR;
13677c478bd9Sstevel@tonic-gate 	bgep->serdes_status = ~0U;
136800d0963fSdilpreet 	return (DDI_SUCCESS);
13697c478bd9Sstevel@tonic-gate }
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate /*
13727c478bd9Sstevel@tonic-gate  * Synchronise the SerDes speed/duplex/autonegotiation capabilities and
13737c478bd9Sstevel@tonic-gate  * advertisements with the required settings as specified by the various
13747c478bd9Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
13757c478bd9Sstevel@tonic-gate  *
13767c478bd9Sstevel@tonic-gate  * We always reinitalise the SerDes; this should cause the link to go down,
13777c478bd9Sstevel@tonic-gate  * and then back up again once the link is stable and autonegotiation
13787c478bd9Sstevel@tonic-gate  * (if enabled) is complete.  We should get a link state change interrupt
13797c478bd9Sstevel@tonic-gate  * somewhere along the way ...
13807c478bd9Sstevel@tonic-gate  *
13817c478bd9Sstevel@tonic-gate  * NOTE: SerDes only supports 1000FDX/HDX (with or without pause) so the
13827c478bd9Sstevel@tonic-gate  * param_* variables relating to lower speeds are ignored.
13837c478bd9Sstevel@tonic-gate  *
13847c478bd9Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
13857c478bd9Sstevel@tonic-gate  */
138600d0963fSdilpreet static int
13877c478bd9Sstevel@tonic-gate bge_update_serdes(bge_t *bgep)
13887c478bd9Sstevel@tonic-gate {
13897c478bd9Sstevel@tonic-gate 	boolean_t adv_autoneg;
13907c478bd9Sstevel@tonic-gate 	boolean_t adv_pause;
13917c478bd9Sstevel@tonic-gate 	boolean_t adv_asym_pause;
13927c478bd9Sstevel@tonic-gate 	boolean_t adv_1000fdx;
13937c478bd9Sstevel@tonic-gate 	boolean_t adv_1000hdx;
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 	uint32_t serdes;
13967c478bd9Sstevel@tonic-gate 	uint32_t advert;
13977c478bd9Sstevel@tonic-gate 
13987c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_update_serdes($%p)", (void *)bgep));
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
14017c478bd9Sstevel@tonic-gate 
14027c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: autoneg %d "
14037c478bd9Sstevel@tonic-gate 	    "pause %d asym_pause %d "
14047c478bd9Sstevel@tonic-gate 	    "1000fdx %d 1000hdx %d "
14057c478bd9Sstevel@tonic-gate 	    "100fdx %d 100hdx %d "
14067c478bd9Sstevel@tonic-gate 	    "10fdx %d 10hdx %d ",
14077c478bd9Sstevel@tonic-gate 	    bgep->param_adv_autoneg,
14087c478bd9Sstevel@tonic-gate 	    bgep->param_adv_pause, bgep->param_adv_asym_pause,
14097c478bd9Sstevel@tonic-gate 	    bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
14107c478bd9Sstevel@tonic-gate 	    bgep->param_adv_100fdx, bgep->param_adv_100hdx,
14117c478bd9Sstevel@tonic-gate 	    bgep->param_adv_10fdx, bgep->param_adv_10hdx));
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 	serdes = advert = 0;
14147c478bd9Sstevel@tonic-gate 
14157c478bd9Sstevel@tonic-gate 	/*
14167c478bd9Sstevel@tonic-gate 	 * SerDes settings are normally based on the param_* variables,
14177c478bd9Sstevel@tonic-gate 	 * but if any loopback mode is in effect, that takes precedence.
14187c478bd9Sstevel@tonic-gate 	 *
14197c478bd9Sstevel@tonic-gate 	 * BGE supports MAC-internal loopback, PHY-internal loopback,
14207c478bd9Sstevel@tonic-gate 	 * and External loopback at a variety of speeds (with a special
14217c478bd9Sstevel@tonic-gate 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
14227c478bd9Sstevel@tonic-gate 	 * is turned ON, and the speed/mastership is forced.
14237c478bd9Sstevel@tonic-gate 	 *
14247c478bd9Sstevel@tonic-gate 	 * Note: for the SerDes interface, "PHY" internal loopback is
14257c478bd9Sstevel@tonic-gate 	 * interpreted as SerDes internal loopback, and all external
14267c478bd9Sstevel@tonic-gate 	 * loopback modes are treated equivalently, as 1Gb/external.
14277c478bd9Sstevel@tonic-gate 	 */
14287c478bd9Sstevel@tonic-gate 	switch (bgep->param_loop_mode) {
14297c478bd9Sstevel@tonic-gate 	case BGE_LOOP_NONE:
14307c478bd9Sstevel@tonic-gate 	default:
14317c478bd9Sstevel@tonic-gate 		adv_autoneg = bgep->param_adv_autoneg;
14327c478bd9Sstevel@tonic-gate 		adv_pause = bgep->param_adv_pause;
14337c478bd9Sstevel@tonic-gate 		adv_asym_pause = bgep->param_adv_asym_pause;
14347c478bd9Sstevel@tonic-gate 		adv_1000fdx = bgep->param_adv_1000fdx;
14357c478bd9Sstevel@tonic-gate 		adv_1000hdx = bgep->param_adv_1000hdx;
14367c478bd9Sstevel@tonic-gate 		break;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_PHY:
14397c478bd9Sstevel@tonic-gate 		serdes |= SERDES_CONTROL_TBI_LOOPBACK;
14407c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
14417c478bd9Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_MAC:
14427c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_1000:
14437c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_100:
14447c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_10:
14457c478bd9Sstevel@tonic-gate 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
14467c478bd9Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
14477c478bd9Sstevel@tonic-gate 		adv_1000hdx = B_FALSE;
14487c478bd9Sstevel@tonic-gate 		break;
14497c478bd9Sstevel@tonic-gate 	}
14507c478bd9Sstevel@tonic-gate 
14517c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: autoneg %d "
14527c478bd9Sstevel@tonic-gate 	    "pause %d asym_pause %d "
14537c478bd9Sstevel@tonic-gate 	    "1000fdx %d 1000hdx %d ",
14547c478bd9Sstevel@tonic-gate 	    adv_autoneg,
14557c478bd9Sstevel@tonic-gate 	    adv_pause, adv_asym_pause,
14567c478bd9Sstevel@tonic-gate 	    adv_1000fdx, adv_1000hdx));
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	/*
14597c478bd9Sstevel@tonic-gate 	 * We should have at least one gigabit technology capability
14607c478bd9Sstevel@tonic-gate 	 * set; if not, we select a default of 1000Mb/s full-duplex
14617c478bd9Sstevel@tonic-gate 	 */
14627c478bd9Sstevel@tonic-gate 	if (!adv_1000fdx && !adv_1000hdx)
14637c478bd9Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
14647c478bd9Sstevel@tonic-gate 
14657c478bd9Sstevel@tonic-gate 	/*
14667c478bd9Sstevel@tonic-gate 	 * Now transform the adv_* variables into the proper settings
14677c478bd9Sstevel@tonic-gate 	 * of the SerDes registers ...
14687c478bd9Sstevel@tonic-gate 	 *
14697c478bd9Sstevel@tonic-gate 	 * If autonegotiation is (now) not enabled, pretend it's been
14707c478bd9Sstevel@tonic-gate 	 * done and failed ...
14717c478bd9Sstevel@tonic-gate 	 */
14727c478bd9Sstevel@tonic-gate 	if (!adv_autoneg)
14737c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_FAULT_ANEG_ERR;
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	if (adv_1000fdx) {
14767c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_FULL_DUPLEX;
14777c478bd9Sstevel@tonic-gate 		bgep->param_adv_1000fdx = adv_1000fdx;
14787c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_FULL;
14797c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
14807c478bd9Sstevel@tonic-gate 	}
14817c478bd9Sstevel@tonic-gate 	if (adv_1000hdx) {
14827c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_HALF_DUPLEX;
14837c478bd9Sstevel@tonic-gate 		bgep->param_adv_1000hdx = adv_1000hdx;
14847c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_HALF;
14857c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
14867c478bd9Sstevel@tonic-gate 	}
14877c478bd9Sstevel@tonic-gate 
14887c478bd9Sstevel@tonic-gate 	if (adv_pause)
14897c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_PAUSE;
14907c478bd9Sstevel@tonic-gate 	if (adv_asym_pause)
14917c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_ASYM_PAUSE;
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate 	/*
14947c478bd9Sstevel@tonic-gate 	 * Restart the SerDes and write the new values.  Note the
14957c478bd9Sstevel@tonic-gate 	 * time, so that we can say whether subsequent link state
14967c478bd9Sstevel@tonic-gate 	 * changes can be attributed to our reprogramming the SerDes
14977c478bd9Sstevel@tonic-gate 	 */
14987c478bd9Sstevel@tonic-gate 	bgep->serdes_advert = advert;
149900d0963fSdilpreet 	(void) bge_restart_serdes(bgep, B_FALSE);
15007c478bd9Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, serdes);
15017c478bd9Sstevel@tonic-gate 
15027c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: serdes |= 0x%x, advert 0x%x",
15037c478bd9Sstevel@tonic-gate 	    serdes, advert));
150400d0963fSdilpreet 	return (DDI_SUCCESS);
15057c478bd9Sstevel@tonic-gate }
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate /*
15087c478bd9Sstevel@tonic-gate  * Bare-minimum autoneg protocol
15097c478bd9Sstevel@tonic-gate  *
15107c478bd9Sstevel@tonic-gate  * This code is only called when the link is up and we're receiving config
15117c478bd9Sstevel@tonic-gate  * words, which implies that the link partner wants to autonegotiate
15127c478bd9Sstevel@tonic-gate  * (otherwise, we wouldn't see configs and wouldn't reach this code).
15137c478bd9Sstevel@tonic-gate  */
15147c478bd9Sstevel@tonic-gate static void
15157c478bd9Sstevel@tonic-gate bge_autoneg_serdes(bge_t *bgep)
15167c478bd9Sstevel@tonic-gate {
15177c478bd9Sstevel@tonic-gate 	boolean_t ack;
15187c478bd9Sstevel@tonic-gate 
15197c478bd9Sstevel@tonic-gate 	bgep->serdes_lpadv = bge_reg_get32(bgep, RX_1000BASEX_AUTONEG_REG);
15207c478bd9Sstevel@tonic-gate 	ack = BIS(bgep->serdes_lpadv, AUTONEG_CODE_ACKNOWLEDGE);
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate 	if (!ack) {
15237c478bd9Sstevel@tonic-gate 		/*
15247c478bd9Sstevel@tonic-gate 		 * Phase 1: after SerDes reset, we send a few zero configs
15257c478bd9Sstevel@tonic-gate 		 * but then stop.  Here the partner is sending configs, but
15267c478bd9Sstevel@tonic-gate 		 * not ACKing ours; we assume that's 'cos we're not sending
15277c478bd9Sstevel@tonic-gate 		 * any.  So here we send ours, with ACK already set.
15287c478bd9Sstevel@tonic-gate 		 */
15297c478bd9Sstevel@tonic-gate 		bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG,
15307c478bd9Sstevel@tonic-gate 		    bgep->serdes_advert | AUTONEG_CODE_ACKNOWLEDGE);
15317c478bd9Sstevel@tonic-gate 		bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG,
15327c478bd9Sstevel@tonic-gate 		    ETHERNET_MODE_SEND_CFGS);
15337c478bd9Sstevel@tonic-gate 	} else {
15347c478bd9Sstevel@tonic-gate 		/*
15357c478bd9Sstevel@tonic-gate 		 * Phase 2: partner has ACKed our configs, so now we can
15367c478bd9Sstevel@tonic-gate 		 * stop sending; once our partner also stops sending, we
15377c478bd9Sstevel@tonic-gate 		 * can resolve the Tx/Rx configs.
15387c478bd9Sstevel@tonic-gate 		 */
15397c478bd9Sstevel@tonic-gate 		bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG,
15407c478bd9Sstevel@tonic-gate 		    ETHERNET_MODE_SEND_CFGS);
15417c478bd9Sstevel@tonic-gate 	}
15427c478bd9Sstevel@tonic-gate 
15437c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_autoneg_serdes: Rx 0x%x %s Tx 0x%x",
15447c478bd9Sstevel@tonic-gate 	    bgep->serdes_lpadv,
15457c478bd9Sstevel@tonic-gate 	    ack ? "stop" : "send",
15467c478bd9Sstevel@tonic-gate 	    bgep->serdes_advert));
15477c478bd9Sstevel@tonic-gate }
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate static boolean_t
15507c478bd9Sstevel@tonic-gate bge_check_serdes(bge_t *bgep, boolean_t recheck)
15517c478bd9Sstevel@tonic-gate {
15527c478bd9Sstevel@tonic-gate 	uint32_t emac_status;
1553c4e25a46Syong tan - Sun Microsystems - Beijing China 	uint32_t tx_status;
15547c478bd9Sstevel@tonic-gate 	uint32_t lpadv;
15557c478bd9Sstevel@tonic-gate 	boolean_t linkup;
15561a719488SCrisson Guanghao Hu 	boolean_t linkup_old = bgep->param_link_up;
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	for (;;) {
15597c478bd9Sstevel@tonic-gate 		/*
15601a719488SCrisson Guanghao Hu 		 * Step 10: BCM5714S, BCM5715S only
15611a719488SCrisson Guanghao Hu 		 * Don't call function bge_autoneg_serdes() as
15621a719488SCrisson Guanghao Hu 		 * RX_1000BASEX_AUTONEG_REG (0x0448) is not applicable
15631a719488SCrisson Guanghao Hu 		 * to BCM5705, BCM5788, BCM5721, BCM5751, BCM5752,
15641a719488SCrisson Guanghao Hu 		 * BCM5714, and BCM5715 devices.
15651a719488SCrisson Guanghao Hu 		 */
1566*087a28d1SDavid Gwynne 		if (DEVICE_5717_SERIES_CHIPSETS(bgep) ||
1567*087a28d1SDavid Gwynne 		    DEVICE_5725_SERIES_CHIPSETS(bgep) ||
1568*087a28d1SDavid Gwynne 		    DEVICE_5714_SERIES_CHIPSETS(bgep)) {
1569c4e25a46Syong tan - Sun Microsystems - Beijing China 			tx_status = bge_reg_get32(bgep,
1570c4e25a46Syong tan - Sun Microsystems - Beijing China 			    TRANSMIT_MAC_STATUS_REG);
1571c4e25a46Syong tan - Sun Microsystems - Beijing China 			linkup = BIS(tx_status, TRANSMIT_STATUS_LINK_UP);
1572c4e25a46Syong tan - Sun Microsystems - Beijing China 			emac_status = bge_reg_get32(bgep,
1573c4e25a46Syong tan - Sun Microsystems - Beijing China 			    ETHERNET_MAC_STATUS_REG);
15741a719488SCrisson Guanghao Hu 			bgep->serdes_status = emac_status;
1575*087a28d1SDavid Gwynne 			/* clear write-one-to-clear bits in MAC status */
1576*087a28d1SDavid Gwynne 			if ((emac_status & ETHERNET_STATUS_MI_COMPLETE) &&
1577*087a28d1SDavid Gwynne 			    (DEVICE_5717_SERIES_CHIPSETS(bgep) ||
1578*087a28d1SDavid Gwynne 			     DEVICE_5725_SERIES_CHIPSETS(bgep))) {
1579*087a28d1SDavid Gwynne 				emac_status |= ETHERNET_STATUS_SYNC_CHANGED |
1580*087a28d1SDavid Gwynne 				    ETHERNET_STATUS_CFG_CHANGED;
1581*087a28d1SDavid Gwynne 			}
1582*087a28d1SDavid Gwynne 			bge_reg_put32(bgep,
1583*087a28d1SDavid Gwynne 			    ETHERNET_MAC_STATUS_REG, emac_status);
1584*087a28d1SDavid Gwynne 			/*
1585*087a28d1SDavid Gwynne 			 * If the link status has not changed then then
1586*087a28d1SDavid Gwynne 			 * break. If it has loop around and recheck again.
1587*087a28d1SDavid Gwynne 			 * Keep looping until the link status has not
1588*087a28d1SDavid Gwynne 			 * changed.
1589*087a28d1SDavid Gwynne 			 */
15901a719488SCrisson Guanghao Hu 			if ((linkup && linkup_old) ||
15911a719488SCrisson Guanghao Hu 			    (!linkup && !linkup_old)) {
15921a719488SCrisson Guanghao Hu 				break;
15931a719488SCrisson Guanghao Hu 			}
15941a719488SCrisson Guanghao Hu 			if (linkup)
15951a719488SCrisson Guanghao Hu 				linkup_old = B_TRUE;
15961a719488SCrisson Guanghao Hu 			else
15971a719488SCrisson Guanghao Hu 				linkup_old = B_FALSE;
15981a719488SCrisson Guanghao Hu 			recheck = B_TRUE;
15991a719488SCrisson Guanghao Hu 		} else {
16001a719488SCrisson Guanghao Hu 			/*
16011a719488SCrisson Guanghao Hu 			 * Step 10: others
16021a719488SCrisson Guanghao Hu 			 * read & clear the main (Ethernet) MAC status
16037c478bd9Sstevel@tonic-gate 			 * (the relevant bits of this are write-one-to-clear).
16047c478bd9Sstevel@tonic-gate 			 */
16051a719488SCrisson Guanghao Hu 			emac_status = bge_reg_get32(bgep,
16061a719488SCrisson Guanghao Hu 			    ETHERNET_MAC_STATUS_REG);
16071a719488SCrisson Guanghao Hu 			bge_reg_put32(bgep,
16081a719488SCrisson Guanghao Hu 			    ETHERNET_MAC_STATUS_REG, emac_status);
16097c478bd9Sstevel@tonic-gate 
16107c478bd9Sstevel@tonic-gate 			BGE_DEBUG(("bge_check_serdes: link %d/%s, "
16117c478bd9Sstevel@tonic-gate 			    "MAC status 0x%x (was 0x%x)",
16127c478bd9Sstevel@tonic-gate 			    bgep->link_state, UPORDOWN(bgep->param_link_up),
16137c478bd9Sstevel@tonic-gate 			    emac_status, bgep->serdes_status));
16147c478bd9Sstevel@tonic-gate 
16157c478bd9Sstevel@tonic-gate 			/*
16167c478bd9Sstevel@tonic-gate 			 * We will only consider the link UP if all the readings
16177c478bd9Sstevel@tonic-gate 			 * are consistent and give meaningful results ...
16187c478bd9Sstevel@tonic-gate 			 */
16197c478bd9Sstevel@tonic-gate 			bgep->serdes_status = emac_status;
16201a719488SCrisson Guanghao Hu 			linkup = BIS(emac_status,
16211a719488SCrisson Guanghao Hu 			    ETHERNET_STATUS_SIGNAL_DETECT);
16227c478bd9Sstevel@tonic-gate 			linkup &= BIS(emac_status, ETHERNET_STATUS_PCS_SYNCHED);
16237c478bd9Sstevel@tonic-gate 
16247c478bd9Sstevel@tonic-gate 			/*
16257c478bd9Sstevel@tonic-gate 			 * Now some fiddling with the interpretation:
16267c478bd9Sstevel@tonic-gate 			 *	if there's been an error at the PCS level, treat
16277c478bd9Sstevel@tonic-gate 			 *	it as a link change (the h/w doesn't do this)
16287c478bd9Sstevel@tonic-gate 			 *
16291a719488SCrisson Guanghao Hu 			 *	if there's been a change, but it's only a PCS
16301a719488SCrisson Guanghao Hu 			 *	sync change (not a config change), AND the link
16311a719488SCrisson Guanghao Hu 			 *	already was & is still UP, then ignore the
16321a719488SCrisson Guanghao Hu 			 *	change
16337c478bd9Sstevel@tonic-gate 			 */
16347c478bd9Sstevel@tonic-gate 			if (BIS(emac_status, ETHERNET_STATUS_PCS_ERROR))
16357c478bd9Sstevel@tonic-gate 				emac_status |= ETHERNET_STATUS_LINK_CHANGED;
16367c478bd9Sstevel@tonic-gate 			else if (BIC(emac_status, ETHERNET_STATUS_CFG_CHANGED))
16377c478bd9Sstevel@tonic-gate 				if (bgep->param_link_up && linkup)
16381a719488SCrisson Guanghao Hu 					emac_status &=
16391a719488SCrisson Guanghao Hu 					    ~ETHERNET_STATUS_LINK_CHANGED;
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 			BGE_DEBUG(("bge_check_serdes: status 0x%x => 0x%x %s",
16421a719488SCrisson Guanghao Hu 			    bgep->serdes_status, emac_status,
16431a719488SCrisson Guanghao Hu 			    UPORDOWN(linkup)));
16447c478bd9Sstevel@tonic-gate 
16457c478bd9Sstevel@tonic-gate 			/*
16467c478bd9Sstevel@tonic-gate 			 * If we're receiving configs, run the autoneg protocol
16477c478bd9Sstevel@tonic-gate 			 */
16481a719488SCrisson Guanghao Hu 			if (linkup && BIS(emac_status,
16491a719488SCrisson Guanghao Hu 			    ETHERNET_STATUS_RECEIVING_CFG))
16507c478bd9Sstevel@tonic-gate 				bge_autoneg_serdes(bgep);
16517c478bd9Sstevel@tonic-gate 
16527c478bd9Sstevel@tonic-gate 			/*
16537c478bd9Sstevel@tonic-gate 			 * If the SerDes status hasn't changed, we're done ...
16547c478bd9Sstevel@tonic-gate 			 */
16557c478bd9Sstevel@tonic-gate 			if (BIC(emac_status, ETHERNET_STATUS_LINK_CHANGED))
16567c478bd9Sstevel@tonic-gate 				break;
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate 			/*
16590d2a8e5eSgd78059 			 * Go round again until we no longer see a change ...
16607c478bd9Sstevel@tonic-gate 			 */
16617c478bd9Sstevel@tonic-gate 			recheck = B_TRUE;
16627c478bd9Sstevel@tonic-gate 		}
16631a719488SCrisson Guanghao Hu 	}
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 	/*
16667c478bd9Sstevel@tonic-gate 	 * If we're not forcing a recheck (i.e. the link state was already
16677c478bd9Sstevel@tonic-gate 	 * known), and we didn't see the hardware flag a change, there's
16687c478bd9Sstevel@tonic-gate 	 * no more to do (and we tell the caller nothing happened).
16697c478bd9Sstevel@tonic-gate 	 */
16707c478bd9Sstevel@tonic-gate 	if (!recheck)
16717c478bd9Sstevel@tonic-gate 		return (B_FALSE);
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 	/*
16747c478bd9Sstevel@tonic-gate 	 * Don't resolve autoneg until we're no longer receiving configs
16757c478bd9Sstevel@tonic-gate 	 */
16767c478bd9Sstevel@tonic-gate 	if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG))
16777c478bd9Sstevel@tonic-gate 		return (B_FALSE);
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	/*
16807c478bd9Sstevel@tonic-gate 	 * Assume very little ...
16817c478bd9Sstevel@tonic-gate 	 */
16827c478bd9Sstevel@tonic-gate 	bgep->param_lp_autoneg = B_FALSE;
16837c478bd9Sstevel@tonic-gate 	bgep->param_lp_1000fdx = B_FALSE;
16847c478bd9Sstevel@tonic-gate 	bgep->param_lp_1000hdx = B_FALSE;
16857c478bd9Sstevel@tonic-gate 	bgep->param_lp_100fdx = B_FALSE;
16867c478bd9Sstevel@tonic-gate 	bgep->param_lp_100hdx = B_FALSE;
16877c478bd9Sstevel@tonic-gate 	bgep->param_lp_10fdx = B_FALSE;
16887c478bd9Sstevel@tonic-gate 	bgep->param_lp_10hdx = B_FALSE;
16897c478bd9Sstevel@tonic-gate 	bgep->param_lp_pause = B_FALSE;
16907c478bd9Sstevel@tonic-gate 	bgep->param_lp_asym_pause = B_FALSE;
16917c478bd9Sstevel@tonic-gate 	bgep->param_link_autoneg = B_FALSE;
16927c478bd9Sstevel@tonic-gate 	bgep->param_link_tx_pause = B_FALSE;
16937c478bd9Sstevel@tonic-gate 	if (bgep->param_adv_autoneg)
16947c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = B_FALSE;
16957c478bd9Sstevel@tonic-gate 	else
16967c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = bgep->param_adv_pause;
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate 	/*
16997c478bd9Sstevel@tonic-gate 	 * Discover all the link partner's abilities.
17007c478bd9Sstevel@tonic-gate 	 */
17017c478bd9Sstevel@tonic-gate 	lpadv = bgep->serdes_lpadv;
17027c478bd9Sstevel@tonic-gate 	if (lpadv != 0 && BIC(lpadv, AUTONEG_CODE_FAULT_MASK)) {
17037c478bd9Sstevel@tonic-gate 		/*
17047c478bd9Sstevel@tonic-gate 		 * No fault, so derive partner's capabilities
17057c478bd9Sstevel@tonic-gate 		 */
17067c478bd9Sstevel@tonic-gate 		bgep->param_lp_autoneg = B_TRUE;
17077c478bd9Sstevel@tonic-gate 		bgep->param_lp_1000fdx = BIS(lpadv, AUTONEG_CODE_FULL_DUPLEX);
17087c478bd9Sstevel@tonic-gate 		bgep->param_lp_1000hdx = BIS(lpadv, AUTONEG_CODE_HALF_DUPLEX);
17097c478bd9Sstevel@tonic-gate 		bgep->param_lp_pause = BIS(lpadv, AUTONEG_CODE_PAUSE);
17107c478bd9Sstevel@tonic-gate 		bgep->param_lp_asym_pause = BIS(lpadv, AUTONEG_CODE_ASYM_PAUSE);
17117c478bd9Sstevel@tonic-gate 
17127c478bd9Sstevel@tonic-gate 		/*
17137c478bd9Sstevel@tonic-gate 		 * Pause direction resolution
17147c478bd9Sstevel@tonic-gate 		 */
17157c478bd9Sstevel@tonic-gate 		bgep->param_link_autoneg = B_TRUE;
17167c478bd9Sstevel@tonic-gate 		if (bgep->param_adv_pause &&
17177c478bd9Sstevel@tonic-gate 		    bgep->param_lp_pause) {
17187c478bd9Sstevel@tonic-gate 			bgep->param_link_tx_pause = B_TRUE;
17197c478bd9Sstevel@tonic-gate 			bgep->param_link_rx_pause = B_TRUE;
17207c478bd9Sstevel@tonic-gate 		}
17217c478bd9Sstevel@tonic-gate 		if (bgep->param_adv_asym_pause &&
17227c478bd9Sstevel@tonic-gate 		    bgep->param_lp_asym_pause) {
17237c478bd9Sstevel@tonic-gate 			if (bgep->param_adv_pause)
17247c478bd9Sstevel@tonic-gate 				bgep->param_link_rx_pause = B_TRUE;
17257c478bd9Sstevel@tonic-gate 			if (bgep->param_lp_pause)
17267c478bd9Sstevel@tonic-gate 				bgep->param_link_tx_pause = B_TRUE;
17277c478bd9Sstevel@tonic-gate 		}
17287c478bd9Sstevel@tonic-gate 	}
17297c478bd9Sstevel@tonic-gate 
17307c478bd9Sstevel@tonic-gate 	/*
17317c478bd9Sstevel@tonic-gate 	 * Step 12: update ndd-visible state parameters, BUT!
17327c478bd9Sstevel@tonic-gate 	 * we don't transfer the new state to <link_state> just yet;
17337c478bd9Sstevel@tonic-gate 	 * instead we mark the <link_state> as UNKNOWN, and our caller
17347c478bd9Sstevel@tonic-gate 	 * will resolve it once the status has stopped changing and
17357c478bd9Sstevel@tonic-gate 	 * been stable for several seconds.
17367c478bd9Sstevel@tonic-gate 	 */
17377c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_serdes: link was %s speed %d duplex %d",
17387c478bd9Sstevel@tonic-gate 	    UPORDOWN(bgep->param_link_up),
17397c478bd9Sstevel@tonic-gate 	    bgep->param_link_speed,
17407c478bd9Sstevel@tonic-gate 	    bgep->param_link_duplex));
17417c478bd9Sstevel@tonic-gate 
17427c478bd9Sstevel@tonic-gate 	if (linkup) {
17437c478bd9Sstevel@tonic-gate 		bgep->param_link_up = B_TRUE;
17447c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
17457c478bd9Sstevel@tonic-gate 		if (bgep->param_adv_1000fdx)
17467c478bd9Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_FULL;
17477c478bd9Sstevel@tonic-gate 		else
17487c478bd9Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_HALF;
17497c478bd9Sstevel@tonic-gate 		if (bgep->param_lp_autoneg && !bgep->param_lp_1000fdx)
17507c478bd9Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_HALF;
17517c478bd9Sstevel@tonic-gate 	} else {
17527c478bd9Sstevel@tonic-gate 		bgep->param_link_up = B_FALSE;
17537c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = 0;
17547c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_UNKNOWN;
17557c478bd9Sstevel@tonic-gate 	}
17567c478bd9Sstevel@tonic-gate 	bgep->link_state = LINK_STATE_UNKNOWN;
17577c478bd9Sstevel@tonic-gate 
1758*087a28d1SDavid Gwynne 	bge_log(bgep, "bge_check_serdes: link now %s speed %d duplex %d",
17597c478bd9Sstevel@tonic-gate 	        UPORDOWN(bgep->param_link_up),
17607c478bd9Sstevel@tonic-gate 	        bgep->param_link_speed,
1761*087a28d1SDavid Gwynne 	        bgep->param_link_duplex);
17627c478bd9Sstevel@tonic-gate 
17637c478bd9Sstevel@tonic-gate 	return (B_TRUE);
17647c478bd9Sstevel@tonic-gate }
17657c478bd9Sstevel@tonic-gate 
17667c478bd9Sstevel@tonic-gate static const phys_ops_t serdes_ops = {
17677c478bd9Sstevel@tonic-gate 	bge_restart_serdes,
17687c478bd9Sstevel@tonic-gate 	bge_update_serdes,
17697c478bd9Sstevel@tonic-gate 	bge_check_serdes
17707c478bd9Sstevel@tonic-gate };
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate /*
17737c478bd9Sstevel@tonic-gate  * ========== Exported physical layer control routines ==========
17747c478bd9Sstevel@tonic-gate  */
17757c478bd9Sstevel@tonic-gate 
17767c478bd9Sstevel@tonic-gate #undef	BGE_DBG
17777c478bd9Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_PHYS	/* debug flag for this code	*/
17787c478bd9Sstevel@tonic-gate 
17797c478bd9Sstevel@tonic-gate /*
17807c478bd9Sstevel@tonic-gate  * Here we have to determine which media we're using (copper or serdes).
17817c478bd9Sstevel@tonic-gate  * Once that's done, we can initialise the physical layer appropriately.
17827c478bd9Sstevel@tonic-gate  */
178300d0963fSdilpreet int
17847c478bd9Sstevel@tonic-gate bge_phys_init(bge_t *bgep)
17857c478bd9Sstevel@tonic-gate {
1786*087a28d1SDavid Gwynne 	uint32_t regval;
1787*087a28d1SDavid Gwynne 
17887c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_init($%p)", (void *)bgep));
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate 	mutex_enter(bgep->genlock);
17917c478bd9Sstevel@tonic-gate 
17927c478bd9Sstevel@tonic-gate 	/*
17937c478bd9Sstevel@tonic-gate 	 * Probe for the (internal) PHY.  If it's not there, we'll assume
179410843bc4Sly149593 	 * that this is a 5703/4S, with a SerDes interface rather than
179510843bc4Sly149593 	 * a PHY. BCM5714S/BCM5715S are not supported.It are based on
179610843bc4Sly149593 	 * BCM800x PHY.
17977c478bd9Sstevel@tonic-gate 	 */
17987c478bd9Sstevel@tonic-gate 	bgep->phy_mii_addr = 1;
1799*087a28d1SDavid Gwynne 
1800dc3f9a75Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5717_SERIES_CHIPSETS(bgep)) {
1801*087a28d1SDavid Gwynne 		bgep->phy_mii_addr = (bgep->pci_func + 1);
1802dc3f9a75Syong tan - Sun Microsystems - Beijing China 		regval = bge_reg_get32(bgep, SGMII_STATUS_REG);
1803dc3f9a75Syong tan - Sun Microsystems - Beijing China 		if (regval & MEDIA_SELECTION_MODE)
1804*087a28d1SDavid Gwynne 			bgep->phy_mii_addr += 7; /* sgmii */
1805dc3f9a75Syong tan - Sun Microsystems - Beijing China 	}
1806dc3f9a75Syong tan - Sun Microsystems - Beijing China 
18077c478bd9Sstevel@tonic-gate 	if (bge_phy_probe(bgep)) {
18087c478bd9Sstevel@tonic-gate 		bgep->chipid.flags &= ~CHIP_FLAG_SERDES;
18097c478bd9Sstevel@tonic-gate 		bgep->physops = &copper_ops;
18107c478bd9Sstevel@tonic-gate 	} else {
18117c478bd9Sstevel@tonic-gate 		bgep->chipid.flags |= CHIP_FLAG_SERDES;
18127c478bd9Sstevel@tonic-gate 		bgep->physops = &serdes_ops;
18137c478bd9Sstevel@tonic-gate 	}
18147c478bd9Sstevel@tonic-gate 
181500d0963fSdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS) {
18167c478bd9Sstevel@tonic-gate 		mutex_exit(bgep->genlock);
181700d0963fSdilpreet 		return (EIO);
181800d0963fSdilpreet 	}
181900d0963fSdilpreet 	if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK) {
182000d0963fSdilpreet 		mutex_exit(bgep->genlock);
182100d0963fSdilpreet 		return (EIO);
182200d0963fSdilpreet 	}
182300d0963fSdilpreet 	mutex_exit(bgep->genlock);
182400d0963fSdilpreet 	return (0);
18257c478bd9Sstevel@tonic-gate }
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate /*
18287c478bd9Sstevel@tonic-gate  * Reset the physical layer
18297c478bd9Sstevel@tonic-gate  */
18307c478bd9Sstevel@tonic-gate void
18317c478bd9Sstevel@tonic-gate bge_phys_reset(bge_t *bgep)
18327c478bd9Sstevel@tonic-gate {
18337c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_reset($%p)", (void *)bgep));
18347c478bd9Sstevel@tonic-gate 
18357c478bd9Sstevel@tonic-gate 	mutex_enter(bgep->genlock);
183600d0963fSdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS)
183700d0963fSdilpreet 		ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED);
183800d0963fSdilpreet 	if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK)
183900d0963fSdilpreet 		ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED);
18407c478bd9Sstevel@tonic-gate 	mutex_exit(bgep->genlock);
18417c478bd9Sstevel@tonic-gate }
18427c478bd9Sstevel@tonic-gate 
18437c478bd9Sstevel@tonic-gate /*
18447c478bd9Sstevel@tonic-gate  * Reset and power off the physical layer.
18457c478bd9Sstevel@tonic-gate  *
18467c478bd9Sstevel@tonic-gate  * Another RESET should get it back to working, but it may take a few
18477c478bd9Sstevel@tonic-gate  * seconds it may take a few moments to return to normal operation ...
18487c478bd9Sstevel@tonic-gate  */
184900d0963fSdilpreet int
18507c478bd9Sstevel@tonic-gate bge_phys_idle(bge_t *bgep)
18517c478bd9Sstevel@tonic-gate {
18527c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_idle($%p)", (void *)bgep));
18537c478bd9Sstevel@tonic-gate 
18547c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
185500d0963fSdilpreet 	return ((*bgep->physops->phys_restart)(bgep, B_TRUE));
18567c478bd9Sstevel@tonic-gate }
18577c478bd9Sstevel@tonic-gate 
18587c478bd9Sstevel@tonic-gate /*
18597c478bd9Sstevel@tonic-gate  * Synchronise the PHYSICAL layer's speed/duplex/autonegotiation capabilities
18607c478bd9Sstevel@tonic-gate  * and advertisements with the required settings as specified by the various
18617c478bd9Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
18627c478bd9Sstevel@tonic-gate  *
18637c478bd9Sstevel@tonic-gate  * We always reset the PHYSICAL layer and reprogram *all* relevant registers.
18647c478bd9Sstevel@tonic-gate  * This is expected to cause the link to go down, and then back up again once
18657c478bd9Sstevel@tonic-gate  * the link is stable and autonegotiation (if enabled) is complete.  We should
18667c478bd9Sstevel@tonic-gate  * get a link state change interrupt somewhere along the way ...
18677c478bd9Sstevel@tonic-gate  *
18687c478bd9Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
18697c478bd9Sstevel@tonic-gate  */
187000d0963fSdilpreet int
18717c478bd9Sstevel@tonic-gate bge_phys_update(bge_t *bgep)
18727c478bd9Sstevel@tonic-gate {
18737c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_update($%p)", (void *)bgep));
18747c478bd9Sstevel@tonic-gate 
18757c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
187600d0963fSdilpreet 	return ((*bgep->physops->phys_update)(bgep));
18777c478bd9Sstevel@tonic-gate }
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate #undef	BGE_DBG
18807c478bd9Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_LINK	/* debug flag for this code	*/
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate /*
18837c478bd9Sstevel@tonic-gate  * Read the link status and determine whether anything's changed ...
18847c478bd9Sstevel@tonic-gate  *
18857c478bd9Sstevel@tonic-gate  * This routine should be called whenever the chip flags a change
1886f724721bSzh199473  * in the hardware link state.
18877c478bd9Sstevel@tonic-gate  *
18887c478bd9Sstevel@tonic-gate  * This routine returns B_FALSE if the link state has not changed,
18897c478bd9Sstevel@tonic-gate  * returns B_TRUE when the change to the new state should be accepted.
18907c478bd9Sstevel@tonic-gate  * In such a case, the param_* variables give the new hardware state,
18917c478bd9Sstevel@tonic-gate  * which the caller should use to update link_state etc.
18927c478bd9Sstevel@tonic-gate  *
18937c478bd9Sstevel@tonic-gate  * The caller must already hold <genlock>
18947c478bd9Sstevel@tonic-gate  */
18957c478bd9Sstevel@tonic-gate boolean_t
18967c478bd9Sstevel@tonic-gate bge_phys_check(bge_t *bgep)
18977c478bd9Sstevel@tonic-gate {
18987c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_check($%p)", (void *)bgep));
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
19017c478bd9Sstevel@tonic-gate 
1902*087a28d1SDavid Gwynne 	/*
1903*087a28d1SDavid Gwynne 	 * Force a link recheck if current state is unknown.
1904*087a28d1SDavid Gwynne 	 * phys_check() returns TRUE if the link status changed,
1905*087a28d1SDavid Gwynne 	 * FALSE otherwise.
1906*087a28d1SDavid Gwynne 	 */
1907*087a28d1SDavid Gwynne 	return ((*bgep->physops->phys_check)(bgep,
1908*087a28d1SDavid Gwynne 	    (bgep->link_state == LINK_STATE_UNKNOWN)));
19097c478bd9Sstevel@tonic-gate }
1910