xref: /illumos-gate/usr/src/uts/common/io/bge/bge_mii.c (revision 87a49193)
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 /*
23087a28d1SDavid Gwynne  * Copyright (c) 2010-2013, by Broadcom, Inc.
24087a28d1SDavid Gwynne  * All Rights Reserved.
25087a28d1SDavid Gwynne  */
26087a28d1SDavid Gwynne 
27087a28d1SDavid Gwynne /*
28087a28d1SDavid Gwynne  * Copyright (c) 2002, 2010, Oracle and/or its affiliates.
29087a28d1SDavid Gwynne  * All rights reserved.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
32*87a49193SRobert Mustacchi /*
33*87a49193SRobert Mustacchi  * Copyright 2023 Oxide Computer Company
34*87a49193SRobert Mustacchi  */
35*87a49193SRobert Mustacchi 
36f724721bSzh #include "bge_impl.h"
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate /*
397c478bd9Sstevel@tonic-gate  * Bit test macros, returning boolean_t values
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate #define	BIS(w, b)	(((w) & (b)) ? B_TRUE : B_FALSE)
427c478bd9Sstevel@tonic-gate #define	BIC(w, b)	(((w) & (b)) ? B_FALSE : B_TRUE)
437c478bd9Sstevel@tonic-gate #define	UPORDOWN(x)	((x) ? "up" : "down")
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate /*
467c478bd9Sstevel@tonic-gate  * ========== Copper (PHY) support ==========
477c478bd9Sstevel@tonic-gate  */
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_PHY	/* debug flag for this code	*/
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate /*
527c478bd9Sstevel@tonic-gate  * #defines:
537c478bd9Sstevel@tonic-gate  *	BGE_COPPER_WIRESPEED controls whether the Broadcom WireSpeed(tm)
547c478bd9Sstevel@tonic-gate  *	feature is enabled.  We need to recheck whether this can be
557c478bd9Sstevel@tonic-gate  *	enabled; at one time it seemed to interact unpleasantly with the
567c478bd9Sstevel@tonic-gate  *	loopback modes.
577c478bd9Sstevel@tonic-gate  *
587c478bd9Sstevel@tonic-gate  *	BGE_COPPER_IDLEOFF controls whether the (copper) PHY power is
597c478bd9Sstevel@tonic-gate  *	turned off when the PHY is idled i.e. during driver suspend().
607c478bd9Sstevel@tonic-gate  *	For now this is disabled because the chip doesn't seem to
617c478bd9Sstevel@tonic-gate  *	resume cleanly if the PHY power is turned off.
627c478bd9Sstevel@tonic-gate  */
637c478bd9Sstevel@tonic-gate #define	BGE_COPPER_WIRESPEED	B_TRUE
647c478bd9Sstevel@tonic-gate #define	BGE_COPPER_IDLEOFF	B_FALSE
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate /*
677c478bd9Sstevel@tonic-gate  * The arrays below can be indexed by the MODE bits from the Auxiliary
687c478bd9Sstevel@tonic-gate  * Status register to determine the current speed/duplex settings.
697c478bd9Sstevel@tonic-gate  */
707c478bd9Sstevel@tonic-gate static const int16_t bge_copper_link_speed[] = {
717c478bd9Sstevel@tonic-gate 	0,				/* MII_AUX_STATUS_MODE_NONE	*/
727c478bd9Sstevel@tonic-gate 	10,				/* MII_AUX_STATUS_MODE_10_H	*/
737c478bd9Sstevel@tonic-gate 	10,				/* MII_AUX_STATUS_MODE_10_F	*/
747c478bd9Sstevel@tonic-gate 	100,				/* MII_AUX_STATUS_MODE_100_H	*/
757c478bd9Sstevel@tonic-gate 	0,				/* MII_AUX_STATUS_MODE_100_4	*/
767c478bd9Sstevel@tonic-gate 	100,				/* MII_AUX_STATUS_MODE_100_F	*/
777c478bd9Sstevel@tonic-gate 	1000,				/* MII_AUX_STATUS_MODE_1000_H	*/
787c478bd9Sstevel@tonic-gate 	1000				/* MII_AUX_STATUS_MODE_1000_F	*/
797c478bd9Sstevel@tonic-gate };
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate static const int8_t bge_copper_link_duplex[] = {
827c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_NONE	*/
837c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_10_H	*/
847c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_10_F	*/
857c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_100_H	*/
867c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_100_4	*/
877c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_100_F	*/
887c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_1000_H	*/
897c478bd9Sstevel@tonic-gate 	LINK_DUPLEX_FULL		/* MII_AUX_STATUS_MODE_1000_F	*/
907c478bd9Sstevel@tonic-gate };
917c478bd9Sstevel@tonic-gate 
925a506a18Syong tan - Sun Microsystems - Beijing China static const int16_t bge_copper_link_speed_5906[] = {
935a506a18Syong tan - Sun Microsystems - Beijing China 	0,				/* MII_AUX_STATUS_MODE_NONE	*/
945a506a18Syong tan - Sun Microsystems - Beijing China 	10,				/* MII_AUX_STATUS_MODE_10_H	*/
955a506a18Syong tan - Sun Microsystems - Beijing China 	10,				/* MII_AUX_STATUS_MODE_10_F	*/
965a506a18Syong tan - Sun Microsystems - Beijing China 	100,				/* MII_AUX_STATUS_MODE_100_H	*/
975a506a18Syong tan - Sun Microsystems - Beijing China 	0,				/* MII_AUX_STATUS_MODE_100_4	*/
985a506a18Syong tan - Sun Microsystems - Beijing China 	100,				/* MII_AUX_STATUS_MODE_100_F	*/
995a506a18Syong tan - Sun Microsystems - Beijing China 	0,				/* MII_AUX_STATUS_MODE_1000_H	*/
1005a506a18Syong tan - Sun Microsystems - Beijing China 	0				/* MII_AUX_STATUS_MODE_1000_F	*/
1015a506a18Syong tan - Sun Microsystems - Beijing China };
1025a506a18Syong tan - Sun Microsystems - Beijing China 
1035a506a18Syong tan - Sun Microsystems - Beijing China static const int8_t bge_copper_link_duplex_5906[] = {
1045a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_NONE	*/
1055a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_10_H	*/
1065a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_10_F	*/
1075a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_HALF,		/* MII_AUX_STATUS_MODE_100_H	*/
1085a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_100_4	*/
1095a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_FULL,		/* MII_AUX_STATUS_MODE_100_F	*/
1105a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_UNKNOWN,		/* MII_AUX_STATUS_MODE_1000_H	*/
1115a506a18Syong tan - Sun Microsystems - Beijing China 	LINK_DUPLEX_UNKNOWN		/* MII_AUX_STATUS_MODE_1000_F	*/
1125a506a18Syong tan - Sun Microsystems - Beijing China };
1135a506a18Syong tan - Sun Microsystems - Beijing China 
1147c478bd9Sstevel@tonic-gate #if	BGE_DEBUGGING
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate static void
bge_phydump(bge_t * bgep,uint16_t mii_status,uint16_t aux)1177c478bd9Sstevel@tonic-gate bge_phydump(bge_t *bgep, uint16_t mii_status, uint16_t aux)
1187c478bd9Sstevel@tonic-gate {
1197c478bd9Sstevel@tonic-gate 	uint16_t regs[32];
1207c478bd9Sstevel@tonic-gate 	int i;
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	for (i = 0; i < 32; ++i)
1257c478bd9Sstevel@tonic-gate 		switch (i) {
1267c478bd9Sstevel@tonic-gate 		default:
1277c478bd9Sstevel@tonic-gate 			regs[i] = bge_mii_get16(bgep, i);
1287c478bd9Sstevel@tonic-gate 			break;
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 		case MII_STATUS:
1317c478bd9Sstevel@tonic-gate 			regs[i] = mii_status;
1327c478bd9Sstevel@tonic-gate 			break;
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 		case MII_AUX_STATUS:
1357c478bd9Sstevel@tonic-gate 			regs[i] = aux;
1367c478bd9Sstevel@tonic-gate 			break;
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 		case 0x0b: case 0x0c: case 0x0d: case 0x0e:
1397c478bd9Sstevel@tonic-gate 		case 0x15: case 0x16: case 0x17:
1407c478bd9Sstevel@tonic-gate 		case 0x1c:
1417c478bd9Sstevel@tonic-gate 		case 0x1f:
1427c478bd9Sstevel@tonic-gate 			/* reserved registers -- don't read these */
1437c478bd9Sstevel@tonic-gate 			regs[i] = 0;
1447c478bd9Sstevel@tonic-gate 			break;
1457c478bd9Sstevel@tonic-gate 		}
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	for (i = 0; i < 32; i += 8)
1487c478bd9Sstevel@tonic-gate 		BGE_DEBUG(("bge_phydump: "
1490c50e2bcSgh 		    "0x%04x %04x %04x %04x %04x %04x %04x %04x",
1500c50e2bcSgh 		    regs[i+0], regs[i+1], regs[i+2], regs[i+3],
1510c50e2bcSgh 		    regs[i+4], regs[i+5], regs[i+6], regs[i+7]));
1527c478bd9Sstevel@tonic-gate }
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate #endif	/* BGE_DEBUGGING */
1557c478bd9Sstevel@tonic-gate 
156087a28d1SDavid Gwynne static void
bge_phy_toggle_auxctl_smdsp(bge_t * bgep,boolean_t enable)157087a28d1SDavid Gwynne bge_phy_toggle_auxctl_smdsp(bge_t *bgep,
158087a28d1SDavid Gwynne                             boolean_t enable)
159087a28d1SDavid Gwynne {
160087a28d1SDavid Gwynne 	uint16_t val;
161087a28d1SDavid Gwynne 
162087a28d1SDavid Gwynne 	val = bge_mii_get16(bgep, MII_AUX_CONTROL);
163087a28d1SDavid Gwynne 
164087a28d1SDavid Gwynne 	if (enable) {
165087a28d1SDavid Gwynne 		val |= MII_AUX_CTRL_SMDSP_ENA;
166087a28d1SDavid Gwynne 	} else {
167087a28d1SDavid Gwynne 		val &= ~MII_AUX_CTRL_SMDSP_ENA;
168087a28d1SDavid Gwynne 	}
169087a28d1SDavid Gwynne 
170087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_AUX_CONTROL, (val | MII_AUX_CTRL_TX_6DB));
171087a28d1SDavid Gwynne }
172087a28d1SDavid Gwynne 
1737c478bd9Sstevel@tonic-gate /*
1747c478bd9Sstevel@tonic-gate  * Basic low-level function to probe for a PHY
1757c478bd9Sstevel@tonic-gate  *
1767c478bd9Sstevel@tonic-gate  * Returns TRUE if the PHY responds with valid data, FALSE otherwise
1777c478bd9Sstevel@tonic-gate  */
1787c478bd9Sstevel@tonic-gate static boolean_t
bge_phy_probe(bge_t * bgep)1797c478bd9Sstevel@tonic-gate bge_phy_probe(bge_t *bgep)
1807c478bd9Sstevel@tonic-gate {
1810c50e2bcSgh 	uint16_t miicfg;
1820c50e2bcSgh 	uint32_t nicsig, niccfg;
183087a28d1SDavid Gwynne 	int i;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_probe($%p)", (void *)bgep));
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
1887c478bd9Sstevel@tonic-gate 
1890c50e2bcSgh 	nicsig = bge_nic_read32(bgep, BGE_NIC_DATA_SIG_ADDR);
1900c50e2bcSgh 	if (nicsig == BGE_NIC_DATA_SIG) {
1910c50e2bcSgh 		niccfg = bge_nic_read32(bgep, BGE_NIC_DATA_NIC_CFG_ADDR);
1920c50e2bcSgh 		switch (niccfg & BGE_NIC_CFG_PHY_TYPE_MASK) {
1930c50e2bcSgh 		default:
1940c50e2bcSgh 		case BGE_NIC_CFG_PHY_TYPE_COPPER:
1950c50e2bcSgh 			return (B_TRUE);
1960c50e2bcSgh 		case BGE_NIC_CFG_PHY_TYPE_FIBER:
1970c50e2bcSgh 			return (B_FALSE);
1980c50e2bcSgh 		}
1990c50e2bcSgh 	} else {
2000c50e2bcSgh 		/*
2010c50e2bcSgh 		 * Read the MII_STATUS register twice, in
2020c50e2bcSgh 		 * order to clear any sticky bits (but they should
2030c50e2bcSgh 		 * have been cleared by the RESET, I think).
2040c50e2bcSgh 		 */
205087a28d1SDavid Gwynne 		for (i = 0; i < 100; i++) {
206087a28d1SDavid Gwynne 			drv_usecwait(40);
207087a28d1SDavid Gwynne 			miicfg = bge_mii_get16(bgep, MII_STATUS);
208087a28d1SDavid Gwynne 		}
2090c50e2bcSgh 		BGE_DEBUG(("bge_phy_probe: status 0x%x", miicfg));
2107c478bd9Sstevel@tonic-gate 
2110c50e2bcSgh 		/*
2120c50e2bcSgh 		 * Now check the value read; it should have at least one bit set
2130c50e2bcSgh 		 * (for the device capabilities) and at least one clear (one of
2140c50e2bcSgh 		 * the error bits). So if we see all 0s or all 1s, there's a
2150c50e2bcSgh 		 * problem.  In particular, bge_mii_get16() returns all 1s if
2160c50e2bcSgh 		 * communications fails ...
2170c50e2bcSgh 		 */
2180c50e2bcSgh 		switch (miicfg) {
2190c50e2bcSgh 		case 0x0000:
2200c50e2bcSgh 		case 0xffff:
2210c50e2bcSgh 			return (B_FALSE);
2227c478bd9Sstevel@tonic-gate 
223087a28d1SDavid Gwynne 		default:
2240c50e2bcSgh 			return (B_TRUE);
2250c50e2bcSgh 		}
2267c478bd9Sstevel@tonic-gate 	}
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate /*
2307c478bd9Sstevel@tonic-gate  * Basic low-level function to reset the PHY.
2317c478bd9Sstevel@tonic-gate  * Doesn't incorporate any special-case workarounds.
2327c478bd9Sstevel@tonic-gate  *
2337c478bd9Sstevel@tonic-gate  * Returns TRUE on success, FALSE if the RESET bit doesn't clear
2347c478bd9Sstevel@tonic-gate  */
2357c478bd9Sstevel@tonic-gate static boolean_t
bge_phy_reset(bge_t * bgep)2367c478bd9Sstevel@tonic-gate bge_phy_reset(bge_t *bgep)
2377c478bd9Sstevel@tonic-gate {
2387c478bd9Sstevel@tonic-gate 	uint16_t control;
2397c478bd9Sstevel@tonic-gate 	uint_t count;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_reset($%p)", (void *)bgep));
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
2447c478bd9Sstevel@tonic-gate 
2455a506a18Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
2465a506a18Syong tan - Sun Microsystems - Beijing China 		drv_usecwait(40);
2475a506a18Syong tan - Sun Microsystems - Beijing China 		/* put PHY into ready state */
2485a506a18Syong tan - Sun Microsystems - Beijing China 		bge_reg_clr32(bgep, MISC_CONFIG_REG, MISC_CONFIG_EPHY_IDDQ);
2495a506a18Syong tan - Sun Microsystems - Beijing China 		(void) bge_reg_get32(bgep, MISC_CONFIG_REG); /* flush */
2505a506a18Syong tan - Sun Microsystems - Beijing China 		drv_usecwait(40);
2515a506a18Syong tan - Sun Microsystems - Beijing China 	}
2525a506a18Syong tan - Sun Microsystems - Beijing China 
2537c478bd9Sstevel@tonic-gate 	/*
2547c478bd9Sstevel@tonic-gate 	 * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear
2557c478bd9Sstevel@tonic-gate 	 */
2567c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_RESET);
2577c478bd9Sstevel@tonic-gate 	for (count = 0; ++count < 1000; ) {
2587c478bd9Sstevel@tonic-gate 		drv_usecwait(5);
2597c478bd9Sstevel@tonic-gate 		control = bge_mii_get16(bgep, MII_CONTROL);
2607c478bd9Sstevel@tonic-gate 		if (BIC(control, MII_CONTROL_RESET))
2617c478bd9Sstevel@tonic-gate 			return (B_TRUE);
2627c478bd9Sstevel@tonic-gate 	}
2637c478bd9Sstevel@tonic-gate 
2645a506a18Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5906_SERIES_CHIPSETS(bgep))
2652fec4481SCarson Tan 		(void) bge_adj_volt_5906(bgep);
2665a506a18Syong tan - Sun Microsystems - Beijing China 
2677c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_phy_reset: FAILED, control now 0x%x", control));
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	return (B_FALSE);
2707c478bd9Sstevel@tonic-gate }
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate /*
2737c478bd9Sstevel@tonic-gate  * Basic low-level function to powerdown the PHY, if supported
2747c478bd9Sstevel@tonic-gate  * If powerdown support is compiled out, this function does nothing.
2757c478bd9Sstevel@tonic-gate  */
2767c478bd9Sstevel@tonic-gate static void
bge_phy_powerdown(bge_t * bgep)2777c478bd9Sstevel@tonic-gate bge_phy_powerdown(bge_t *bgep)
2787c478bd9Sstevel@tonic-gate {
2797c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phy_powerdown"));
2807c478bd9Sstevel@tonic-gate #if	BGE_COPPER_IDLEOFF
2817c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_PWRDN);
2827c478bd9Sstevel@tonic-gate #endif	/* BGE_COPPER_IDLEOFF */
2837c478bd9Sstevel@tonic-gate }
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate /*
2867c478bd9Sstevel@tonic-gate  * The following functions are based on sample code provided by
2877c478bd9Sstevel@tonic-gate  * Broadcom (20-June-2003), and implement workarounds said to be
2887c478bd9Sstevel@tonic-gate  * required on the early revisions of the BCM5703/4C.
2897c478bd9Sstevel@tonic-gate  *
2907c478bd9Sstevel@tonic-gate  * The registers and values used are mostly UNDOCUMENTED, and
2917c478bd9Sstevel@tonic-gate  * therefore don't have symbolic names ;-(
2927c478bd9Sstevel@tonic-gate  *
2937c478bd9Sstevel@tonic-gate  * Many of the comments are straight out of the Broadcom code:
2947c478bd9Sstevel@tonic-gate  * even where the code has been restructured, the original
2957c478bd9Sstevel@tonic-gate  * comments have been preserved in order to explain what these
2967c478bd9Sstevel@tonic-gate  * undocumented registers & values are all about ...
2977c478bd9Sstevel@tonic-gate  */
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate static void
bge_phy_macro_wait(bge_t * bgep)3007c478bd9Sstevel@tonic-gate bge_phy_macro_wait(bge_t *bgep)
3017c478bd9Sstevel@tonic-gate {
3027c478bd9Sstevel@tonic-gate 	uint_t count;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	for (count = 100; --count; )
3057c478bd9Sstevel@tonic-gate 		if ((bge_mii_get16(bgep, 0x16) & 0x1000) == 0)
3067c478bd9Sstevel@tonic-gate 			break;
3077c478bd9Sstevel@tonic-gate }
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate /*
3107c478bd9Sstevel@tonic-gate  * PHY test data pattern:
3117c478bd9Sstevel@tonic-gate  *
3127c478bd9Sstevel@tonic-gate  * For 5703/04, each DFE TAP has 21-bits (low word 15, hi word 6)
3137c478bd9Sstevel@tonic-gate  * For 5705,    each DFE TAP has 19-bits (low word 15, hi word 4)
3147c478bd9Sstevel@tonic-gate  * For simplicity, we check only 19-bits, so we don't have to
3157c478bd9Sstevel@tonic-gate  * distinguish which chip it is.
3167c478bd9Sstevel@tonic-gate  * the LO word contains 15 bits, make sure pattern data is < 0x7fff
3177c478bd9Sstevel@tonic-gate  * the HI word contains  6 bits, make sure pattern data is < 0x003f
3187c478bd9Sstevel@tonic-gate  */
3197c478bd9Sstevel@tonic-gate #define	N_CHANNELS	4
3207c478bd9Sstevel@tonic-gate #define	N_TAPS		3
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate static struct {
3237c478bd9Sstevel@tonic-gate 	uint16_t	lo;
3247c478bd9Sstevel@tonic-gate 	uint16_t	hi;
3257c478bd9Sstevel@tonic-gate } tap_data[N_CHANNELS][N_TAPS] = {
3267c478bd9Sstevel@tonic-gate 	{
3277c478bd9Sstevel@tonic-gate 		{ 0x5555, 0x0005 },	/* ch0, TAP 0, LO/HI pattern */
3287c478bd9Sstevel@tonic-gate 		{ 0x2aaa, 0x000a },	/* ch0, TAP 1, LO/HI pattern */
3297c478bd9Sstevel@tonic-gate 		{ 0x3456, 0x0003 }	/* ch0, TAP 2, LO/HI pattern */
3307c478bd9Sstevel@tonic-gate 	},
3317c478bd9Sstevel@tonic-gate 	{
3327c478bd9Sstevel@tonic-gate 		{ 0x2aaa, 0x000a },	/* ch1, TAP 0, LO/HI pattern */
3337c478bd9Sstevel@tonic-gate 		{ 0x3333, 0x0003 },	/* ch1, TAP 1, LO/HI pattern */
3347c478bd9Sstevel@tonic-gate 		{ 0x789a, 0x0005 }	/* ch1, TAP 2, LO/HI pattern */
3357c478bd9Sstevel@tonic-gate 	},
3367c478bd9Sstevel@tonic-gate 	{
3377c478bd9Sstevel@tonic-gate 		{ 0x5a5a, 0x0005 },	/* ch2, TAP 0, LO/HI pattern */
3387c478bd9Sstevel@tonic-gate 		{ 0x2a6a, 0x000a },	/* ch2, TAP 1, LO/HI pattern */
3397c478bd9Sstevel@tonic-gate 		{ 0x1bcd, 0x0003 }	/* ch2, TAP 2, LO/HI pattern */
3407c478bd9Sstevel@tonic-gate 	},
3417c478bd9Sstevel@tonic-gate 	{
3427c478bd9Sstevel@tonic-gate 		{ 0x2a5a, 0x000a },	/* ch3, TAP 0, LO/HI pattern */
3437c478bd9Sstevel@tonic-gate 		{ 0x33c3, 0x0003 },	/* ch3, TAP 1, LO/HI pattern */
3447c478bd9Sstevel@tonic-gate 		{ 0x2ef1, 0x0005 }	/* ch3, TAP 2, LO/HI pattern */
3457c478bd9Sstevel@tonic-gate 	}
3467c478bd9Sstevel@tonic-gate };
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate /*
3497c478bd9Sstevel@tonic-gate  * Check whether the PHY has locked up after a RESET.
3507c478bd9Sstevel@tonic-gate  *
3517c478bd9Sstevel@tonic-gate  * Returns TRUE if it did, FALSE is it's OK ;-)
3527c478bd9Sstevel@tonic-gate  */
3537c478bd9Sstevel@tonic-gate static boolean_t
bge_phy_locked_up(bge_t * bgep)3547c478bd9Sstevel@tonic-gate bge_phy_locked_up(bge_t *bgep)
3557c478bd9Sstevel@tonic-gate {
3567c478bd9Sstevel@tonic-gate 	uint16_t dataLo;
3577c478bd9Sstevel@tonic-gate 	uint16_t dataHi;
3587c478bd9Sstevel@tonic-gate 	uint_t chan;
3597c478bd9Sstevel@tonic-gate 	uint_t tap;
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 	/*
3627c478bd9Sstevel@tonic-gate 	 * Check TAPs for all 4 channels, as soon as we see a lockup
3637c478bd9Sstevel@tonic-gate 	 * we'll stop checking.
3647c478bd9Sstevel@tonic-gate 	 */
3657c478bd9Sstevel@tonic-gate 	for (chan = 0; chan < N_CHANNELS; ++chan) {
3667c478bd9Sstevel@tonic-gate 		/* Select channel and set TAP index to 0 */
3677c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
3687c478bd9Sstevel@tonic-gate 		/* Freeze filter again just to be safe */
3697c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0002);
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 		/*
3727c478bd9Sstevel@tonic-gate 		 * Write fixed pattern to the RAM, 3 TAPs for
3737c478bd9Sstevel@tonic-gate 		 * each channel, each TAP have 2 WORDs (LO/HI)
3747c478bd9Sstevel@tonic-gate 		 */
3757c478bd9Sstevel@tonic-gate 		for (tap = 0; tap < N_TAPS; ++tap) {
3767c478bd9Sstevel@tonic-gate 			bge_mii_put16(bgep, 0x15, tap_data[chan][tap].lo);
3777c478bd9Sstevel@tonic-gate 			bge_mii_put16(bgep, 0x15, tap_data[chan][tap].hi);
3787c478bd9Sstevel@tonic-gate 		}
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 		/*
3817c478bd9Sstevel@tonic-gate 		 * Active PHY's Macro operation to write DFE
3827c478bd9Sstevel@tonic-gate 		 * TAP from RAM, and wait for Macro to complete.
3837c478bd9Sstevel@tonic-gate 		 */
3847c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0202);
3857c478bd9Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 		/*
3887c478bd9Sstevel@tonic-gate 		 * Done with write phase, now begin read phase.
3897c478bd9Sstevel@tonic-gate 		 */
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 		/* Select channel and set TAP index to 0 */
3927c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 		/*
3957c478bd9Sstevel@tonic-gate 		 * Active PHY's Macro operation to load DFE
3967c478bd9Sstevel@tonic-gate 		 * TAP to RAM, and wait for Macro to complete
3977c478bd9Sstevel@tonic-gate 		 */
3987c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0082);
3997c478bd9Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 		/* Enable "pre-fetch" */
4027c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x16, 0x0802);
4037c478bd9Sstevel@tonic-gate 		bge_phy_macro_wait(bgep);
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 		/*
4067c478bd9Sstevel@tonic-gate 		 * Read back the TAP values.  3 TAPs for each
4077c478bd9Sstevel@tonic-gate 		 * channel, each TAP have 2 WORDs (LO/HI)
4087c478bd9Sstevel@tonic-gate 		 */
4097c478bd9Sstevel@tonic-gate 		for (tap = 0; tap < N_TAPS; ++tap) {
4107c478bd9Sstevel@tonic-gate 			/*
4117c478bd9Sstevel@tonic-gate 			 * Read Lo/Hi then wait for 'done' is faster.
4127c478bd9Sstevel@tonic-gate 			 * For DFE TAP, the HI word contains 6 bits,
4137c478bd9Sstevel@tonic-gate 			 * LO word contains 15 bits
4147c478bd9Sstevel@tonic-gate 			 */
4157c478bd9Sstevel@tonic-gate 			dataLo = bge_mii_get16(bgep, 0x15) & 0x7fff;
4167c478bd9Sstevel@tonic-gate 			dataHi = bge_mii_get16(bgep, 0x15) & 0x003f;
4177c478bd9Sstevel@tonic-gate 			bge_phy_macro_wait(bgep);
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 			/*
4207c478bd9Sstevel@tonic-gate 			 * Check if what we wrote is what we read back.
4217c478bd9Sstevel@tonic-gate 			 * If failed, then the PHY is locked up, we need
4227c478bd9Sstevel@tonic-gate 			 * to do PHY reset again
4237c478bd9Sstevel@tonic-gate 			 */
4247c478bd9Sstevel@tonic-gate 			if (dataLo != tap_data[chan][tap].lo)
4257c478bd9Sstevel@tonic-gate 				return (B_TRUE);	/* wedged!	*/
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 			if (dataHi != tap_data[chan][tap].hi)
4287c478bd9Sstevel@tonic-gate 				return (B_TRUE);	/* wedged!	*/
4297c478bd9Sstevel@tonic-gate 		}
4307c478bd9Sstevel@tonic-gate 	}
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	/*
4337c478bd9Sstevel@tonic-gate 	 * The PHY isn't locked up ;-)
4347c478bd9Sstevel@tonic-gate 	 */
4357c478bd9Sstevel@tonic-gate 	return (B_FALSE);
4367c478bd9Sstevel@tonic-gate }
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate /*
4397c478bd9Sstevel@tonic-gate  * Special-case code to reset the PHY on the 5702/5703/5704C/5705/5782.
4407c478bd9Sstevel@tonic-gate  * Tries up to 5 times to recover from failure to reset or PHY lockup.
4417c478bd9Sstevel@tonic-gate  *
4427c478bd9Sstevel@tonic-gate  * Returns TRUE on success, FALSE if there's an unrecoverable problem
4437c478bd9Sstevel@tonic-gate  */
4447c478bd9Sstevel@tonic-gate static boolean_t
bge_phy_reset_and_check(bge_t * bgep)4457c478bd9Sstevel@tonic-gate bge_phy_reset_and_check(bge_t *bgep)
4467c478bd9Sstevel@tonic-gate {
4477c478bd9Sstevel@tonic-gate 	boolean_t reset_success;
4487c478bd9Sstevel@tonic-gate 	boolean_t phy_locked;
4497c478bd9Sstevel@tonic-gate 	uint16_t extctrl;
4501eff5f77SRijawanemohammadhusen Nadaf 	uint16_t gigctrl;
4517c478bd9Sstevel@tonic-gate 	uint_t retries;
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	for (retries = 0; retries < 5; ++retries) {
4547c478bd9Sstevel@tonic-gate 		/* Issue a phy reset, and wait for reset to complete */
4557c478bd9Sstevel@tonic-gate 		/* Assuming reset is successful first */
4567c478bd9Sstevel@tonic-gate 		reset_success = bge_phy_reset(bgep);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 		/*
4597c478bd9Sstevel@tonic-gate 		 * Now go check the DFE TAPs to see if locked up, but
4607c478bd9Sstevel@tonic-gate 		 * first, we need to set up PHY so we can read DFE
4617c478bd9Sstevel@tonic-gate 		 * TAPs.
4627c478bd9Sstevel@tonic-gate 		 */
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 		/*
4657c478bd9Sstevel@tonic-gate 		 * Disable Transmitter and Interrupt, while we play
4667c478bd9Sstevel@tonic-gate 		 * with the PHY registers, so the link partner won't
4677c478bd9Sstevel@tonic-gate 		 * see any strange data and the Driver won't see any
4687c478bd9Sstevel@tonic-gate 		 * interrupts.
4697c478bd9Sstevel@tonic-gate 		 */
4707c478bd9Sstevel@tonic-gate 		extctrl = bge_mii_get16(bgep, 0x10);
4717c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x10, extctrl | 0x3000);
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 		/* Setup Full-Duplex, 1000 mbps */
4747c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x0, 0x0140);
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 		/* Set to Master mode */
4771eff5f77SRijawanemohammadhusen Nadaf 		gigctrl = bge_mii_get16(bgep, 0x9);
4787c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x9, 0x1800);
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 		/* Enable SM_DSP_CLOCK & 6dB */
4817c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x18, 0x0c00);	/* "the ADC fix" */
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 		/* Work-arounds */
4847c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x201f);
4857c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x2aaa);
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 		/* More workarounds */
4887c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x000a);
4897c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x0323);	/* "the Gamma fix" */
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 		/* Blocks the PHY control access */
4927c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x17, 0x8005);
4937c478bd9Sstevel@tonic-gate 		bge_mii_put16(bgep, 0x15, 0x0800);
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 		/* Test whether PHY locked up ;-( */
4967c478bd9Sstevel@tonic-gate 		phy_locked = bge_phy_locked_up(bgep);
4977c478bd9Sstevel@tonic-gate 		if (reset_success && !phy_locked)
4987c478bd9Sstevel@tonic-gate 			break;
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate 		/*
5017c478bd9Sstevel@tonic-gate 		 * Some problem here ... log it & retry
5027c478bd9Sstevel@tonic-gate 		 */
5037c478bd9Sstevel@tonic-gate 		if (!reset_success)
5047c478bd9Sstevel@tonic-gate 			BGE_REPORT((bgep, "PHY didn't reset!"));
5057c478bd9Sstevel@tonic-gate 		if (phy_locked)
5067c478bd9Sstevel@tonic-gate 			BGE_REPORT((bgep, "PHY locked up!"));
5077c478bd9Sstevel@tonic-gate 	}
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	/* Remove block phy control */
5107c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x17, 0x8005);
5117c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x15, 0x0000);
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	/* Unfreeze DFE TAP filter for all channels */
5147c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x17, 0x8200);
5157c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x16, 0x0000);
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	/* Restore PHY back to operating state */
5187c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x18, 0x0400);
5197c478bd9Sstevel@tonic-gate 
5201eff5f77SRijawanemohammadhusen Nadaf 	/* Restore 1000BASE-T Control Register */
5211eff5f77SRijawanemohammadhusen Nadaf 	bge_mii_put16(bgep, 0x9, gigctrl);
5221eff5f77SRijawanemohammadhusen Nadaf 
5237c478bd9Sstevel@tonic-gate 	/* Enable transmitter and interrupt */
5247c478bd9Sstevel@tonic-gate 	extctrl = bge_mii_get16(bgep, 0x10);
5257c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x10, extctrl & ~0x3000);
5267c478bd9Sstevel@tonic-gate 
5275a506a18Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5906_SERIES_CHIPSETS(bgep))
5282fec4481SCarson Tan 		(void) bge_adj_volt_5906(bgep);
5295a506a18Syong tan - Sun Microsystems - Beijing China 
53000d0963fSdilpreet 	if (!reset_success)
53100d0963fSdilpreet 		bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
53200d0963fSdilpreet 	else if (phy_locked)
53300d0963fSdilpreet 		bge_fm_ereport(bgep, DDI_FM_DEVICE_INVAL_STATE);
5347c478bd9Sstevel@tonic-gate 	return (reset_success && !phy_locked);
5357c478bd9Sstevel@tonic-gate }
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate static void
bge_phy_tweak_gmii(bge_t * bgep)5387c478bd9Sstevel@tonic-gate bge_phy_tweak_gmii(bge_t *bgep)
5397c478bd9Sstevel@tonic-gate {
5407c478bd9Sstevel@tonic-gate 	/* Tweak GMII timing */
5417c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x1c, 0x8d68);
5427c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, 0x1c, 0x8d68);
5437c478bd9Sstevel@tonic-gate }
5447c478bd9Sstevel@tonic-gate 
545256e438eSzh /* Bit Error Rate reduction fix */
546256e438eSzh static void
bge_phy_bit_err_fix(bge_t * bgep)547256e438eSzh bge_phy_bit_err_fix(bge_t *bgep)
548256e438eSzh {
549256e438eSzh 	bge_mii_put16(bgep, 0x18, 0x0c00);
550256e438eSzh 	bge_mii_put16(bgep, 0x17, 0x000a);
551256e438eSzh 	bge_mii_put16(bgep, 0x15, 0x310b);
552256e438eSzh 	bge_mii_put16(bgep, 0x17, 0x201f);
553256e438eSzh 	bge_mii_put16(bgep, 0x15, 0x9506);
554256e438eSzh 	bge_mii_put16(bgep, 0x17, 0x401f);
555256e438eSzh 	bge_mii_put16(bgep, 0x15, 0x14e2);
556256e438eSzh 	bge_mii_put16(bgep, 0x18, 0x0400);
557256e438eSzh }
558256e438eSzh 
5597c478bd9Sstevel@tonic-gate /*
560087a28d1SDavid Gwynne  * End of Broadcom-derived workaround code
5617c478bd9Sstevel@tonic-gate  */
5627c478bd9Sstevel@tonic-gate 
56300d0963fSdilpreet static int
bge_restart_copper(bge_t * bgep,boolean_t powerdown)5647c478bd9Sstevel@tonic-gate bge_restart_copper(bge_t *bgep, boolean_t powerdown)
5657c478bd9Sstevel@tonic-gate {
5667c478bd9Sstevel@tonic-gate 	uint16_t phy_status;
5677c478bd9Sstevel@tonic-gate 	boolean_t reset_ok;
56835bff3c2Syong tan - Sun Microsystems - Beijing China 	uint16_t extctrl, auxctrl;
569087a28d1SDavid Gwynne 	int i;
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_restart_copper($%p, %d)", (void *)bgep, powerdown));
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
5747c478bd9Sstevel@tonic-gate 
575087a28d1SDavid Gwynne 	switch (MHCR_CHIP_ASIC_REV(bgep)) {
5767c478bd9Sstevel@tonic-gate 	default:
5777c478bd9Sstevel@tonic-gate 		/*
5787c478bd9Sstevel@tonic-gate 		 * Shouldn't happen; it means we don't recognise this chip.
5797c478bd9Sstevel@tonic-gate 		 * It's probably a new one, so we'll try our best anyway ...
5807c478bd9Sstevel@tonic-gate 		 */
5817c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5703:
5827c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5704:
5837c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5705:
584f724721bSzh 	case MHCR_CHIP_ASIC_REV_5752:
5857c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5714:
58610843bc4Sly 	case MHCR_CHIP_ASIC_REV_5715:
5877c478bd9Sstevel@tonic-gate 		reset_ok = bge_phy_reset_and_check(bgep);
5887c478bd9Sstevel@tonic-gate 		break;
5897c478bd9Sstevel@tonic-gate 
5905a506a18Syong tan - Sun Microsystems - Beijing China 	case MHCR_CHIP_ASIC_REV_5906:
5917c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5700:
5927c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_ASIC_REV_5701:
593a2876d03SRobert Mustacchi 	case MHCR_CHIP_ASIC_REV_5723: /* 5717, 5725, 57765 series as well */
5944d6eaea5Syong tan - Sun Microsystems - Beijing China 	case MHCR_CHIP_ASIC_REV_5721_5751:
5957c478bd9Sstevel@tonic-gate 		/*
5967c478bd9Sstevel@tonic-gate 		 * Just a plain reset; the "check" code breaks these chips
5977c478bd9Sstevel@tonic-gate 		 */
5987c478bd9Sstevel@tonic-gate 		reset_ok = bge_phy_reset(bgep);
59900d0963fSdilpreet 		if (!reset_ok)
60000d0963fSdilpreet 			bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
6017c478bd9Sstevel@tonic-gate 		break;
6027c478bd9Sstevel@tonic-gate 	}
60300d0963fSdilpreet 	if (!reset_ok) {
60400d0963fSdilpreet 		BGE_REPORT((bgep, "PHY failed to reset correctly"));
60500d0963fSdilpreet 		return (DDI_FAILURE);
60600d0963fSdilpreet 	}
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 	/*
6097c478bd9Sstevel@tonic-gate 	 * Step 5: disable WOL (not required after RESET)
6107c478bd9Sstevel@tonic-gate 	 *
6117c478bd9Sstevel@tonic-gate 	 * Step 6: refer to errata
6127c478bd9Sstevel@tonic-gate 	 */
6137c478bd9Sstevel@tonic-gate 	switch (bgep->chipid.asic_rev) {
6147c478bd9Sstevel@tonic-gate 	default:
6157c478bd9Sstevel@tonic-gate 		break;
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 	case MHCR_CHIP_REV_5704_A0:
6187c478bd9Sstevel@tonic-gate 		bge_phy_tweak_gmii(bgep);
6197c478bd9Sstevel@tonic-gate 		break;
6207c478bd9Sstevel@tonic-gate 	}
6217c478bd9Sstevel@tonic-gate 
622087a28d1SDavid Gwynne 	switch (MHCR_CHIP_ASIC_REV(bgep)) {
623256e438eSzh 	case MHCR_CHIP_ASIC_REV_5705:
624256e438eSzh 	case MHCR_CHIP_ASIC_REV_5721_5751:
625256e438eSzh 		bge_phy_bit_err_fix(bgep);
626256e438eSzh 		break;
627256e438eSzh 	}
628256e438eSzh 
6296e6ed1baSyong tan - Sun Microsystems - Beijing China 	if (!(bgep->chipid.flags & CHIP_FLAG_NO_JUMBO) &&
6306e6ed1baSyong tan - Sun Microsystems - Beijing China 	    (bgep->chipid.default_mtu > BGE_DEFAULT_MTU)) {
63135bff3c2Syong tan - Sun Microsystems - Beijing China 		/* Set the GMII Fifo Elasticity to high latency */
63235bff3c2Syong tan - Sun Microsystems - Beijing China 		extctrl = bge_mii_get16(bgep, 0x10);
63335bff3c2Syong tan - Sun Microsystems - Beijing China 		bge_mii_put16(bgep, 0x10, extctrl | 0x1);
63435bff3c2Syong tan - Sun Microsystems - Beijing China 
63535bff3c2Syong tan - Sun Microsystems - Beijing China 		/* Allow reception of extended length packets */
63635bff3c2Syong tan - Sun Microsystems - Beijing China 		bge_mii_put16(bgep, MII_AUX_CONTROL, 0x0007);
63735bff3c2Syong tan - Sun Microsystems - Beijing China 		auxctrl = bge_mii_get16(bgep, MII_AUX_CONTROL);
63835bff3c2Syong tan - Sun Microsystems - Beijing China 		auxctrl |= 0x4000;
63935bff3c2Syong tan - Sun Microsystems - Beijing China 		bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl);
64035bff3c2Syong tan - Sun Microsystems - Beijing China 	}
64135bff3c2Syong tan - Sun Microsystems - Beijing China 
6427c478bd9Sstevel@tonic-gate 	/*
6437c478bd9Sstevel@tonic-gate 	 * Step 7: read the MII_INTR_STATUS register twice,
6447c478bd9Sstevel@tonic-gate 	 * in order to clear any sticky bits (but they should
6457c478bd9Sstevel@tonic-gate 	 * have been cleared by the RESET, I think), and we're
6467c478bd9Sstevel@tonic-gate 	 * not using PHY interrupts anyway.
6477c478bd9Sstevel@tonic-gate 	 *
6487c478bd9Sstevel@tonic-gate 	 * Step 8: enable the PHY to interrupt on link status
6497c478bd9Sstevel@tonic-gate 	 * change (not required)
6507c478bd9Sstevel@tonic-gate 	 *
6517c478bd9Sstevel@tonic-gate 	 * Step 9: configure PHY LED Mode - not applicable?
6527c478bd9Sstevel@tonic-gate 	 *
6537c478bd9Sstevel@tonic-gate 	 * Step 10: read the MII_STATUS register twice, in
6547c478bd9Sstevel@tonic-gate 	 * order to clear any sticky bits (but they should
6557c478bd9Sstevel@tonic-gate 	 * have been cleared by the RESET, I think).
6567c478bd9Sstevel@tonic-gate 	 */
657087a28d1SDavid Gwynne 	for (i = 0; i < 100; i++) {
658087a28d1SDavid Gwynne 		drv_usecwait(40);
659087a28d1SDavid Gwynne 		phy_status = bge_mii_get16(bgep, MII_STATUS);
660087a28d1SDavid Gwynne 	}
6617c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_restart_copper: status 0x%x", phy_status));
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	/*
6647c478bd9Sstevel@tonic-gate 	 * Finally, shut down the PHY, if required
6657c478bd9Sstevel@tonic-gate 	 */
6667c478bd9Sstevel@tonic-gate 	if (powerdown)
6677c478bd9Sstevel@tonic-gate 		bge_phy_powerdown(bgep);
66800d0963fSdilpreet 	return (DDI_SUCCESS);
6697c478bd9Sstevel@tonic-gate }
6707c478bd9Sstevel@tonic-gate 
671087a28d1SDavid Gwynne boolean_t
bge_eee_cap(bge_t * bgep)672087a28d1SDavid Gwynne bge_eee_cap(bge_t * bgep)
673087a28d1SDavid Gwynne {
674087a28d1SDavid Gwynne 	if (!(DEVICE_5717_SERIES_CHIPSETS(bgep) ||
675087a28d1SDavid Gwynne 	    DEVICE_5725_SERIES_CHIPSETS(bgep))) {
676087a28d1SDavid Gwynne 		/* EEE is not supported on this chip */
677087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_eee: eee not supported (device 0x%x)",
678087a28d1SDavid Gwynne 		    bgep->chipid.device));
679087a28d1SDavid Gwynne 		return (B_FALSE);
680087a28d1SDavid Gwynne 	}
681087a28d1SDavid Gwynne 
682087a28d1SDavid Gwynne 	switch (CHIP_ASIC_REV_PROD_ID(bgep)) {
683087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5717_B0: /* = CHIP_ASIC_REV_5718_B0 */
684087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5717_C0:
685087a28d1SDavid Gwynne 	/* case CHIP_ASIC_REV_5718_B0: */
686087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5719_A0:
687087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5719_A1:
688087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5720_A0:
689087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5725_A0:
690087a28d1SDavid Gwynne 	case CHIP_ASIC_REV_5727_B0:
691087a28d1SDavid Gwynne 		return (B_TRUE);
692087a28d1SDavid Gwynne 
693087a28d1SDavid Gwynne 	default:
694087a28d1SDavid Gwynne 		/* EEE is not supported on this asic rev */
695087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_eee: eee not supported (asic rev 0x%08x)",
696087a28d1SDavid Gwynne 		    bgep->chipid.asic_rev));
697087a28d1SDavid Gwynne 		return (B_FALSE);
698087a28d1SDavid Gwynne 	}
699087a28d1SDavid Gwynne }
700087a28d1SDavid Gwynne 
701087a28d1SDavid Gwynne void
bge_eee_init(bge_t * bgep)702087a28d1SDavid Gwynne bge_eee_init(bge_t * bgep)
703087a28d1SDavid Gwynne {
704087a28d1SDavid Gwynne 	uint32_t val;
705087a28d1SDavid Gwynne 
706087a28d1SDavid Gwynne 	BGE_TRACE(("bge_eee_init($%p)", (void *)bgep));
707087a28d1SDavid Gwynne 
708087a28d1SDavid Gwynne 	ASSERT(mutex_owned(bgep->genlock));
709087a28d1SDavid Gwynne 
710087a28d1SDavid Gwynne 	if (!bge_eee_cap(bgep)) {
711087a28d1SDavid Gwynne 		return;
712087a28d1SDavid Gwynne 	}
713087a28d1SDavid Gwynne 
714087a28d1SDavid Gwynne 	/* Enable MAC control of LPI */
715087a28d1SDavid Gwynne 
716087a28d1SDavid Gwynne 	val = (EEE_LINK_IDLE_PCIE_NL0 | EEE_LINK_IDLE_UART_IDL);
717087a28d1SDavid Gwynne 	if (DEVICE_5725_SERIES_CHIPSETS(bgep))
718087a28d1SDavid Gwynne 		val |= EEE_LINK_IDLE_APE_TX_MT;
719087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_LINK_IDLE_CONTROL_REG, val);
720087a28d1SDavid Gwynne 
721087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_CONTROL_REG, EEE_CONTROL_EXIT_20_1_US);
722087a28d1SDavid Gwynne 
723087a28d1SDavid Gwynne 	val = EEE_MODE_ERLY_L1_XIT_DET | EEE_MODE_LPI_IN_TX |
724087a28d1SDavid Gwynne 	    EEE_MODE_LPI_IN_RX | EEE_MODE_EEE_ENABLE;
725087a28d1SDavid Gwynne 
726087a28d1SDavid Gwynne 	if (bgep->chipid.device != DEVICE_ID_5717)
727087a28d1SDavid Gwynne 		val |= EEE_MODE_SND_IDX_DET_EN;
728087a28d1SDavid Gwynne 
729087a28d1SDavid Gwynne 	//val |= EEE_MODE_APE_TX_DET_EN;
730087a28d1SDavid Gwynne 
731087a28d1SDavid Gwynne 	if (!bgep->chipid.eee) {
732087a28d1SDavid Gwynne 		val = 0;
733087a28d1SDavid Gwynne 	}
734087a28d1SDavid Gwynne 
735087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_MODE_REG, val);
736087a28d1SDavid Gwynne 
737087a28d1SDavid Gwynne 	/* Set EEE timer debounce values */
738087a28d1SDavid Gwynne 
739087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_DEBOUNCE_T1_CONTROL_REG,
740087a28d1SDavid Gwynne 	    EEE_DEBOUNCE_T1_PCIEXIT_2047US | EEE_DEBOUNCE_T1_LNKIDLE_2047US);
741087a28d1SDavid Gwynne 
742087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_DEBOUNCE_T2_CONTROL_REG,
743087a28d1SDavid Gwynne 	    EEE_DEBOUNCE_T2_APE_TX_2047US | EEE_DEBOUNCE_T2_TXIDXEQ_2047US);
744087a28d1SDavid Gwynne }
745087a28d1SDavid Gwynne 
746087a28d1SDavid Gwynne void
bge_eee_autoneg(bge_t * bgep,boolean_t adv_100fdx,boolean_t adv_1000fdx)747087a28d1SDavid Gwynne bge_eee_autoneg(bge_t * bgep, boolean_t adv_100fdx, boolean_t adv_1000fdx)
748087a28d1SDavid Gwynne {
749087a28d1SDavid Gwynne 	uint32_t val;
750087a28d1SDavid Gwynne 	uint16_t mii_val;
751087a28d1SDavid Gwynne 
752087a28d1SDavid Gwynne 	BGE_TRACE(("bge_eee_autoneg($%p)", (void *)bgep));
753087a28d1SDavid Gwynne 
754087a28d1SDavid Gwynne 	ASSERT(mutex_owned(bgep->genlock));
755087a28d1SDavid Gwynne 
756087a28d1SDavid Gwynne 	if (!bge_eee_cap(bgep)) {
757087a28d1SDavid Gwynne 		return;
758087a28d1SDavid Gwynne 	}
759087a28d1SDavid Gwynne 
760087a28d1SDavid Gwynne 	/* Disable LPI Requests */
761087a28d1SDavid Gwynne 	val = bge_reg_get32(bgep, EEE_MODE_REG);
762087a28d1SDavid Gwynne 	val &= ~EEE_MODE_LPI_ENABLE;
763087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_MODE_REG, val);
764087a28d1SDavid Gwynne 
765087a28d1SDavid Gwynne 	bge_phy_toggle_auxctl_smdsp(bgep, B_TRUE);
766087a28d1SDavid Gwynne 
767087a28d1SDavid Gwynne 	mii_val = 0;
768087a28d1SDavid Gwynne 
769087a28d1SDavid Gwynne 	if (bgep->chipid.eee) {
770087a28d1SDavid Gwynne 		if (adv_100fdx) {
771087a28d1SDavid Gwynne 			mii_val |= EEE_CL45_D7_RESULT_STAT_LP_100TX;
772087a28d1SDavid Gwynne 		}
773087a28d1SDavid Gwynne 		if (adv_1000fdx) {
774087a28d1SDavid Gwynne 			mii_val |= EEE_CL45_D7_RESULT_STAT_LP_1000T;
775087a28d1SDavid Gwynne 		}
776087a28d1SDavid Gwynne 	}
777087a28d1SDavid Gwynne 
778087a28d1SDavid Gwynne 	/* Enable EEE advertisement for the specified mode(s)... */
779087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_MMD_CTRL, MDIO_MMD_AN);
780087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_MMD_ADDRESS_DATA, MDIO_AN_EEE_ADV);
781087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_MMD_CTRL,
782087a28d1SDavid Gwynne 	    MII_MMD_CTRL_DATA_NOINC | MDIO_MMD_AN);
783087a28d1SDavid Gwynne 	bge_mii_put16(bgep, MII_MMD_ADDRESS_DATA, mii_val);
784087a28d1SDavid Gwynne 
785087a28d1SDavid Gwynne 	/* Setup PHY DSP for EEE */
786087a28d1SDavid Gwynne 	switch (bgep->chipid.device) {
787087a28d1SDavid Gwynne 	case DEVICE_ID_5717:
788087a28d1SDavid Gwynne 	case DEVICE_ID_5718:
789087a28d1SDavid Gwynne 	case DEVICE_ID_5719:
790087a28d1SDavid Gwynne 		/* If we advertised any EEE advertisements above... */
791087a28d1SDavid Gwynne 		if (mii_val) {
792087a28d1SDavid Gwynne 			mii_val = (MII_DSP_TAP26_ALNOKO |
793087a28d1SDavid Gwynne 			    MII_DSP_TAP26_RMRXSTO |
794087a28d1SDavid Gwynne 			    MII_DSP_TAP26_OPCSINPT);
795087a28d1SDavid Gwynne 		}
796087a28d1SDavid Gwynne 		bge_phydsp_write(bgep, MII_DSP_TAP26, mii_val);
797087a28d1SDavid Gwynne 		/* fall through */
798087a28d1SDavid Gwynne 	case DEVICE_ID_5720:
799087a28d1SDavid Gwynne 	case DEVICE_ID_5725:
800087a28d1SDavid Gwynne 	case DEVICE_ID_5727:
801087a28d1SDavid Gwynne 		mii_val = bge_phydsp_read(bgep, MII_DSP_CH34TP2);
802087a28d1SDavid Gwynne 		bge_phydsp_write(bgep, MII_DSP_CH34TP2,
803087a28d1SDavid Gwynne 		    (mii_val | MII_DSP_CH34TP2_HIBW01));
804087a28d1SDavid Gwynne 	}
805087a28d1SDavid Gwynne 
806087a28d1SDavid Gwynne 	bge_phy_toggle_auxctl_smdsp(bgep, B_FALSE);
807087a28d1SDavid Gwynne }
808087a28d1SDavid Gwynne 
809087a28d1SDavid Gwynne void
bge_eee_adjust(bge_t * bgep)810087a28d1SDavid Gwynne bge_eee_adjust(bge_t * bgep)
811087a28d1SDavid Gwynne {
812087a28d1SDavid Gwynne 	uint32_t val;
813087a28d1SDavid Gwynne 	uint16_t mii_val;
814087a28d1SDavid Gwynne 
815087a28d1SDavid Gwynne 	BGE_TRACE(("bge_eee_adjust($%p, %d)", (void *)bgep));
816087a28d1SDavid Gwynne 
817087a28d1SDavid Gwynne 	ASSERT(mutex_owned(bgep->genlock));
818087a28d1SDavid Gwynne 
819087a28d1SDavid Gwynne 	if (!bge_eee_cap(bgep)) {
820087a28d1SDavid Gwynne 		return;
821087a28d1SDavid Gwynne 	}
822087a28d1SDavid Gwynne 
823087a28d1SDavid Gwynne 	bgep->eee_lpi_wait = 0;
824087a28d1SDavid Gwynne 
825087a28d1SDavid Gwynne 	/* Check for PHY link status */
826087a28d1SDavid Gwynne 	if (bgep->param_link_up) {
827087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_eee_adjust: link status up"));
828087a28d1SDavid Gwynne 
829087a28d1SDavid Gwynne 		/*
830087a28d1SDavid Gwynne 		 * XXX if duplex full and speed is 1000 or 100 then do the
831087a28d1SDavid Gwynne 		 * following...
832087a28d1SDavid Gwynne 		 */
833087a28d1SDavid Gwynne 
834087a28d1SDavid Gwynne 		if (bgep->param_link_speed == 1000) {
835087a28d1SDavid Gwynne 			BGE_DEBUG(("bge_eee_adjust: eee timing for 1000Mb"));
836087a28d1SDavid Gwynne 			bge_reg_put32(bgep, EEE_CONTROL_REG,
837087a28d1SDavid Gwynne 			    EEE_CONTROL_EXIT_16_5_US);
838087a28d1SDavid Gwynne 		} else if (bgep->param_link_speed == 100) {
839087a28d1SDavid Gwynne 			BGE_DEBUG(("bge_eee_adjust: eee timing for 100Mb"));
840087a28d1SDavid Gwynne 			bge_reg_put32(bgep, EEE_CONTROL_REG,
841087a28d1SDavid Gwynne 			    EEE_CONTROL_EXIT_36_US);
842087a28d1SDavid Gwynne 		}
843087a28d1SDavid Gwynne 
844087a28d1SDavid Gwynne 		/* Read PHY's EEE negotiation status */
845087a28d1SDavid Gwynne 		bge_mii_put16(bgep, MII_MMD_CTRL, MDIO_MMD_AN);
846087a28d1SDavid Gwynne 		bge_mii_put16(bgep, MII_MMD_ADDRESS_DATA,
847087a28d1SDavid Gwynne 		    EEE_CL45_D7_RESULT_STAT);
848087a28d1SDavid Gwynne 		bge_mii_put16(bgep, MII_MMD_CTRL,
849087a28d1SDavid Gwynne 		    MII_MMD_CTRL_DATA_NOINC | MDIO_MMD_AN);
850087a28d1SDavid Gwynne 		mii_val = bge_mii_get16(bgep, MII_MMD_ADDRESS_DATA);
851087a28d1SDavid Gwynne 
852087a28d1SDavid Gwynne 		/* Enable EEE LPI request if EEE negotiated */
853087a28d1SDavid Gwynne 		if ((mii_val == EEE_CL45_D7_RESULT_STAT_LP_1000T) ||
854087a28d1SDavid Gwynne 		    (mii_val == EEE_CL45_D7_RESULT_STAT_LP_100TX)) {
855087a28d1SDavid Gwynne 			BGE_DEBUG(("bge_eee_adjust: eee negotiaton success, lpi scheduled"));
856087a28d1SDavid Gwynne 			bgep->eee_lpi_wait = 2;
857087a28d1SDavid Gwynne 		} else {
858087a28d1SDavid Gwynne 			BGE_DEBUG(("bge_eee_adjust: eee negotiation failed"));
859087a28d1SDavid Gwynne 		}
860087a28d1SDavid Gwynne 	} else {
861087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_eee_adjust: link status down"));
862087a28d1SDavid Gwynne 	}
863087a28d1SDavid Gwynne 
864087a28d1SDavid Gwynne 	if (!bgep->eee_lpi_wait) {
865087a28d1SDavid Gwynne 		if (bgep->param_link_up) {
866087a28d1SDavid Gwynne 			bge_phy_toggle_auxctl_smdsp(bgep, B_TRUE);
867087a28d1SDavid Gwynne 			bge_phydsp_write(bgep, MII_DSP_TAP26, 0);
868087a28d1SDavid Gwynne 			bge_phy_toggle_auxctl_smdsp(bgep, B_FALSE);
869087a28d1SDavid Gwynne 		}
870087a28d1SDavid Gwynne 
871087a28d1SDavid Gwynne 		/* Disable LPI requests */
872087a28d1SDavid Gwynne 		val = bge_reg_get32(bgep, EEE_MODE_REG);
873087a28d1SDavid Gwynne 		val &= ~EEE_MODE_LPI_ENABLE;
874087a28d1SDavid Gwynne 		bge_reg_put32(bgep, EEE_MODE_REG, val);
875087a28d1SDavid Gwynne 	}
876087a28d1SDavid Gwynne }
877087a28d1SDavid Gwynne 
878087a28d1SDavid Gwynne void
bge_eee_enable(bge_t * bgep)879087a28d1SDavid Gwynne bge_eee_enable(bge_t * bgep)
880087a28d1SDavid Gwynne {
881087a28d1SDavid Gwynne 	uint32_t val;
882087a28d1SDavid Gwynne 
883087a28d1SDavid Gwynne 	/* XXX check for EEE for 5717 family... */
884087a28d1SDavid Gwynne 
885087a28d1SDavid Gwynne 	if (bgep->param_link_speed == 1000) {
886087a28d1SDavid Gwynne 		bge_phy_toggle_auxctl_smdsp(bgep, B_TRUE);
887087a28d1SDavid Gwynne 		bge_phydsp_write(bgep, MII_DSP_TAP26,
888087a28d1SDavid Gwynne 		    MII_DSP_TAP26_ALNOKO | MII_DSP_TAP26_RMRXSTO);
889087a28d1SDavid Gwynne 		bge_phy_toggle_auxctl_smdsp(bgep, B_FALSE);
890087a28d1SDavid Gwynne 	}
891087a28d1SDavid Gwynne 
892087a28d1SDavid Gwynne 	val = bge_reg_get32(bgep, EEE_MODE_REG);
893087a28d1SDavid Gwynne 	val |= EEE_MODE_LPI_ENABLE;
894087a28d1SDavid Gwynne 	bge_reg_put32(bgep, EEE_MODE_REG, val);
895087a28d1SDavid Gwynne }
896087a28d1SDavid Gwynne 
8977c478bd9Sstevel@tonic-gate /*
8987c478bd9Sstevel@tonic-gate  * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities
8997c478bd9Sstevel@tonic-gate  * and advertisements with the required settings as specified by the various
9007c478bd9Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
9017c478bd9Sstevel@tonic-gate  *
9027c478bd9Sstevel@tonic-gate  * We always reset the PHY and reprogram *all* the relevant registers,
9037c478bd9Sstevel@tonic-gate  * not just those changed.  This should cause the link to go down, and then
9047c478bd9Sstevel@tonic-gate  * back up again once the link is stable and autonegotiation (if enabled)
9057c478bd9Sstevel@tonic-gate  * is complete.  We should get a link state change interrupt somewhere along
9067c478bd9Sstevel@tonic-gate  * the way ...
9077c478bd9Sstevel@tonic-gate  *
9087c478bd9Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
9097c478bd9Sstevel@tonic-gate  */
91000d0963fSdilpreet static int
bge_update_copper(bge_t * bgep)9117c478bd9Sstevel@tonic-gate bge_update_copper(bge_t *bgep)
9127c478bd9Sstevel@tonic-gate {
9137c478bd9Sstevel@tonic-gate 	boolean_t adv_autoneg;
9147c478bd9Sstevel@tonic-gate 	boolean_t adv_pause;
9157c478bd9Sstevel@tonic-gate 	boolean_t adv_asym_pause;
9167c478bd9Sstevel@tonic-gate 	boolean_t adv_1000fdx;
9177c478bd9Sstevel@tonic-gate 	boolean_t adv_1000hdx;
9187c478bd9Sstevel@tonic-gate 	boolean_t adv_100fdx;
9197c478bd9Sstevel@tonic-gate 	boolean_t adv_100hdx;
9207c478bd9Sstevel@tonic-gate 	boolean_t adv_10fdx;
9217c478bd9Sstevel@tonic-gate 	boolean_t adv_10hdx;
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	uint16_t control;
9247c478bd9Sstevel@tonic-gate 	uint16_t gigctrl;
9257c478bd9Sstevel@tonic-gate 	uint16_t auxctrl;
9267c478bd9Sstevel@tonic-gate 	uint16_t anar;
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_update_copper($%p)", (void *)bgep));
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: autoneg %d "
9330c50e2bcSgh 	    "pause %d asym_pause %d "
9340c50e2bcSgh 	    "1000fdx %d 1000hdx %d "
9350c50e2bcSgh 	    "100fdx %d 100hdx %d "
9360c50e2bcSgh 	    "10fdx %d 10hdx %d ",
9370c50e2bcSgh 	    bgep->param_adv_autoneg,
9380c50e2bcSgh 	    bgep->param_adv_pause, bgep->param_adv_asym_pause,
9390c50e2bcSgh 	    bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
9400c50e2bcSgh 	    bgep->param_adv_100fdx, bgep->param_adv_100hdx,
9410c50e2bcSgh 	    bgep->param_adv_10fdx, bgep->param_adv_10hdx));
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 	control = gigctrl = auxctrl = anar = 0;
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 	/*
9467c478bd9Sstevel@tonic-gate 	 * PHY settings are normally based on the param_* variables,
9477c478bd9Sstevel@tonic-gate 	 * but if any loopback mode is in effect, that takes precedence.
9487c478bd9Sstevel@tonic-gate 	 *
9497c478bd9Sstevel@tonic-gate 	 * BGE supports MAC-internal loopback, PHY-internal loopback,
9507c478bd9Sstevel@tonic-gate 	 * and External loopback at a variety of speeds (with a special
9517c478bd9Sstevel@tonic-gate 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
9527c478bd9Sstevel@tonic-gate 	 * is turned ON, and the speed/mastership is forced.
9537c478bd9Sstevel@tonic-gate 	 */
9547c478bd9Sstevel@tonic-gate 	switch (bgep->param_loop_mode) {
9557c478bd9Sstevel@tonic-gate 	case BGE_LOOP_NONE:
9567c478bd9Sstevel@tonic-gate 	default:
9577c478bd9Sstevel@tonic-gate 		adv_autoneg = bgep->param_adv_autoneg;
9587c478bd9Sstevel@tonic-gate 		adv_pause = bgep->param_adv_pause;
9597c478bd9Sstevel@tonic-gate 		adv_asym_pause = bgep->param_adv_asym_pause;
9607c478bd9Sstevel@tonic-gate 		adv_1000fdx = bgep->param_adv_1000fdx;
9617c478bd9Sstevel@tonic-gate 		adv_1000hdx = bgep->param_adv_1000hdx;
9627c478bd9Sstevel@tonic-gate 		adv_100fdx = bgep->param_adv_100fdx;
9637c478bd9Sstevel@tonic-gate 		adv_100hdx = bgep->param_adv_100hdx;
9647c478bd9Sstevel@tonic-gate 		adv_10fdx = bgep->param_adv_10fdx;
9657c478bd9Sstevel@tonic-gate 		adv_10hdx = bgep->param_adv_10hdx;
9667c478bd9Sstevel@tonic-gate 		break;
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_1000:
9697c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_100:
9707c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_10:
9717c478bd9Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_PHY:
9727c478bd9Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_MAC:
9737c478bd9Sstevel@tonic-gate 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
9747c478bd9Sstevel@tonic-gate 		adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE;
9757c478bd9Sstevel@tonic-gate 		adv_1000hdx = adv_100hdx = adv_10hdx = B_FALSE;
9767c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_FULL;
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 		switch (bgep->param_loop_mode) {
9797c478bd9Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_1000:
9807c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
9817c478bd9Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
9827c478bd9Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
983bdb9230aSGarrett D'Amore 			gigctrl |= MII_MSCONTROL_MANUAL;
984bdb9230aSGarrett D'Amore 			gigctrl |= MII_MSCONTROL_MASTER;
9857c478bd9Sstevel@tonic-gate 			break;
9867c478bd9Sstevel@tonic-gate 
9877c478bd9Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_100:
9887c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 100;
9897c478bd9Sstevel@tonic-gate 			adv_100fdx = B_TRUE;
9907c478bd9Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
9917c478bd9Sstevel@tonic-gate 			break;
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 		case BGE_LOOP_EXTERNAL_10:
9947c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 10;
9957c478bd9Sstevel@tonic-gate 			adv_10fdx = B_TRUE;
9967c478bd9Sstevel@tonic-gate 			auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
9977c478bd9Sstevel@tonic-gate 			break;
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 		case BGE_LOOP_INTERNAL_PHY:
10007c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
10017c478bd9Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
10027c478bd9Sstevel@tonic-gate 			control = MII_CONTROL_LOOPBACK;
10037c478bd9Sstevel@tonic-gate 			break;
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate 		case BGE_LOOP_INTERNAL_MAC:
10067c478bd9Sstevel@tonic-gate 			bgep->param_link_speed = 1000;
10077c478bd9Sstevel@tonic-gate 			adv_1000fdx = B_TRUE;
10087c478bd9Sstevel@tonic-gate 			break;
10097c478bd9Sstevel@tonic-gate 		}
10107c478bd9Sstevel@tonic-gate 	}
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: autoneg %d "
10130c50e2bcSgh 	    "pause %d asym_pause %d "
10140c50e2bcSgh 	    "1000fdx %d 1000hdx %d "
10150c50e2bcSgh 	    "100fdx %d 100hdx %d "
10160c50e2bcSgh 	    "10fdx %d 10hdx %d ",
10170c50e2bcSgh 	    adv_autoneg,
10180c50e2bcSgh 	    adv_pause, adv_asym_pause,
10190c50e2bcSgh 	    adv_1000fdx, adv_1000hdx,
10200c50e2bcSgh 	    adv_100fdx, adv_100hdx,
10210c50e2bcSgh 	    adv_10fdx, adv_10hdx));
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 	/*
10247c478bd9Sstevel@tonic-gate 	 * We should have at least one technology capability set;
10257c478bd9Sstevel@tonic-gate 	 * if not, we select a default of 1000Mb/s full-duplex
10267c478bd9Sstevel@tonic-gate 	 */
10277c478bd9Sstevel@tonic-gate 	if (!adv_1000fdx && !adv_100fdx && !adv_10fdx &&
10287c478bd9Sstevel@tonic-gate 	    !adv_1000hdx && !adv_100hdx && !adv_10hdx)
10297c478bd9Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate 	/*
10327c478bd9Sstevel@tonic-gate 	 * Now transform the adv_* variables into the proper settings
10337c478bd9Sstevel@tonic-gate 	 * of the PHY registers ...
10347c478bd9Sstevel@tonic-gate 	 *
10357c478bd9Sstevel@tonic-gate 	 * If autonegotiation is (now) enabled, we want to trigger
10367c478bd9Sstevel@tonic-gate 	 * a new autonegotiation cycle once the PHY has been
10377c478bd9Sstevel@tonic-gate 	 * programmed with the capabilities to be advertised.
10387c478bd9Sstevel@tonic-gate 	 */
10397c478bd9Sstevel@tonic-gate 	if (adv_autoneg)
10407c478bd9Sstevel@tonic-gate 		control |= MII_CONTROL_ANE|MII_CONTROL_RSAN;
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate 	if (adv_1000fdx)
1043bdb9230aSGarrett D'Amore 		control |= MII_CONTROL_1GB|MII_CONTROL_FDUPLEX;
10447c478bd9Sstevel@tonic-gate 	else if (adv_1000hdx)
1045bdb9230aSGarrett D'Amore 		control |= MII_CONTROL_1GB;
10467c478bd9Sstevel@tonic-gate 	else if (adv_100fdx)
10477c478bd9Sstevel@tonic-gate 		control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX;
10487c478bd9Sstevel@tonic-gate 	else if (adv_100hdx)
10497c478bd9Sstevel@tonic-gate 		control |= MII_CONTROL_100MB;
10507c478bd9Sstevel@tonic-gate 	else if (adv_10fdx)
10517c478bd9Sstevel@tonic-gate 		control |= MII_CONTROL_FDUPLEX;
10527c478bd9Sstevel@tonic-gate 	else if (adv_10hdx)
10537c478bd9Sstevel@tonic-gate 		control |= 0;
10547c478bd9Sstevel@tonic-gate 	else
10557c478bd9Sstevel@tonic-gate 		{ _NOTE(EMPTY); }	/* Can't get here anyway ...	*/
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 	if (adv_1000fdx)
1058bdb9230aSGarrett D'Amore 		gigctrl |= MII_MSCONTROL_1000T_FD;
10597c478bd9Sstevel@tonic-gate 	if (adv_1000hdx)
1060bdb9230aSGarrett D'Amore 		gigctrl |= MII_MSCONTROL_1000T;
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	if (adv_100fdx)
10637c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_100BASE_TX_FD;
10647c478bd9Sstevel@tonic-gate 	if (adv_100hdx)
10657c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_100BASE_TX;
10667c478bd9Sstevel@tonic-gate 	if (adv_10fdx)
10677c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_10BASE_T_FD;
10687c478bd9Sstevel@tonic-gate 	if (adv_10hdx)
10697c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_10BASE_T;
10707c478bd9Sstevel@tonic-gate 
10717c478bd9Sstevel@tonic-gate 	if (adv_pause)
10727c478bd9Sstevel@tonic-gate 		anar |= MII_ABILITY_PAUSE;
10737c478bd9Sstevel@tonic-gate 	if (adv_asym_pause)
1074bdb9230aSGarrett D'Amore 		anar |= MII_ABILITY_ASMPAUSE;
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 	/*
10777c478bd9Sstevel@tonic-gate 	 * Munge in any other fixed bits we require ...
10787c478bd9Sstevel@tonic-gate 	 */
10797c478bd9Sstevel@tonic-gate 	anar |= MII_AN_SELECTOR_8023;
10807c478bd9Sstevel@tonic-gate 	auxctrl |= MII_AUX_CTRL_NORM_TX_MODE;
10817c478bd9Sstevel@tonic-gate 	auxctrl |= MII_AUX_CTRL_NORMAL;
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate 	/*
10847c478bd9Sstevel@tonic-gate 	 * Restart the PHY and write the new values.  Note the
10857c478bd9Sstevel@tonic-gate 	 * time, so that we can say whether subsequent link state
10867c478bd9Sstevel@tonic-gate 	 * changes can be attributed to our reprogramming the PHY
10877c478bd9Sstevel@tonic-gate 	 */
108800d0963fSdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) == DDI_FAILURE)
108900d0963fSdilpreet 		return (DDI_FAILURE);
10907c478bd9Sstevel@tonic-gate 	bge_mii_put16(bgep, MII_AN_ADVERT, anar);
109135bff3c2Syong tan - Sun Microsystems - Beijing China 	if (auxctrl & MII_AUX_CTRL_NORM_EXT_LOOPBACK)
109235bff3c2Syong tan - Sun Microsystems - Beijing China 		bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl);
1093bdb9230aSGarrett D'Amore 	bge_mii_put16(bgep, MII_MSCONTROL, gigctrl);
10941eff5f77SRijawanemohammadhusen Nadaf 	bge_mii_put16(bgep, MII_CONTROL, control);
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: anar <- 0x%x", anar));
10977c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: auxctrl <- 0x%x", auxctrl));
10987c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_copper: gigctrl <- 0x%x", gigctrl));
10991eff5f77SRijawanemohammadhusen Nadaf 	BGE_DEBUG(("bge_update_copper: control <- 0x%x", control));
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate #if	BGE_COPPER_WIRESPEED
11027c478bd9Sstevel@tonic-gate 	/*
11037c478bd9Sstevel@tonic-gate 	 * Enable the 'wire-speed' feature, if the chip supports it
11047c478bd9Sstevel@tonic-gate 	 * and we haven't got (any) loopback mode selected.
11057c478bd9Sstevel@tonic-gate 	 */
11067c478bd9Sstevel@tonic-gate 	switch (bgep->chipid.device) {
11077c478bd9Sstevel@tonic-gate 	case DEVICE_ID_5700:
11087c478bd9Sstevel@tonic-gate 	case DEVICE_ID_5700x:
11097c478bd9Sstevel@tonic-gate 	case DEVICE_ID_5705C:
11107c478bd9Sstevel@tonic-gate 	case DEVICE_ID_5782:
11117c478bd9Sstevel@tonic-gate 		/*
11127c478bd9Sstevel@tonic-gate 		 * These chips are known or assumed not to support it
11137c478bd9Sstevel@tonic-gate 		 */
11147c478bd9Sstevel@tonic-gate 		break;
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	default:
11177c478bd9Sstevel@tonic-gate 		/*
11187c478bd9Sstevel@tonic-gate 		 * All other Broadcom chips are expected to support it.
11197c478bd9Sstevel@tonic-gate 		 */
11207c478bd9Sstevel@tonic-gate 		if (bgep->param_loop_mode == BGE_LOOP_NONE)
11217c478bd9Sstevel@tonic-gate 			bge_mii_put16(bgep, MII_AUX_CONTROL,
11220c50e2bcSgh 			    MII_AUX_CTRL_MISC_WRITE_ENABLE |
11230c50e2bcSgh 			    MII_AUX_CTRL_MISC_WIRE_SPEED |
11240c50e2bcSgh 			    MII_AUX_CTRL_MISC);
11257c478bd9Sstevel@tonic-gate 		break;
11267c478bd9Sstevel@tonic-gate 	}
11277c478bd9Sstevel@tonic-gate #endif	/* BGE_COPPER_WIRESPEED */
1128087a28d1SDavid Gwynne 
1129087a28d1SDavid Gwynne 	/* enable EEE on those chips that support it */
1130087a28d1SDavid Gwynne 	bge_eee_autoneg(bgep, adv_100fdx, adv_1000fdx);
1131087a28d1SDavid Gwynne 
113200d0963fSdilpreet 	return (DDI_SUCCESS);
11337c478bd9Sstevel@tonic-gate }
11347c478bd9Sstevel@tonic-gate 
11357c478bd9Sstevel@tonic-gate static boolean_t
bge_check_copper(bge_t * bgep,boolean_t recheck)11367c478bd9Sstevel@tonic-gate bge_check_copper(bge_t *bgep, boolean_t recheck)
11377c478bd9Sstevel@tonic-gate {
11387c478bd9Sstevel@tonic-gate 	uint32_t emac_status;
11397c478bd9Sstevel@tonic-gate 	uint16_t mii_status;
11407c478bd9Sstevel@tonic-gate 	uint16_t aux;
11417c478bd9Sstevel@tonic-gate 	uint_t mode;
11427c478bd9Sstevel@tonic-gate 	boolean_t linkup;
1143087a28d1SDavid Gwynne 	int i;
11447c478bd9Sstevel@tonic-gate 
11457c478bd9Sstevel@tonic-gate 	/*
11467c478bd9Sstevel@tonic-gate 	 * Step 10: read the status from the PHY (which is self-clearing
11477c478bd9Sstevel@tonic-gate 	 * on read!); also read & clear the main (Ethernet) MAC status
11487c478bd9Sstevel@tonic-gate 	 * (the relevant bits of this are write-one-to-clear).
11497c478bd9Sstevel@tonic-gate 	 */
1150087a28d1SDavid Gwynne 	for (i = 0; i < 100; i++) {
1151087a28d1SDavid Gwynne 		drv_usecwait(40);
1152087a28d1SDavid Gwynne 		mii_status = bge_mii_get16(bgep, MII_STATUS);
1153087a28d1SDavid Gwynne 	}
11547c478bd9Sstevel@tonic-gate 	emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG);
11557c478bd9Sstevel@tonic-gate 	bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status);
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link %d/%s, MII status 0x%x "
11580c50e2bcSgh 	    "(was 0x%x), Ethernet MAC status 0x%x",
11590c50e2bcSgh 	    bgep->link_state, UPORDOWN(bgep->param_link_up), mii_status,
11600c50e2bcSgh 	    bgep->phy_gen_status, emac_status));
11617c478bd9Sstevel@tonic-gate 
11627c478bd9Sstevel@tonic-gate 	/*
11637c478bd9Sstevel@tonic-gate 	 * If the PHY status hasn't changed since last we looked, and
11647c478bd9Sstevel@tonic-gate 	 * we not forcing a recheck (i.e. the link state was already
11657c478bd9Sstevel@tonic-gate 	 * known), there's nothing to do.
11667c478bd9Sstevel@tonic-gate 	 */
1167087a28d1SDavid Gwynne 	if (mii_status == bgep->phy_gen_status && !recheck) {
1168087a28d1SDavid Gwynne 		BGE_DEBUG(("bge_check_copper: no link change"));
11697c478bd9Sstevel@tonic-gate 		return (B_FALSE);
1170087a28d1SDavid Gwynne 	}
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate 	do {
11737c478bd9Sstevel@tonic-gate 		/*
11747c478bd9Sstevel@tonic-gate 		 * Step 11: read AUX STATUS register to find speed/duplex
11757c478bd9Sstevel@tonic-gate 		 */
1176087a28d1SDavid Gwynne 		for (i = 0; i < 2000; i++) {
1177087a28d1SDavid Gwynne 			drv_usecwait(10);
1178087a28d1SDavid Gwynne 			aux = bge_mii_get16(bgep, MII_AUX_STATUS);
1179087a28d1SDavid Gwynne 		}
11807c478bd9Sstevel@tonic-gate 		BGE_CDB(bge_phydump, (bgep, mii_status, aux));
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 		/*
11837c478bd9Sstevel@tonic-gate 		 * We will only consider the link UP if all the readings
11847c478bd9Sstevel@tonic-gate 		 * are consistent and give meaningful results ...
11857c478bd9Sstevel@tonic-gate 		 */
11867c478bd9Sstevel@tonic-gate 		mode = aux & MII_AUX_STATUS_MODE_MASK;
11877c478bd9Sstevel@tonic-gate 		mode >>= MII_AUX_STATUS_MODE_SHIFT;
11885a506a18Syong tan - Sun Microsystems - Beijing China 		if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
11895a506a18Syong tan - Sun Microsystems - Beijing China 			linkup = BIS(aux, MII_AUX_STATUS_LINKUP);
11905a506a18Syong tan - Sun Microsystems - Beijing China 			linkup &= BIS(mii_status, MII_STATUS_LINKUP);
11915a506a18Syong tan - Sun Microsystems - Beijing China 		} else {
11925a506a18Syong tan - Sun Microsystems - Beijing China 			linkup = bge_copper_link_speed[mode] > 0;
11935a506a18Syong tan - Sun Microsystems - Beijing China 			linkup &= bge_copper_link_duplex[mode] !=
11945a506a18Syong tan - Sun Microsystems - Beijing China 			    LINK_DUPLEX_UNKNOWN;
11955a506a18Syong tan - Sun Microsystems - Beijing China 			linkup &= BIS(aux, MII_AUX_STATUS_LINKUP);
11965a506a18Syong tan - Sun Microsystems - Beijing China 			linkup &= BIS(mii_status, MII_STATUS_LINKUP);
11975a506a18Syong tan - Sun Microsystems - Beijing China 		}
11987c478bd9Sstevel@tonic-gate 
11997c478bd9Sstevel@tonic-gate 		BGE_DEBUG(("bge_check_copper: MII status 0x%x aux 0x%x "
12000c50e2bcSgh 		    "=> mode %d (%s)",
12010c50e2bcSgh 		    mii_status, aux,
12020c50e2bcSgh 		    mode, UPORDOWN(linkup)));
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 		/*
12057c478bd9Sstevel@tonic-gate 		 * Record current register values, then reread status
12067c478bd9Sstevel@tonic-gate 		 * register & loop until it stabilises ...
12077c478bd9Sstevel@tonic-gate 		 */
12087c478bd9Sstevel@tonic-gate 		bgep->phy_aux_status = aux;
12097c478bd9Sstevel@tonic-gate 		bgep->phy_gen_status = mii_status;
1210087a28d1SDavid Gwynne 
1211087a28d1SDavid Gwynne 		for (i = 0; i < 100; i++)
1212087a28d1SDavid Gwynne 		{
1213087a28d1SDavid Gwynne 			drv_usecwait(40);
1214087a28d1SDavid Gwynne 			mii_status = bge_mii_get16(bgep, MII_STATUS);
1215087a28d1SDavid Gwynne 		}
12167c478bd9Sstevel@tonic-gate 	} while (mii_status != bgep->phy_gen_status);
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate 	/*
12197c478bd9Sstevel@tonic-gate 	 * Assume very little ...
12207c478bd9Sstevel@tonic-gate 	 */
12217c478bd9Sstevel@tonic-gate 	bgep->param_lp_autoneg = B_FALSE;
12227c478bd9Sstevel@tonic-gate 	bgep->param_lp_1000fdx = B_FALSE;
12237c478bd9Sstevel@tonic-gate 	bgep->param_lp_1000hdx = B_FALSE;
12247c478bd9Sstevel@tonic-gate 	bgep->param_lp_100fdx = B_FALSE;
12257c478bd9Sstevel@tonic-gate 	bgep->param_lp_100hdx = B_FALSE;
12267c478bd9Sstevel@tonic-gate 	bgep->param_lp_10fdx = B_FALSE;
12277c478bd9Sstevel@tonic-gate 	bgep->param_lp_10hdx = B_FALSE;
12287c478bd9Sstevel@tonic-gate 	bgep->param_lp_pause = B_FALSE;
12297c478bd9Sstevel@tonic-gate 	bgep->param_lp_asym_pause = B_FALSE;
12307c478bd9Sstevel@tonic-gate 	bgep->param_link_autoneg = B_FALSE;
12317c478bd9Sstevel@tonic-gate 	bgep->param_link_tx_pause = B_FALSE;
12327c478bd9Sstevel@tonic-gate 	if (bgep->param_adv_autoneg)
12337c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = B_FALSE;
12347c478bd9Sstevel@tonic-gate 	else
12357c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = bgep->param_adv_pause;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	/*
12387c478bd9Sstevel@tonic-gate 	 * Discover all the link partner's abilities.
1239256e438eSzh 	 * These are scattered through various registers ...
12407c478bd9Sstevel@tonic-gate 	 */
12417c478bd9Sstevel@tonic-gate 	if (BIS(aux, MII_AUX_STATUS_LP_ANEG_ABLE)) {
12427c478bd9Sstevel@tonic-gate 		bgep->param_lp_autoneg = B_TRUE;
12437c478bd9Sstevel@tonic-gate 		bgep->param_link_autoneg = B_TRUE;
12447c478bd9Sstevel@tonic-gate 		bgep->param_link_tx_pause = BIS(aux, MII_AUX_STATUS_TX_PAUSE);
12457c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = BIS(aux, MII_AUX_STATUS_RX_PAUSE);
12467c478bd9Sstevel@tonic-gate 
1247bdb9230aSGarrett D'Amore 		aux = bge_mii_get16(bgep, MII_MSSTATUS);
1248bdb9230aSGarrett D'Amore 		bgep->param_lp_1000fdx = BIS(aux, MII_MSSTATUS_LP1000T_FD);
1249bdb9230aSGarrett D'Amore 		bgep->param_lp_1000hdx = BIS(aux, MII_MSSTATUS_LP1000T);
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 		aux = bge_mii_get16(bgep, MII_AN_LPABLE);
12527c478bd9Sstevel@tonic-gate 		bgep->param_lp_100fdx = BIS(aux, MII_ABILITY_100BASE_TX_FD);
12537c478bd9Sstevel@tonic-gate 		bgep->param_lp_100hdx = BIS(aux, MII_ABILITY_100BASE_TX);
12547c478bd9Sstevel@tonic-gate 		bgep->param_lp_10fdx = BIS(aux, MII_ABILITY_10BASE_T_FD);
12557c478bd9Sstevel@tonic-gate 		bgep->param_lp_10hdx = BIS(aux, MII_ABILITY_10BASE_T);
12567c478bd9Sstevel@tonic-gate 		bgep->param_lp_pause = BIS(aux, MII_ABILITY_PAUSE);
1257bdb9230aSGarrett D'Amore 		bgep->param_lp_asym_pause = BIS(aux, MII_ABILITY_ASMPAUSE);
12587c478bd9Sstevel@tonic-gate 	}
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 	/*
12617c478bd9Sstevel@tonic-gate 	 * Step 12: update ndd-visible state parameters, BUT!
12627c478bd9Sstevel@tonic-gate 	 * we don't transfer the new state to <link_state> just yet;
12637c478bd9Sstevel@tonic-gate 	 * instead we mark the <link_state> as UNKNOWN, and our caller
12647c478bd9Sstevel@tonic-gate 	 * will resolve it once the status has stopped changing and
12657c478bd9Sstevel@tonic-gate 	 * been stable for several seconds.
12667c478bd9Sstevel@tonic-gate 	 */
12677c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_copper: link was %s speed %d duplex %d",
12680c50e2bcSgh 	    UPORDOWN(bgep->param_link_up),
12690c50e2bcSgh 	    bgep->param_link_speed,
12700c50e2bcSgh 	    bgep->param_link_duplex));
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 	if (!linkup)
12737c478bd9Sstevel@tonic-gate 		mode = MII_AUX_STATUS_MODE_NONE;
12747c478bd9Sstevel@tonic-gate 	bgep->param_link_up = linkup;
12757c478bd9Sstevel@tonic-gate 	bgep->link_state = LINK_STATE_UNKNOWN;
12765a506a18Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
12775a506a18Syong tan - Sun Microsystems - Beijing China 		if (bgep->phy_aux_status & MII_AUX_STATUS_NEG_ENABLED_5906) {
12785a506a18Syong tan - Sun Microsystems - Beijing China 			bgep->param_link_speed =
12795a506a18Syong tan - Sun Microsystems - Beijing China 			    bge_copper_link_speed_5906[mode];
12805a506a18Syong tan - Sun Microsystems - Beijing China 			bgep->param_link_duplex =
12815a506a18Syong tan - Sun Microsystems - Beijing China 			    bge_copper_link_duplex_5906[mode];
12825a506a18Syong tan - Sun Microsystems - Beijing China 		} else {
12835a506a18Syong tan - Sun Microsystems - Beijing China 			bgep->param_link_speed = (bgep->phy_aux_status &
12845a506a18Syong tan - Sun Microsystems - Beijing China 			    MII_AUX_STATUS_SPEED_IND_5906) ?  100 : 10;
12855a506a18Syong tan - Sun Microsystems - Beijing China 			bgep->param_link_duplex = (bgep->phy_aux_status &
12865a506a18Syong tan - Sun Microsystems - Beijing China 			    MII_AUX_STATUS_DUPLEX_IND_5906) ? LINK_DUPLEX_FULL :
12875a506a18Syong tan - Sun Microsystems - Beijing China 			    LINK_DUPLEX_HALF;
12885a506a18Syong tan - Sun Microsystems - Beijing China 		}
12895a506a18Syong tan - Sun Microsystems - Beijing China 	} else {
12905a506a18Syong tan - Sun Microsystems - Beijing China 		bgep->param_link_speed = bge_copper_link_speed[mode];
12915a506a18Syong tan - Sun Microsystems - Beijing China 		bgep->param_link_duplex = bge_copper_link_duplex[mode];
12925a506a18Syong tan - Sun Microsystems - Beijing China 	}
12937c478bd9Sstevel@tonic-gate 
1294087a28d1SDavid Gwynne 	bge_eee_adjust(bgep);
1295087a28d1SDavid Gwynne 
1296087a28d1SDavid Gwynne 	bge_log(bgep, "bge_check_copper: link now %s speed %d duplex %d",
1297087a28d1SDavid Gwynne 	        UPORDOWN(bgep->param_link_up),
1298087a28d1SDavid Gwynne 	        bgep->param_link_speed,
1299087a28d1SDavid Gwynne 	        bgep->param_link_duplex);
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate 	return (B_TRUE);
13027c478bd9Sstevel@tonic-gate }
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate static const phys_ops_t copper_ops = {
13057c478bd9Sstevel@tonic-gate 	bge_restart_copper,
13067c478bd9Sstevel@tonic-gate 	bge_update_copper,
13077c478bd9Sstevel@tonic-gate 	bge_check_copper
13087c478bd9Sstevel@tonic-gate };
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate /*
13127c478bd9Sstevel@tonic-gate  * ========== SerDes support ==========
13137c478bd9Sstevel@tonic-gate  */
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate #undef	BGE_DBG
13167c478bd9Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_SERDES	/* debug flag for this code	*/
13177c478bd9Sstevel@tonic-gate 
13187c478bd9Sstevel@tonic-gate /*
13197c478bd9Sstevel@tonic-gate  * Reinitialise the SerDes interface.  Note that it normally powers
13207c478bd9Sstevel@tonic-gate  * up in the disabled state, so we need to explicitly activate it.
13217c478bd9Sstevel@tonic-gate  */
132200d0963fSdilpreet static int
bge_restart_serdes(bge_t * bgep,boolean_t powerdown)13237c478bd9Sstevel@tonic-gate bge_restart_serdes(bge_t *bgep, boolean_t powerdown)
13247c478bd9Sstevel@tonic-gate {
13257c478bd9Sstevel@tonic-gate 	uint32_t macmode;
13267c478bd9Sstevel@tonic-gate 
13277c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_restart_serdes($%p, %d)", (void *)bgep, powerdown));
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 	/*
13327c478bd9Sstevel@tonic-gate 	 * Ensure that the main Ethernet MAC mode register is programmed
13337c478bd9Sstevel@tonic-gate 	 * appropriately for the SerDes interface ...
13347c478bd9Sstevel@tonic-gate 	 */
13357c478bd9Sstevel@tonic-gate 	macmode = bge_reg_get32(bgep, ETHERNET_MAC_MODE_REG);
1336087a28d1SDavid Gwynne 	macmode &= ~ETHERNET_MODE_LINK_POLARITY;
1337087a28d1SDavid Gwynne 	macmode &= ~ETHERNET_MODE_PORTMODE_MASK;
1338087a28d1SDavid Gwynne 	if (DEVICE_5717_SERIES_CHIPSETS(bgep) ||
1339087a28d1SDavid Gwynne 	    DEVICE_5725_SERIES_CHIPSETS(bgep) ||
1340a2876d03SRobert Mustacchi 	    DEVICE_5714_SERIES_CHIPSETS(bgep) ||
1341a2876d03SRobert Mustacchi 	    DEVICE_57765_SERIES_CHIPSETS(bgep)) {
13421a719488SCrisson Guanghao Hu 		macmode |= ETHERNET_MODE_PORTMODE_GMII;
13431a719488SCrisson Guanghao Hu 	} else {
13441a719488SCrisson Guanghao Hu 		macmode |= ETHERNET_MODE_PORTMODE_TBI;
13451a719488SCrisson Guanghao Hu 	}
13467c478bd9Sstevel@tonic-gate 	bge_reg_put32(bgep, ETHERNET_MAC_MODE_REG, macmode);
13477c478bd9Sstevel@tonic-gate 
13487c478bd9Sstevel@tonic-gate 	/*
13497c478bd9Sstevel@tonic-gate 	 * Ensure that loopback is OFF and comma detection is enabled.  Then
13507c478bd9Sstevel@tonic-gate 	 * disable the SerDes output (the first time through, it may/will
13517c478bd9Sstevel@tonic-gate 	 * already be disabled).  If we're shutting down, leave it disabled.
13527c478bd9Sstevel@tonic-gate 	 */
13537c478bd9Sstevel@tonic-gate 	bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TBI_LOOPBACK);
13547c478bd9Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_COMMA_DETECT);
13557c478bd9Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
13567c478bd9Sstevel@tonic-gate 	if (powerdown)
135700d0963fSdilpreet 		return (DDI_SUCCESS);
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	/*
13607c478bd9Sstevel@tonic-gate 	 * Otherwise, pause, (re-)enable the SerDes output, and send
13617c478bd9Sstevel@tonic-gate 	 * all-zero config words in order to force autoneg restart.
13627c478bd9Sstevel@tonic-gate 	 * Invalidate the saved "link partners received configs", as
13637c478bd9Sstevel@tonic-gate 	 * we're starting over ...
13647c478bd9Sstevel@tonic-gate 	 */
13657c478bd9Sstevel@tonic-gate 	drv_usecwait(10000);
13667c478bd9Sstevel@tonic-gate 	bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
13677c478bd9Sstevel@tonic-gate 	bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG, 0);
13687c478bd9Sstevel@tonic-gate 	bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
13697c478bd9Sstevel@tonic-gate 	drv_usecwait(10);
13707c478bd9Sstevel@tonic-gate 	bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
13717c478bd9Sstevel@tonic-gate 	bgep->serdes_lpadv = AUTONEG_CODE_FAULT_ANEG_ERR;
13727c478bd9Sstevel@tonic-gate 	bgep->serdes_status = ~0U;
137300d0963fSdilpreet 	return (DDI_SUCCESS);
13747c478bd9Sstevel@tonic-gate }
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate /*
13777c478bd9Sstevel@tonic-gate  * Synchronise the SerDes speed/duplex/autonegotiation capabilities and
13787c478bd9Sstevel@tonic-gate  * advertisements with the required settings as specified by the various
13797c478bd9Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
13807c478bd9Sstevel@tonic-gate  *
13817c478bd9Sstevel@tonic-gate  * We always reinitalise the SerDes; this should cause the link to go down,
13827c478bd9Sstevel@tonic-gate  * and then back up again once the link is stable and autonegotiation
13837c478bd9Sstevel@tonic-gate  * (if enabled) is complete.  We should get a link state change interrupt
13847c478bd9Sstevel@tonic-gate  * somewhere along the way ...
13857c478bd9Sstevel@tonic-gate  *
13867c478bd9Sstevel@tonic-gate  * NOTE: SerDes only supports 1000FDX/HDX (with or without pause) so the
13877c478bd9Sstevel@tonic-gate  * param_* variables relating to lower speeds are ignored.
13887c478bd9Sstevel@tonic-gate  *
13897c478bd9Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
13907c478bd9Sstevel@tonic-gate  */
139100d0963fSdilpreet static int
bge_update_serdes(bge_t * bgep)13927c478bd9Sstevel@tonic-gate bge_update_serdes(bge_t *bgep)
13937c478bd9Sstevel@tonic-gate {
13947c478bd9Sstevel@tonic-gate 	boolean_t adv_autoneg;
13957c478bd9Sstevel@tonic-gate 	boolean_t adv_pause;
13967c478bd9Sstevel@tonic-gate 	boolean_t adv_asym_pause;
13977c478bd9Sstevel@tonic-gate 	boolean_t adv_1000fdx;
13987c478bd9Sstevel@tonic-gate 	boolean_t adv_1000hdx;
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 	uint32_t serdes;
14017c478bd9Sstevel@tonic-gate 	uint32_t advert;
14027c478bd9Sstevel@tonic-gate 
14037c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_update_serdes($%p)", (void *)bgep));
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: autoneg %d "
14080c50e2bcSgh 	    "pause %d asym_pause %d "
14090c50e2bcSgh 	    "1000fdx %d 1000hdx %d "
14100c50e2bcSgh 	    "100fdx %d 100hdx %d "
14110c50e2bcSgh 	    "10fdx %d 10hdx %d ",
14120c50e2bcSgh 	    bgep->param_adv_autoneg,
14130c50e2bcSgh 	    bgep->param_adv_pause, bgep->param_adv_asym_pause,
14140c50e2bcSgh 	    bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
14150c50e2bcSgh 	    bgep->param_adv_100fdx, bgep->param_adv_100hdx,
14160c50e2bcSgh 	    bgep->param_adv_10fdx, bgep->param_adv_10hdx));
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate 	serdes = advert = 0;
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate 	/*
14217c478bd9Sstevel@tonic-gate 	 * SerDes settings are normally based on the param_* variables,
14227c478bd9Sstevel@tonic-gate 	 * but if any loopback mode is in effect, that takes precedence.
14237c478bd9Sstevel@tonic-gate 	 *
14247c478bd9Sstevel@tonic-gate 	 * BGE supports MAC-internal loopback, PHY-internal loopback,
14257c478bd9Sstevel@tonic-gate 	 * and External loopback at a variety of speeds (with a special
14267c478bd9Sstevel@tonic-gate 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
14277c478bd9Sstevel@tonic-gate 	 * is turned ON, and the speed/mastership is forced.
14287c478bd9Sstevel@tonic-gate 	 *
14297c478bd9Sstevel@tonic-gate 	 * Note: for the SerDes interface, "PHY" internal loopback is
14307c478bd9Sstevel@tonic-gate 	 * interpreted as SerDes internal loopback, and all external
14317c478bd9Sstevel@tonic-gate 	 * loopback modes are treated equivalently, as 1Gb/external.
14327c478bd9Sstevel@tonic-gate 	 */
14337c478bd9Sstevel@tonic-gate 	switch (bgep->param_loop_mode) {
14347c478bd9Sstevel@tonic-gate 	case BGE_LOOP_NONE:
14357c478bd9Sstevel@tonic-gate 	default:
14367c478bd9Sstevel@tonic-gate 		adv_autoneg = bgep->param_adv_autoneg;
14377c478bd9Sstevel@tonic-gate 		adv_pause = bgep->param_adv_pause;
14387c478bd9Sstevel@tonic-gate 		adv_asym_pause = bgep->param_adv_asym_pause;
14397c478bd9Sstevel@tonic-gate 		adv_1000fdx = bgep->param_adv_1000fdx;
14407c478bd9Sstevel@tonic-gate 		adv_1000hdx = bgep->param_adv_1000hdx;
14417c478bd9Sstevel@tonic-gate 		break;
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_PHY:
14447c478bd9Sstevel@tonic-gate 		serdes |= SERDES_CONTROL_TBI_LOOPBACK;
14457c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
14467c478bd9Sstevel@tonic-gate 	case BGE_LOOP_INTERNAL_MAC:
14477c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_1000:
14487c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_100:
14497c478bd9Sstevel@tonic-gate 	case BGE_LOOP_EXTERNAL_10:
14507c478bd9Sstevel@tonic-gate 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
14517c478bd9Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
14527c478bd9Sstevel@tonic-gate 		adv_1000hdx = B_FALSE;
14537c478bd9Sstevel@tonic-gate 		break;
14547c478bd9Sstevel@tonic-gate 	}
14557c478bd9Sstevel@tonic-gate 
14567c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: autoneg %d "
14570c50e2bcSgh 	    "pause %d asym_pause %d "
14580c50e2bcSgh 	    "1000fdx %d 1000hdx %d ",
14590c50e2bcSgh 	    adv_autoneg,
14600c50e2bcSgh 	    adv_pause, adv_asym_pause,
14610c50e2bcSgh 	    adv_1000fdx, adv_1000hdx));
14627c478bd9Sstevel@tonic-gate 
14637c478bd9Sstevel@tonic-gate 	/*
14647c478bd9Sstevel@tonic-gate 	 * We should have at least one gigabit technology capability
14657c478bd9Sstevel@tonic-gate 	 * set; if not, we select a default of 1000Mb/s full-duplex
14667c478bd9Sstevel@tonic-gate 	 */
14677c478bd9Sstevel@tonic-gate 	if (!adv_1000fdx && !adv_1000hdx)
14687c478bd9Sstevel@tonic-gate 		adv_1000fdx = B_TRUE;
14697c478bd9Sstevel@tonic-gate 
14707c478bd9Sstevel@tonic-gate 	/*
14717c478bd9Sstevel@tonic-gate 	 * Now transform the adv_* variables into the proper settings
14727c478bd9Sstevel@tonic-gate 	 * of the SerDes registers ...
14737c478bd9Sstevel@tonic-gate 	 *
14747c478bd9Sstevel@tonic-gate 	 * If autonegotiation is (now) not enabled, pretend it's been
14757c478bd9Sstevel@tonic-gate 	 * done and failed ...
14767c478bd9Sstevel@tonic-gate 	 */
14777c478bd9Sstevel@tonic-gate 	if (!adv_autoneg)
14787c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_FAULT_ANEG_ERR;
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 	if (adv_1000fdx) {
14817c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_FULL_DUPLEX;
14827c478bd9Sstevel@tonic-gate 		bgep->param_adv_1000fdx = adv_1000fdx;
14837c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_FULL;
14847c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
14857c478bd9Sstevel@tonic-gate 	}
14867c478bd9Sstevel@tonic-gate 	if (adv_1000hdx) {
14877c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_HALF_DUPLEX;
14887c478bd9Sstevel@tonic-gate 		bgep->param_adv_1000hdx = adv_1000hdx;
14897c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_HALF;
14907c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
14917c478bd9Sstevel@tonic-gate 	}
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate 	if (adv_pause)
14947c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_PAUSE;
14957c478bd9Sstevel@tonic-gate 	if (adv_asym_pause)
14967c478bd9Sstevel@tonic-gate 		advert |= AUTONEG_CODE_ASYM_PAUSE;
14977c478bd9Sstevel@tonic-gate 
14987c478bd9Sstevel@tonic-gate 	/*
14997c478bd9Sstevel@tonic-gate 	 * Restart the SerDes and write the new values.  Note the
15007c478bd9Sstevel@tonic-gate 	 * time, so that we can say whether subsequent link state
15017c478bd9Sstevel@tonic-gate 	 * changes can be attributed to our reprogramming the SerDes
15027c478bd9Sstevel@tonic-gate 	 */
15037c478bd9Sstevel@tonic-gate 	bgep->serdes_advert = advert;
150400d0963fSdilpreet 	(void) bge_restart_serdes(bgep, B_FALSE);
15057c478bd9Sstevel@tonic-gate 	bge_reg_set32(bgep, SERDES_CONTROL_REG, serdes);
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_update_serdes: serdes |= 0x%x, advert 0x%x",
15080c50e2bcSgh 	    serdes, advert));
150900d0963fSdilpreet 	return (DDI_SUCCESS);
15107c478bd9Sstevel@tonic-gate }
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate /*
15137c478bd9Sstevel@tonic-gate  * Bare-minimum autoneg protocol
15147c478bd9Sstevel@tonic-gate  *
15157c478bd9Sstevel@tonic-gate  * This code is only called when the link is up and we're receiving config
15167c478bd9Sstevel@tonic-gate  * words, which implies that the link partner wants to autonegotiate
15177c478bd9Sstevel@tonic-gate  * (otherwise, we wouldn't see configs and wouldn't reach this code).
15187c478bd9Sstevel@tonic-gate  */
15197c478bd9Sstevel@tonic-gate static void
bge_autoneg_serdes(bge_t * bgep)15207c478bd9Sstevel@tonic-gate bge_autoneg_serdes(bge_t *bgep)
15217c478bd9Sstevel@tonic-gate {
15227c478bd9Sstevel@tonic-gate 	boolean_t ack;
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 	bgep->serdes_lpadv = bge_reg_get32(bgep, RX_1000BASEX_AUTONEG_REG);
15257c478bd9Sstevel@tonic-gate 	ack = BIS(bgep->serdes_lpadv, AUTONEG_CODE_ACKNOWLEDGE);
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate 	if (!ack) {
15287c478bd9Sstevel@tonic-gate 		/*
15297c478bd9Sstevel@tonic-gate 		 * Phase 1: after SerDes reset, we send a few zero configs
15307c478bd9Sstevel@tonic-gate 		 * but then stop.  Here the partner is sending configs, but
15317c478bd9Sstevel@tonic-gate 		 * not ACKing ours; we assume that's 'cos we're not sending
15327c478bd9Sstevel@tonic-gate 		 * any.  So here we send ours, with ACK already set.
15337c478bd9Sstevel@tonic-gate 		 */
15347c478bd9Sstevel@tonic-gate 		bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG,
15350c50e2bcSgh 		    bgep->serdes_advert | AUTONEG_CODE_ACKNOWLEDGE);
15367c478bd9Sstevel@tonic-gate 		bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG,
15370c50e2bcSgh 		    ETHERNET_MODE_SEND_CFGS);
15387c478bd9Sstevel@tonic-gate 	} else {
15397c478bd9Sstevel@tonic-gate 		/*
15407c478bd9Sstevel@tonic-gate 		 * Phase 2: partner has ACKed our configs, so now we can
15417c478bd9Sstevel@tonic-gate 		 * stop sending; once our partner also stops sending, we
15427c478bd9Sstevel@tonic-gate 		 * can resolve the Tx/Rx configs.
15437c478bd9Sstevel@tonic-gate 		 */
15447c478bd9Sstevel@tonic-gate 		bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG,
15450c50e2bcSgh 		    ETHERNET_MODE_SEND_CFGS);
15467c478bd9Sstevel@tonic-gate 	}
15477c478bd9Sstevel@tonic-gate 
15487c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_autoneg_serdes: Rx 0x%x %s Tx 0x%x",
15490c50e2bcSgh 	    bgep->serdes_lpadv,
15500c50e2bcSgh 	    ack ? "stop" : "send",
15510c50e2bcSgh 	    bgep->serdes_advert));
15527c478bd9Sstevel@tonic-gate }
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate static boolean_t
bge_check_serdes(bge_t * bgep,boolean_t recheck)15557c478bd9Sstevel@tonic-gate bge_check_serdes(bge_t *bgep, boolean_t recheck)
15567c478bd9Sstevel@tonic-gate {
15577c478bd9Sstevel@tonic-gate 	uint32_t emac_status;
1558c4e25a46Syong tan - Sun Microsystems - Beijing China 	uint32_t tx_status;
15597c478bd9Sstevel@tonic-gate 	uint32_t lpadv;
15607c478bd9Sstevel@tonic-gate 	boolean_t linkup;
15611a719488SCrisson Guanghao Hu 	boolean_t linkup_old = bgep->param_link_up;
15627c478bd9Sstevel@tonic-gate 
15637c478bd9Sstevel@tonic-gate 	for (;;) {
15647c478bd9Sstevel@tonic-gate 		/*
15651a719488SCrisson Guanghao Hu 		 * Step 10: BCM5714S, BCM5715S only
15661a719488SCrisson Guanghao Hu 		 * Don't call function bge_autoneg_serdes() as
15671a719488SCrisson Guanghao Hu 		 * RX_1000BASEX_AUTONEG_REG (0x0448) is not applicable
15681a719488SCrisson Guanghao Hu 		 * to BCM5705, BCM5788, BCM5721, BCM5751, BCM5752,
1569a2876d03SRobert Mustacchi 		 * BCM5714, BCM5715, and BCM57765 family devices.
15707c478bd9Sstevel@tonic-gate 		 */
1571087a28d1SDavid Gwynne 		if (DEVICE_5717_SERIES_CHIPSETS(bgep) ||
1572087a28d1SDavid Gwynne 		    DEVICE_5725_SERIES_CHIPSETS(bgep) ||
1573a2876d03SRobert Mustacchi 		    DEVICE_5714_SERIES_CHIPSETS(bgep) ||
1574a2876d03SRobert Mustacchi 		    DEVICE_57765_SERIES_CHIPSETS(bgep)) {
1575c4e25a46Syong tan - Sun Microsystems - Beijing China 			tx_status = bge_reg_get32(bgep,
1576c4e25a46Syong tan - Sun Microsystems - Beijing China 			    TRANSMIT_MAC_STATUS_REG);
1577c4e25a46Syong tan - Sun Microsystems - Beijing China 			linkup = BIS(tx_status, TRANSMIT_STATUS_LINK_UP);
1578c4e25a46Syong tan - Sun Microsystems - Beijing China 			emac_status = bge_reg_get32(bgep,
1579c4e25a46Syong tan - Sun Microsystems - Beijing China 			    ETHERNET_MAC_STATUS_REG);
15801a719488SCrisson Guanghao Hu 			bgep->serdes_status = emac_status;
1581087a28d1SDavid Gwynne 			/* clear write-one-to-clear bits in MAC status */
1582087a28d1SDavid Gwynne 			if ((emac_status & ETHERNET_STATUS_MI_COMPLETE) &&
1583087a28d1SDavid Gwynne 			    (DEVICE_5717_SERIES_CHIPSETS(bgep) ||
1584087a28d1SDavid Gwynne 			     DEVICE_5725_SERIES_CHIPSETS(bgep))) {
1585087a28d1SDavid Gwynne 				emac_status |= ETHERNET_STATUS_SYNC_CHANGED |
1586087a28d1SDavid Gwynne 				    ETHERNET_STATUS_CFG_CHANGED;
1587087a28d1SDavid Gwynne 			}
1588087a28d1SDavid Gwynne 			bge_reg_put32(bgep,
1589087a28d1SDavid Gwynne 			    ETHERNET_MAC_STATUS_REG, emac_status);
1590087a28d1SDavid Gwynne 			/*
1591087a28d1SDavid Gwynne 			 * If the link status has not changed then then
1592087a28d1SDavid Gwynne 			 * break. If it has loop around and recheck again.
1593087a28d1SDavid Gwynne 			 * Keep looping until the link status has not
1594087a28d1SDavid Gwynne 			 * changed.
1595087a28d1SDavid Gwynne 			 */
15961a719488SCrisson Guanghao Hu 			if ((linkup && linkup_old) ||
15971a719488SCrisson Guanghao Hu 			    (!linkup && !linkup_old)) {
15981a719488SCrisson Guanghao Hu 				break;
15991a719488SCrisson Guanghao Hu 			}
16001a719488SCrisson Guanghao Hu 			if (linkup)
16011a719488SCrisson Guanghao Hu 				linkup_old = B_TRUE;
16021a719488SCrisson Guanghao Hu 			else
16031a719488SCrisson Guanghao Hu 				linkup_old = B_FALSE;
16041a719488SCrisson Guanghao Hu 			recheck = B_TRUE;
16051a719488SCrisson Guanghao Hu 		} else {
16061a719488SCrisson Guanghao Hu 			/*
16071a719488SCrisson Guanghao Hu 			 * Step 10: others
16081a719488SCrisson Guanghao Hu 			 * read & clear the main (Ethernet) MAC status
16091a719488SCrisson Guanghao Hu 			 * (the relevant bits of this are write-one-to-clear).
16101a719488SCrisson Guanghao Hu 			 */
16111a719488SCrisson Guanghao Hu 			emac_status = bge_reg_get32(bgep,
16121a719488SCrisson Guanghao Hu 			    ETHERNET_MAC_STATUS_REG);
16131a719488SCrisson Guanghao Hu 			bge_reg_put32(bgep,
16141a719488SCrisson Guanghao Hu 			    ETHERNET_MAC_STATUS_REG, emac_status);
16157c478bd9Sstevel@tonic-gate 
16161a719488SCrisson Guanghao Hu 			BGE_DEBUG(("bge_check_serdes: link %d/%s, "
16171a719488SCrisson Guanghao Hu 			    "MAC status 0x%x (was 0x%x)",
16181a719488SCrisson Guanghao Hu 			    bgep->link_state, UPORDOWN(bgep->param_link_up),
16191a719488SCrisson Guanghao Hu 			    emac_status, bgep->serdes_status));
16207c478bd9Sstevel@tonic-gate 
16211a719488SCrisson Guanghao Hu 			/*
16221a719488SCrisson Guanghao Hu 			 * We will only consider the link UP if all the readings
16231a719488SCrisson Guanghao Hu 			 * are consistent and give meaningful results ...
16241a719488SCrisson Guanghao Hu 			 */
16251a719488SCrisson Guanghao Hu 			bgep->serdes_status = emac_status;
16261a719488SCrisson Guanghao Hu 			linkup = BIS(emac_status,
16271a719488SCrisson Guanghao Hu 			    ETHERNET_STATUS_SIGNAL_DETECT);
16281a719488SCrisson Guanghao Hu 			linkup &= BIS(emac_status, ETHERNET_STATUS_PCS_SYNCHED);
16297c478bd9Sstevel@tonic-gate 
16301a719488SCrisson Guanghao Hu 			/*
16311a719488SCrisson Guanghao Hu 			 * Now some fiddling with the interpretation:
16321a719488SCrisson Guanghao Hu 			 *	if there's been an error at the PCS level, treat
16331a719488SCrisson Guanghao Hu 			 *	it as a link change (the h/w doesn't do this)
16341a719488SCrisson Guanghao Hu 			 *
16351a719488SCrisson Guanghao Hu 			 *	if there's been a change, but it's only a PCS
16361a719488SCrisson Guanghao Hu 			 *	sync change (not a config change), AND the link
16371a719488SCrisson Guanghao Hu 			 *	already was & is still UP, then ignore the
16381a719488SCrisson Guanghao Hu 			 *	change
16391a719488SCrisson Guanghao Hu 			 */
16401a719488SCrisson Guanghao Hu 			if (BIS(emac_status, ETHERNET_STATUS_PCS_ERROR))
16411a719488SCrisson Guanghao Hu 				emac_status |= ETHERNET_STATUS_LINK_CHANGED;
16421a719488SCrisson Guanghao Hu 			else if (BIC(emac_status, ETHERNET_STATUS_CFG_CHANGED))
16431a719488SCrisson Guanghao Hu 				if (bgep->param_link_up && linkup)
16441a719488SCrisson Guanghao Hu 					emac_status &=
16451a719488SCrisson Guanghao Hu 					    ~ETHERNET_STATUS_LINK_CHANGED;
16467c478bd9Sstevel@tonic-gate 
16471a719488SCrisson Guanghao Hu 			BGE_DEBUG(("bge_check_serdes: status 0x%x => 0x%x %s",
16481a719488SCrisson Guanghao Hu 			    bgep->serdes_status, emac_status,
16491a719488SCrisson Guanghao Hu 			    UPORDOWN(linkup)));
16507c478bd9Sstevel@tonic-gate 
16511a719488SCrisson Guanghao Hu 			/*
16521a719488SCrisson Guanghao Hu 			 * If we're receiving configs, run the autoneg protocol
16531a719488SCrisson Guanghao Hu 			 */
16541a719488SCrisson Guanghao Hu 			if (linkup && BIS(emac_status,
16551a719488SCrisson Guanghao Hu 			    ETHERNET_STATUS_RECEIVING_CFG))
16561a719488SCrisson Guanghao Hu 				bge_autoneg_serdes(bgep);
16577c478bd9Sstevel@tonic-gate 
16581a719488SCrisson Guanghao Hu 			/*
16591a719488SCrisson Guanghao Hu 			 * If the SerDes status hasn't changed, we're done ...
16601a719488SCrisson Guanghao Hu 			 */
16611a719488SCrisson Guanghao Hu 			if (BIC(emac_status, ETHERNET_STATUS_LINK_CHANGED))
16621a719488SCrisson Guanghao Hu 				break;
16637c478bd9Sstevel@tonic-gate 
16641a719488SCrisson Guanghao Hu 			/*
16651a719488SCrisson Guanghao Hu 			 * Go round again until we no longer see a change ...
16661a719488SCrisson Guanghao Hu 			 */
16671a719488SCrisson Guanghao Hu 			recheck = B_TRUE;
16681a719488SCrisson Guanghao Hu 		}
16697c478bd9Sstevel@tonic-gate 	}
16707c478bd9Sstevel@tonic-gate 
16717c478bd9Sstevel@tonic-gate 	/*
16727c478bd9Sstevel@tonic-gate 	 * If we're not forcing a recheck (i.e. the link state was already
16737c478bd9Sstevel@tonic-gate 	 * known), and we didn't see the hardware flag a change, there's
16747c478bd9Sstevel@tonic-gate 	 * no more to do (and we tell the caller nothing happened).
16757c478bd9Sstevel@tonic-gate 	 */
16767c478bd9Sstevel@tonic-gate 	if (!recheck)
16777c478bd9Sstevel@tonic-gate 		return (B_FALSE);
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	/*
16807c478bd9Sstevel@tonic-gate 	 * Don't resolve autoneg until we're no longer receiving configs
16817c478bd9Sstevel@tonic-gate 	 */
16827c478bd9Sstevel@tonic-gate 	if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG))
16837c478bd9Sstevel@tonic-gate 		return (B_FALSE);
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 	/*
16867c478bd9Sstevel@tonic-gate 	 * Assume very little ...
16877c478bd9Sstevel@tonic-gate 	 */
16887c478bd9Sstevel@tonic-gate 	bgep->param_lp_autoneg = B_FALSE;
16897c478bd9Sstevel@tonic-gate 	bgep->param_lp_1000fdx = B_FALSE;
16907c478bd9Sstevel@tonic-gate 	bgep->param_lp_1000hdx = B_FALSE;
16917c478bd9Sstevel@tonic-gate 	bgep->param_lp_100fdx = B_FALSE;
16927c478bd9Sstevel@tonic-gate 	bgep->param_lp_100hdx = B_FALSE;
16937c478bd9Sstevel@tonic-gate 	bgep->param_lp_10fdx = B_FALSE;
16947c478bd9Sstevel@tonic-gate 	bgep->param_lp_10hdx = B_FALSE;
16957c478bd9Sstevel@tonic-gate 	bgep->param_lp_pause = B_FALSE;
16967c478bd9Sstevel@tonic-gate 	bgep->param_lp_asym_pause = B_FALSE;
16977c478bd9Sstevel@tonic-gate 	bgep->param_link_autoneg = B_FALSE;
16987c478bd9Sstevel@tonic-gate 	bgep->param_link_tx_pause = B_FALSE;
16997c478bd9Sstevel@tonic-gate 	if (bgep->param_adv_autoneg)
17007c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = B_FALSE;
17017c478bd9Sstevel@tonic-gate 	else
17027c478bd9Sstevel@tonic-gate 		bgep->param_link_rx_pause = bgep->param_adv_pause;
17037c478bd9Sstevel@tonic-gate 
17047c478bd9Sstevel@tonic-gate 	/*
17057c478bd9Sstevel@tonic-gate 	 * Discover all the link partner's abilities.
17067c478bd9Sstevel@tonic-gate 	 */
17077c478bd9Sstevel@tonic-gate 	lpadv = bgep->serdes_lpadv;
17087c478bd9Sstevel@tonic-gate 	if (lpadv != 0 && BIC(lpadv, AUTONEG_CODE_FAULT_MASK)) {
17097c478bd9Sstevel@tonic-gate 		/*
17107c478bd9Sstevel@tonic-gate 		 * No fault, so derive partner's capabilities
17117c478bd9Sstevel@tonic-gate 		 */
17127c478bd9Sstevel@tonic-gate 		bgep->param_lp_autoneg = B_TRUE;
17137c478bd9Sstevel@tonic-gate 		bgep->param_lp_1000fdx = BIS(lpadv, AUTONEG_CODE_FULL_DUPLEX);
17147c478bd9Sstevel@tonic-gate 		bgep->param_lp_1000hdx = BIS(lpadv, AUTONEG_CODE_HALF_DUPLEX);
17157c478bd9Sstevel@tonic-gate 		bgep->param_lp_pause = BIS(lpadv, AUTONEG_CODE_PAUSE);
17167c478bd9Sstevel@tonic-gate 		bgep->param_lp_asym_pause = BIS(lpadv, AUTONEG_CODE_ASYM_PAUSE);
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 		/*
17197c478bd9Sstevel@tonic-gate 		 * Pause direction resolution
17207c478bd9Sstevel@tonic-gate 		 */
17217c478bd9Sstevel@tonic-gate 		bgep->param_link_autoneg = B_TRUE;
17227c478bd9Sstevel@tonic-gate 		if (bgep->param_adv_pause &&
17237c478bd9Sstevel@tonic-gate 		    bgep->param_lp_pause) {
17247c478bd9Sstevel@tonic-gate 			bgep->param_link_tx_pause = B_TRUE;
17257c478bd9Sstevel@tonic-gate 			bgep->param_link_rx_pause = B_TRUE;
17267c478bd9Sstevel@tonic-gate 		}
17277c478bd9Sstevel@tonic-gate 		if (bgep->param_adv_asym_pause &&
17287c478bd9Sstevel@tonic-gate 		    bgep->param_lp_asym_pause) {
17297c478bd9Sstevel@tonic-gate 			if (bgep->param_adv_pause)
17307c478bd9Sstevel@tonic-gate 				bgep->param_link_rx_pause = B_TRUE;
17317c478bd9Sstevel@tonic-gate 			if (bgep->param_lp_pause)
17327c478bd9Sstevel@tonic-gate 				bgep->param_link_tx_pause = B_TRUE;
17337c478bd9Sstevel@tonic-gate 		}
17347c478bd9Sstevel@tonic-gate 	}
17357c478bd9Sstevel@tonic-gate 
17367c478bd9Sstevel@tonic-gate 	/*
17377c478bd9Sstevel@tonic-gate 	 * Step 12: update ndd-visible state parameters, BUT!
17387c478bd9Sstevel@tonic-gate 	 * we don't transfer the new state to <link_state> just yet;
17397c478bd9Sstevel@tonic-gate 	 * instead we mark the <link_state> as UNKNOWN, and our caller
17407c478bd9Sstevel@tonic-gate 	 * will resolve it once the status has stopped changing and
17417c478bd9Sstevel@tonic-gate 	 * been stable for several seconds.
17427c478bd9Sstevel@tonic-gate 	 */
17437c478bd9Sstevel@tonic-gate 	BGE_DEBUG(("bge_check_serdes: link was %s speed %d duplex %d",
17440c50e2bcSgh 	    UPORDOWN(bgep->param_link_up),
17450c50e2bcSgh 	    bgep->param_link_speed,
17460c50e2bcSgh 	    bgep->param_link_duplex));
17477c478bd9Sstevel@tonic-gate 
17487c478bd9Sstevel@tonic-gate 	if (linkup) {
17497c478bd9Sstevel@tonic-gate 		bgep->param_link_up = B_TRUE;
17507c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = 1000;
17517c478bd9Sstevel@tonic-gate 		if (bgep->param_adv_1000fdx)
17527c478bd9Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_FULL;
17537c478bd9Sstevel@tonic-gate 		else
17547c478bd9Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_HALF;
17557c478bd9Sstevel@tonic-gate 		if (bgep->param_lp_autoneg && !bgep->param_lp_1000fdx)
17567c478bd9Sstevel@tonic-gate 			bgep->param_link_duplex = LINK_DUPLEX_HALF;
17577c478bd9Sstevel@tonic-gate 	} else {
17587c478bd9Sstevel@tonic-gate 		bgep->param_link_up = B_FALSE;
17597c478bd9Sstevel@tonic-gate 		bgep->param_link_speed = 0;
17607c478bd9Sstevel@tonic-gate 		bgep->param_link_duplex = LINK_DUPLEX_UNKNOWN;
17617c478bd9Sstevel@tonic-gate 	}
17627c478bd9Sstevel@tonic-gate 	bgep->link_state = LINK_STATE_UNKNOWN;
17637c478bd9Sstevel@tonic-gate 
1764087a28d1SDavid Gwynne 	bge_log(bgep, "bge_check_serdes: link now %s speed %d duplex %d",
1765087a28d1SDavid Gwynne 	        UPORDOWN(bgep->param_link_up),
1766087a28d1SDavid Gwynne 	        bgep->param_link_speed,
1767087a28d1SDavid Gwynne 	        bgep->param_link_duplex);
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 	return (B_TRUE);
17707c478bd9Sstevel@tonic-gate }
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate static const phys_ops_t serdes_ops = {
17737c478bd9Sstevel@tonic-gate 	bge_restart_serdes,
17747c478bd9Sstevel@tonic-gate 	bge_update_serdes,
17757c478bd9Sstevel@tonic-gate 	bge_check_serdes
17767c478bd9Sstevel@tonic-gate };
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate /*
17797c478bd9Sstevel@tonic-gate  * ========== Exported physical layer control routines ==========
17807c478bd9Sstevel@tonic-gate  */
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate #undef	BGE_DBG
17837c478bd9Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_PHYS	/* debug flag for this code	*/
17847c478bd9Sstevel@tonic-gate 
17857c478bd9Sstevel@tonic-gate /*
17867c478bd9Sstevel@tonic-gate  * Here we have to determine which media we're using (copper or serdes).
17877c478bd9Sstevel@tonic-gate  * Once that's done, we can initialise the physical layer appropriately.
17887c478bd9Sstevel@tonic-gate  */
178900d0963fSdilpreet int
bge_phys_init(bge_t * bgep)17907c478bd9Sstevel@tonic-gate bge_phys_init(bge_t *bgep)
17917c478bd9Sstevel@tonic-gate {
1792087a28d1SDavid Gwynne 	uint32_t regval;
1793087a28d1SDavid Gwynne 
17947c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_init($%p)", (void *)bgep));
17957c478bd9Sstevel@tonic-gate 
17967c478bd9Sstevel@tonic-gate 	mutex_enter(bgep->genlock);
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 	/*
17997c478bd9Sstevel@tonic-gate 	 * Probe for the (internal) PHY.  If it's not there, we'll assume
180010843bc4Sly 	 * that this is a 5703/4S, with a SerDes interface rather than
180110843bc4Sly 	 * a PHY. BCM5714S/BCM5715S are not supported.It are based on
180210843bc4Sly 	 * BCM800x PHY.
18037c478bd9Sstevel@tonic-gate 	 */
18047c478bd9Sstevel@tonic-gate 	bgep->phy_mii_addr = 1;
1805087a28d1SDavid Gwynne 
1806dc3f9a75Syong tan - Sun Microsystems - Beijing China 	if (DEVICE_5717_SERIES_CHIPSETS(bgep)) {
1807087a28d1SDavid Gwynne 		bgep->phy_mii_addr = (bgep->pci_func + 1);
1808dc3f9a75Syong tan - Sun Microsystems - Beijing China 		regval = bge_reg_get32(bgep, SGMII_STATUS_REG);
1809dc3f9a75Syong tan - Sun Microsystems - Beijing China 		if (regval & MEDIA_SELECTION_MODE)
1810087a28d1SDavid Gwynne 			bgep->phy_mii_addr += 7; /* sgmii */
1811dc3f9a75Syong tan - Sun Microsystems - Beijing China 	}
1812dc3f9a75Syong tan - Sun Microsystems - Beijing China 
18137c478bd9Sstevel@tonic-gate 	if (bge_phy_probe(bgep)) {
18147c478bd9Sstevel@tonic-gate 		bgep->chipid.flags &= ~CHIP_FLAG_SERDES;
18157c478bd9Sstevel@tonic-gate 		bgep->physops = &copper_ops;
18167c478bd9Sstevel@tonic-gate 	} else {
18177c478bd9Sstevel@tonic-gate 		bgep->chipid.flags |= CHIP_FLAG_SERDES;
18187c478bd9Sstevel@tonic-gate 		bgep->physops = &serdes_ops;
18197c478bd9Sstevel@tonic-gate 	}
18207c478bd9Sstevel@tonic-gate 
182100d0963fSdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS) {
182200d0963fSdilpreet 		mutex_exit(bgep->genlock);
182300d0963fSdilpreet 		return (EIO);
182400d0963fSdilpreet 	}
182500d0963fSdilpreet 	if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK) {
182600d0963fSdilpreet 		mutex_exit(bgep->genlock);
182700d0963fSdilpreet 		return (EIO);
182800d0963fSdilpreet 	}
18297c478bd9Sstevel@tonic-gate 	mutex_exit(bgep->genlock);
183000d0963fSdilpreet 	return (0);
18317c478bd9Sstevel@tonic-gate }
18327c478bd9Sstevel@tonic-gate 
18337c478bd9Sstevel@tonic-gate /*
18347c478bd9Sstevel@tonic-gate  * Reset the physical layer
18357c478bd9Sstevel@tonic-gate  */
18367c478bd9Sstevel@tonic-gate void
bge_phys_reset(bge_t * bgep)18377c478bd9Sstevel@tonic-gate bge_phys_reset(bge_t *bgep)
18387c478bd9Sstevel@tonic-gate {
18397c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_reset($%p)", (void *)bgep));
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 	mutex_enter(bgep->genlock);
184200d0963fSdilpreet 	if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS)
184300d0963fSdilpreet 		ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED);
184400d0963fSdilpreet 	if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK)
184500d0963fSdilpreet 		ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED);
18467c478bd9Sstevel@tonic-gate 	mutex_exit(bgep->genlock);
18477c478bd9Sstevel@tonic-gate }
18487c478bd9Sstevel@tonic-gate 
18497c478bd9Sstevel@tonic-gate /*
18507c478bd9Sstevel@tonic-gate  * Reset and power off the physical layer.
18517c478bd9Sstevel@tonic-gate  *
18527c478bd9Sstevel@tonic-gate  * Another RESET should get it back to working, but it may take a few
18537c478bd9Sstevel@tonic-gate  * seconds it may take a few moments to return to normal operation ...
18547c478bd9Sstevel@tonic-gate  */
185500d0963fSdilpreet int
bge_phys_idle(bge_t * bgep)18567c478bd9Sstevel@tonic-gate bge_phys_idle(bge_t *bgep)
18577c478bd9Sstevel@tonic-gate {
18587c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_idle($%p)", (void *)bgep));
18597c478bd9Sstevel@tonic-gate 
18607c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
186100d0963fSdilpreet 	return ((*bgep->physops->phys_restart)(bgep, B_TRUE));
18627c478bd9Sstevel@tonic-gate }
18637c478bd9Sstevel@tonic-gate 
18647c478bd9Sstevel@tonic-gate /*
18657c478bd9Sstevel@tonic-gate  * Synchronise the PHYSICAL layer's speed/duplex/autonegotiation capabilities
18667c478bd9Sstevel@tonic-gate  * and advertisements with the required settings as specified by the various
18677c478bd9Sstevel@tonic-gate  * param_* variables that can be poked via the NDD interface.
18687c478bd9Sstevel@tonic-gate  *
18697c478bd9Sstevel@tonic-gate  * We always reset the PHYSICAL layer and reprogram *all* relevant registers.
18707c478bd9Sstevel@tonic-gate  * This is expected to cause the link to go down, and then back up again once
18717c478bd9Sstevel@tonic-gate  * the link is stable and autonegotiation (if enabled) is complete.  We should
18727c478bd9Sstevel@tonic-gate  * get a link state change interrupt somewhere along the way ...
18737c478bd9Sstevel@tonic-gate  *
18747c478bd9Sstevel@tonic-gate  * NOTE: <genlock> must already be held by the caller
18757c478bd9Sstevel@tonic-gate  */
187600d0963fSdilpreet int
bge_phys_update(bge_t * bgep)18777c478bd9Sstevel@tonic-gate bge_phys_update(bge_t *bgep)
18787c478bd9Sstevel@tonic-gate {
18797c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_update($%p)", (void *)bgep));
18807c478bd9Sstevel@tonic-gate 
18817c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
188200d0963fSdilpreet 	return ((*bgep->physops->phys_update)(bgep));
18837c478bd9Sstevel@tonic-gate }
18847c478bd9Sstevel@tonic-gate 
18857c478bd9Sstevel@tonic-gate #undef	BGE_DBG
18867c478bd9Sstevel@tonic-gate #define	BGE_DBG		BGE_DBG_LINK	/* debug flag for this code	*/
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate /*
18897c478bd9Sstevel@tonic-gate  * Read the link status and determine whether anything's changed ...
18907c478bd9Sstevel@tonic-gate  *
18917c478bd9Sstevel@tonic-gate  * This routine should be called whenever the chip flags a change
1892f724721bSzh  * in the hardware link state.
18937c478bd9Sstevel@tonic-gate  *
18947c478bd9Sstevel@tonic-gate  * This routine returns B_FALSE if the link state has not changed,
18957c478bd9Sstevel@tonic-gate  * returns B_TRUE when the change to the new state should be accepted.
18967c478bd9Sstevel@tonic-gate  * In such a case, the param_* variables give the new hardware state,
18977c478bd9Sstevel@tonic-gate  * which the caller should use to update link_state etc.
18987c478bd9Sstevel@tonic-gate  *
18997c478bd9Sstevel@tonic-gate  * The caller must already hold <genlock>
19007c478bd9Sstevel@tonic-gate  */
19017c478bd9Sstevel@tonic-gate boolean_t
bge_phys_check(bge_t * bgep)19027c478bd9Sstevel@tonic-gate bge_phys_check(bge_t *bgep)
19037c478bd9Sstevel@tonic-gate {
19047c478bd9Sstevel@tonic-gate 	BGE_TRACE(("bge_phys_check($%p)", (void *)bgep));
19057c478bd9Sstevel@tonic-gate 
19067c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(bgep->genlock));
19077c478bd9Sstevel@tonic-gate 
1908087a28d1SDavid Gwynne 	/*
1909087a28d1SDavid Gwynne 	 * Force a link recheck if current state is unknown.
1910087a28d1SDavid Gwynne 	 * phys_check() returns TRUE if the link status changed,
1911087a28d1SDavid Gwynne 	 * FALSE otherwise.
1912087a28d1SDavid Gwynne 	 */
1913087a28d1SDavid Gwynne 	return ((*bgep->physops->phys_check)(bgep,
1914087a28d1SDavid Gwynne 	    (bgep->link_state == LINK_STATE_UNKNOWN)));
19157c478bd9Sstevel@tonic-gate }
1916*87a49193SRobert Mustacchi 
1917*87a49193SRobert Mustacchi mac_ether_media_t
bge_phys_media(bge_t * bgep)1918*87a49193SRobert Mustacchi bge_phys_media(bge_t *bgep)
1919*87a49193SRobert Mustacchi {
1920*87a49193SRobert Mustacchi 	switch (bgep->link_state) {
1921*87a49193SRobert Mustacchi 	case LINK_STATE_UP:
1922*87a49193SRobert Mustacchi 		break;
1923*87a49193SRobert Mustacchi 	case LINK_STATE_DOWN:
1924*87a49193SRobert Mustacchi 		return (ETHER_MEDIA_NONE);
1925*87a49193SRobert Mustacchi 	case LINK_STATE_UNKNOWN:
1926*87a49193SRobert Mustacchi 	default:
1927*87a49193SRobert Mustacchi 		return (ETHER_MEDIA_UNKNOWN);
1928*87a49193SRobert Mustacchi 	}
1929*87a49193SRobert Mustacchi 
1930*87a49193SRobert Mustacchi 	if ((bgep->chipid.flags & CHIP_FLAG_SERDES) != 0) {
1931*87a49193SRobert Mustacchi 		switch (bgep->param_link_speed) {
1932*87a49193SRobert Mustacchi 		case 1000:
1933*87a49193SRobert Mustacchi 			return (ETHER_MEDIA_1000BASE_X);
1934*87a49193SRobert Mustacchi 		case 100:
1935*87a49193SRobert Mustacchi 			return (ETHER_MEDIA_100BASE_FX);
1936*87a49193SRobert Mustacchi 		default:
1937*87a49193SRobert Mustacchi 			break;
1938*87a49193SRobert Mustacchi 		}
1939*87a49193SRobert Mustacchi 	} else {
1940*87a49193SRobert Mustacchi 		switch (bgep->param_link_speed) {
1941*87a49193SRobert Mustacchi 		case 1000:
1942*87a49193SRobert Mustacchi 			return (ETHER_MEDIA_1000BASE_T);
1943*87a49193SRobert Mustacchi 		case 100:
1944*87a49193SRobert Mustacchi 			/*
1945*87a49193SRobert Mustacchi 			 * All NetExtreme I docs we can find suggest that the
1946*87a49193SRobert Mustacchi 			 * PHY never supported anything other than 100BASE-TX so
1947*87a49193SRobert Mustacchi 			 * we don't further interrogate the device.
1948*87a49193SRobert Mustacchi 			 */
1949*87a49193SRobert Mustacchi 			return (ETHER_MEDIA_100BASE_TX);
1950*87a49193SRobert Mustacchi 		case 10:
1951*87a49193SRobert Mustacchi 			return (ETHER_MEDIA_10BASE_T);
1952*87a49193SRobert Mustacchi 		default:
1953*87a49193SRobert Mustacchi 			break;
1954*87a49193SRobert Mustacchi 		}
1955*87a49193SRobert Mustacchi 	}
1956*87a49193SRobert Mustacchi 
1957*87a49193SRobert Mustacchi 	return (ETHER_MEDIA_UNKNOWN);
1958*87a49193SRobert Mustacchi }
1959