xref: /onnv-gate/usr/src/uts/common/io/nge/nge_xmii.c (revision 10496:3e884efee5be)
15578Smx205022 /*
25578Smx205022  * CDDL HEADER START
35578Smx205022  *
45578Smx205022  * The contents of this file are subject to the terms of the
55578Smx205022  * Common Development and Distribution License (the "License").
65578Smx205022  * You may not use this file except in compliance with the License.
75578Smx205022  *
85578Smx205022  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95578Smx205022  * or http://www.opensolaris.org/os/licensing.
105578Smx205022  * See the License for the specific language governing permissions
115578Smx205022  * and limitations under the License.
125578Smx205022  *
135578Smx205022  * When distributing Covered Code, include this CDDL HEADER in each
145578Smx205022  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155578Smx205022  * If applicable, add the following below this CDDL HEADER, with the
165578Smx205022  * fields enclosed by brackets "[]" replaced with your own identifying
175578Smx205022  * information: Portions Copyright [yyyy] [name of copyright owner]
185578Smx205022  *
195578Smx205022  * CDDL HEADER END
205578Smx205022  */
215578Smx205022 
225574Smx205022 /*
23*9860Sgdamore@opensolaris.org  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
245574Smx205022  * Use is subject to license terms.
255574Smx205022  */
265574Smx205022 
275574Smx205022 #include "nge.h"
285574Smx205022 
295574Smx205022 #undef	NGE_DBG
305574Smx205022 #define	NGE_DBG		NGE_DBG_MII	/* debug flag for this code	*/
315574Smx205022 
325574Smx205022 /*
335574Smx205022  * The arrays below can be indexed by the MODE bits from the mac2phy
345574Smx205022  * register to determine the current speed/duplex settings.
355574Smx205022  */
365574Smx205022 static const int16_t nge_copper_link_speed[] = {
375574Smx205022 	0,				/* MII_AUX_STATUS_MODE_NONE	*/
385574Smx205022 	10,				/* MII_AUX_STAT0,US_MODE_10	*/
395574Smx205022 	100,				/* MII_AUX_STAT0,US_MODE_100	*/
405574Smx205022 	1000,				/* MII_AUX_STAT0,US_MODE_1000	*/
415574Smx205022 };
425574Smx205022 
435574Smx205022 static const int8_t nge_copper_link_duplex[] = {
445574Smx205022 	LINK_DUPLEX_UNKNOWN,		/* MII_DUPLEX_NONE	*/
455574Smx205022 	LINK_DUPLEX_HALF,		/* MII_DUPLEX_HALF	*/
465574Smx205022 	LINK_DUPLEX_FULL,		/* MII_DUPLEX_FULL	*/
475574Smx205022 };
485574Smx205022 
495574Smx205022 
505574Smx205022 static uint16_t nge_mii_access(nge_t *ngep, nge_regno_t regno,
515574Smx205022     uint16_t data, uint32_t cmd);
525574Smx205022 #pragma	inline(nge_mii_access)
535574Smx205022 
545574Smx205022 static uint16_t
nge_mii_access(nge_t * ngep,nge_regno_t regno,uint16_t data,uint32_t cmd)555574Smx205022 nge_mii_access(nge_t *ngep, nge_regno_t regno, uint16_t data, uint32_t cmd)
565574Smx205022 {
575574Smx205022 	uint16_t tries;
585574Smx205022 	uint16_t mdio_data;
595574Smx205022 	nge_mdio_adr mdio_adr;
605574Smx205022 	nge_mintr_src intr_src;
615574Smx205022 
625574Smx205022 	NGE_TRACE(("nge_mii_access($%p, 0x%lx, 0x%x, 0x%x)",
635574Smx205022 	    (void *)ngep, regno, data, cmd));
645574Smx205022 
655574Smx205022 	/*
665574Smx205022 	 * Clear the privous interrupt event
675574Smx205022 	 */
685574Smx205022 	intr_src.src_val = nge_reg_get8(ngep, NGE_MINTR_SRC);
695574Smx205022 	nge_reg_put8(ngep, NGE_MINTR_SRC, intr_src.src_val);
705574Smx205022 
715574Smx205022 	/*
725574Smx205022 	 * Check whether the current operation has been finished
735574Smx205022 	 */
745574Smx205022 	mdio_adr.adr_val = nge_reg_get16(ngep, NGE_MDIO_ADR);
755574Smx205022 	for (tries = 0; tries < 30; tries ++) {
765574Smx205022 		if (mdio_adr.adr_bits.mdio_clc == NGE_CLEAR)
775574Smx205022 			break;
785574Smx205022 		drv_usecwait(10);
795574Smx205022 		mdio_adr.adr_val = nge_reg_get16(ngep, NGE_MDIO_ADR);
805574Smx205022 	}
815574Smx205022 
825574Smx205022 	/*
835574Smx205022 	 * The current operation can not be finished successfully
845574Smx205022 	 *  The driver should halt the current operation
855574Smx205022 	 */
865574Smx205022 	if (tries == 30) {
875574Smx205022 		mdio_adr.adr_bits.mdio_clc = NGE_SET;
885574Smx205022 		nge_reg_put16(ngep, NGE_MDIO_ADR, mdio_adr.adr_val);
895574Smx205022 		drv_usecwait(100);
905574Smx205022 	}
915574Smx205022 
925574Smx205022 	/*
935574Smx205022 	 * Assemble the operation cmd
945574Smx205022 	 */
957781SMin.Xu@Sun.COM 	mdio_adr.adr_bits.phy_reg = (uint16_t)regno;
965574Smx205022 	mdio_adr.adr_bits.phy_adr = ngep->phy_xmii_addr;
975574Smx205022 	mdio_adr.adr_bits.mdio_rw = (cmd == NGE_MDIO_WRITE) ? 1 : 0;
985574Smx205022 
995574Smx205022 
1005574Smx205022 	if (cmd == NGE_MDIO_WRITE)
1015574Smx205022 		nge_reg_put16(ngep, NGE_MDIO_DATA, data);
1025574Smx205022 
1035574Smx205022 	nge_reg_put16(ngep, NGE_MDIO_ADR, mdio_adr.adr_val);
1045574Smx205022 
1055574Smx205022 	/*
1065574Smx205022 	 * To check whether the read/write operation is finished
1075574Smx205022 	 */
1085574Smx205022 	for (tries = 0; tries < 300; tries ++) {
1095574Smx205022 		drv_usecwait(10);
1105574Smx205022 		mdio_adr.adr_val = nge_reg_get16(ngep, NGE_MDIO_ADR);
1115574Smx205022 		if (mdio_adr.adr_bits.mdio_clc == NGE_CLEAR)
1125574Smx205022 			break;
1135574Smx205022 	}
1145574Smx205022 	if (tries == 300)
1155574Smx205022 		return ((uint16_t)~0);
1165574Smx205022 
1175574Smx205022 	/*
1185574Smx205022 	 * Read the data from MDIO data register
1195574Smx205022 	 */
1205574Smx205022 	if (cmd == NGE_MDIO_READ)
1215574Smx205022 		mdio_data = nge_reg_get16(ngep, NGE_MDIO_DATA);
1225574Smx205022 
1235574Smx205022 	/*
1245574Smx205022 	 * To check whether the read/write operation is valid
1255574Smx205022 	 */
1265574Smx205022 	intr_src.src_val = nge_reg_get8(ngep, NGE_MINTR_SRC);
1275574Smx205022 	nge_reg_put8(ngep, NGE_MINTR_SRC, intr_src.src_val);
1285574Smx205022 	if (intr_src.src_bits.mrei == NGE_SET)
1295574Smx205022 		return ((uint16_t)~0);
1305574Smx205022 
1315574Smx205022 	return (mdio_data);
1325574Smx205022 }
1335574Smx205022 
1345574Smx205022 uint16_t nge_mii_get16(nge_t *ngep, nge_regno_t regno);
1355574Smx205022 #pragma	inline(nge_mii_get16)
1365574Smx205022 
1375574Smx205022 uint16_t
nge_mii_get16(nge_t * ngep,nge_regno_t regno)1385574Smx205022 nge_mii_get16(nge_t *ngep, nge_regno_t regno)
1395574Smx205022 {
1405574Smx205022 
1415574Smx205022 	return (nge_mii_access(ngep, regno, 0, NGE_MDIO_READ));
1425574Smx205022 }
1435574Smx205022 
1445574Smx205022 void nge_mii_put16(nge_t *ngep, nge_regno_t regno, uint16_t data);
1455574Smx205022 #pragma	inline(nge_mii_put16)
1465574Smx205022 
1475574Smx205022 void
nge_mii_put16(nge_t * ngep,nge_regno_t regno,uint16_t data)1485574Smx205022 nge_mii_put16(nge_t *ngep, nge_regno_t regno, uint16_t data)
1495574Smx205022 {
1505574Smx205022 
1515574Smx205022 	(void) nge_mii_access(ngep, regno, data, NGE_MDIO_WRITE);
1525574Smx205022 }
1535574Smx205022 
1545574Smx205022 /*
1555574Smx205022  * Basic low-level function to probe for a PHY
1565574Smx205022  *
1575574Smx205022  * Returns TRUE if the PHY responds with valid data, FALSE otherwise
1585574Smx205022  */
1595574Smx205022 static boolean_t
nge_phy_probe(nge_t * ngep)1605574Smx205022 nge_phy_probe(nge_t *ngep)
1615574Smx205022 {
1625574Smx205022 	int i;
1635574Smx205022 	uint16_t phy_status;
1645574Smx205022 	uint16_t phyidh;
1655574Smx205022 	uint16_t phyidl;
1665574Smx205022 
1675574Smx205022 	NGE_TRACE(("nge_phy_probe($%p)", (void *)ngep));
1685574Smx205022 
1695574Smx205022 	/*
1705574Smx205022 	 * Scan the phys to find the right address
1715574Smx205022 	 * of the phy
1725574Smx205022 	 *
1735574Smx205022 	 * Probe maximum for 32 phy addresses
1745574Smx205022 	 */
1755574Smx205022 	for (i = 0; i < NGE_PHY_NUMBER; i++) {
1765574Smx205022 		ngep->phy_xmii_addr = i;
1775574Smx205022 		/*
1785574Smx205022 		 * Read the MII_STATUS register twice, in
1795574Smx205022 		 * order to clear any sticky bits (but they should
1805574Smx205022 		 * have been cleared by the RESET, I think).
1815574Smx205022 		 */
1825574Smx205022 		phy_status = nge_mii_get16(ngep, MII_STATUS);
1835574Smx205022 		phy_status = nge_mii_get16(ngep, MII_STATUS);
1845574Smx205022 		if (phy_status != 0xffff) {
1855574Smx205022 			phyidh = nge_mii_get16(ngep, MII_PHYIDH);
1865574Smx205022 			phyidl = nge_mii_get16(ngep, MII_PHYIDL);
1875574Smx205022 			ngep->phy_id =
1885574Smx205022 			    (((uint32_t)phyidh << 16) |(phyidl & MII_IDL_MASK));
1895574Smx205022 			NGE_DEBUG(("nge_phy_probe: status 0x%x, phy id 0x%x",
1905574Smx205022 			    phy_status, ngep->phy_id));
1915574Smx205022 
1925574Smx205022 			return (B_TRUE);
1935574Smx205022 		}
1945574Smx205022 	}
1955574Smx205022 
1965574Smx205022 	return (B_FALSE);
1975574Smx205022 }
1985574Smx205022 
1995574Smx205022 
2005574Smx205022 /*
2015574Smx205022  * Basic low-level function to powerup the phy and remove the isolation
2025574Smx205022  */
2035574Smx205022 
2045574Smx205022 static boolean_t
nge_phy_recover(nge_t * ngep)2055574Smx205022 nge_phy_recover(nge_t *ngep)
2065574Smx205022 {
2075574Smx205022 	uint16_t control;
2085574Smx205022 	uint16_t count;
2095574Smx205022 
2105574Smx205022 	NGE_TRACE(("nge_phy_recover($%p)", (void *)ngep));
2115574Smx205022 	control = nge_mii_get16(ngep, MII_CONTROL);
2125574Smx205022 	control &= ~(MII_CONTROL_PWRDN | MII_CONTROL_ISOLATE);
2135574Smx205022 	nge_mii_put16(ngep, MII_CONTROL, control);
2145574Smx205022 	for (count = 0; ++count < 10; ) {
2155574Smx205022 		drv_usecwait(5);
2165574Smx205022 		control = nge_mii_get16(ngep, MII_CONTROL);
2175574Smx205022 		if (BIC(control, MII_CONTROL_PWRDN))
2185574Smx205022 			return (B_TRUE);
2195574Smx205022 	}
2205574Smx205022 
2215574Smx205022 	return (B_FALSE);
2225574Smx205022 }
2235574Smx205022 /*
2245574Smx205022  * Basic low-level function to reset the PHY.
2255574Smx205022  * Doesn't incorporate any special-case workarounds.
2265574Smx205022  *
2275574Smx205022  * Returns TRUE on success, FALSE if the RESET bit doesn't clear
2285574Smx205022  */
2295574Smx205022 boolean_t
nge_phy_reset(nge_t * ngep)2305574Smx205022 nge_phy_reset(nge_t *ngep)
2315574Smx205022 {
2325574Smx205022 	uint16_t control;
2335574Smx205022 	uint_t count;
2345574Smx205022 
2355574Smx205022 	NGE_TRACE(("nge_phy_reset($%p)", (void *)ngep));
2365574Smx205022 
2375574Smx205022 	ASSERT(mutex_owned(ngep->genlock));
2385574Smx205022 
2395574Smx205022 	/*
2405574Smx205022 	 * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear
2415574Smx205022 	 */
2425574Smx205022 	control = nge_mii_get16(ngep, MII_CONTROL);
2435574Smx205022 	control |= MII_CONTROL_RESET;
2445574Smx205022 	nge_mii_put16(ngep, MII_CONTROL, control);
2456366Smx205022 	/* We should wait for 500ms. It's defined in the manual */
2466397Smx205022 	delay(drv_usectohz(500000));
2475574Smx205022 	for (count = 0; ++count < 10; ) {
2485574Smx205022 		drv_usecwait(5);
2495574Smx205022 		control = nge_mii_get16(ngep, MII_CONTROL);
2505574Smx205022 		if (BIC(control, MII_CONTROL_RESET))
2515574Smx205022 			return (B_TRUE);
2525574Smx205022 	}
2535574Smx205022 	NGE_DEBUG(("nge_phy_reset: FAILED, control now 0x%x", control));
2545574Smx205022 
2555574Smx205022 	return (B_FALSE);
2565574Smx205022 }
2575574Smx205022 
2586366Smx205022 static boolean_t
nge_phy_restart(nge_t * ngep)2595574Smx205022 nge_phy_restart(nge_t *ngep)
2605574Smx205022 {
2615574Smx205022 	uint16_t mii_reg;
2625574Smx205022 
2636366Smx205022 	if (!nge_phy_recover(ngep))
2646366Smx205022 		return (B_FALSE);
2656366Smx205022 	if (!nge_phy_reset(ngep))
2666366Smx205022 		return (B_FALSE);
2676366Smx205022 
268*9860Sgdamore@opensolaris.org 	if (MII_PHY_MFG(ngep->phy_id) == MII_ID_CICADA) {
2695574Smx205022 		if (ngep->phy_mode == RGMII_IN) {
2705574Smx205022 			mii_reg = nge_mii_get16(ngep,
2715574Smx205022 			    MII_CICADA_EXT_CONTROL);
2725574Smx205022 			mii_reg &= ~(MII_CICADA_MODE_SELECT_BITS
2735574Smx205022 			    | MII_CICADA_POWER_SUPPLY_BITS);
2745574Smx205022 			mii_reg |= (MII_CICADA_MODE_SELECT_RGMII
2755574Smx205022 			    | MII_CICADA_POWER_SUPPLY_2_5V);
2765574Smx205022 			nge_mii_put16(ngep, MII_CICADA_EXT_CONTROL, mii_reg);
2775574Smx205022 
2785574Smx205022 			mii_reg = nge_mii_get16(ngep,
2795574Smx205022 			    MII_CICADA_AUXCTRL_STATUS);
2805574Smx205022 			mii_reg |= MII_CICADA_PIN_PRORITY_SETTING;
2815574Smx205022 			nge_mii_put16(ngep, MII_CICADA_AUXCTRL_STATUS,
2825574Smx205022 			    mii_reg);
2835574Smx205022 		} else {
2845574Smx205022 			mii_reg = nge_mii_get16(ngep,
2855574Smx205022 			    MII_CICADA_10BASET_CONTROL);
2865574Smx205022 			mii_reg |= MII_CICADA_DISABLE_ECHO_MODE;
2875574Smx205022 			nge_mii_put16(ngep,
2885574Smx205022 			    MII_CICADA_10BASET_CONTROL, mii_reg);
2895574Smx205022 
2905574Smx205022 			mii_reg = nge_mii_get16(ngep,
2915574Smx205022 			    MII_CICADA_BYPASS_CONTROL);
2925574Smx205022 			mii_reg &= (~CICADA_125MHZ_CLOCK_ENABLE);
2935574Smx205022 			nge_mii_put16(ngep, MII_CICADA_BYPASS_CONTROL, mii_reg);
2945574Smx205022 		}
2955574Smx205022 	}
2966366Smx205022 
2976366Smx205022 	return (B_TRUE);
2985574Smx205022 }
2995574Smx205022 
3005574Smx205022 /*
3015574Smx205022  * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities
3025574Smx205022  * and advertisements with the required settings as specified by the various
3035574Smx205022  * param_* variables that can be poked via the NDD interface.
3045574Smx205022  *
3055574Smx205022  * We always reset the PHY and reprogram *all* the relevant registers,
3065574Smx205022  * not just those changed.  This should cause the link to go down, and then
3075574Smx205022  * back up again once the link is stable and autonegotiation (if enabled)
3085574Smx205022  * is complete.  We should get a link state change interrupt somewhere along
3095574Smx205022  * the way ...
3105574Smx205022  *
3115574Smx205022  * NOTE: <genlock> must already be held by the caller
3125574Smx205022  */
3135574Smx205022 static void
nge_update_copper(nge_t * ngep)3145574Smx205022 nge_update_copper(nge_t *ngep)
3155574Smx205022 {
3165574Smx205022 	uint16_t control;
3175574Smx205022 	uint16_t gigctrl;
3185574Smx205022 	uint16_t anar;
3195574Smx205022 	boolean_t adv_autoneg;
3205574Smx205022 	boolean_t adv_pause;
3215574Smx205022 	boolean_t adv_asym_pause;
3225574Smx205022 	boolean_t adv_1000fdx;
3235574Smx205022 	boolean_t adv_100fdx;
3245574Smx205022 	boolean_t adv_100hdx;
3255574Smx205022 	boolean_t adv_10fdx;
3265574Smx205022 	boolean_t adv_10hdx;
3275574Smx205022 
3285574Smx205022 	NGE_TRACE(("nge_update_copper($%p)", (void *)ngep));
3295574Smx205022 
3305574Smx205022 	ASSERT(mutex_owned(ngep->genlock));
3315574Smx205022 
3325574Smx205022 	NGE_DEBUG(("nge_update_copper: autoneg %d "
3335574Smx205022 	    "pause %d asym_pause %d "
3345574Smx205022 	    "1000fdx %d "
3355574Smx205022 	    "100fdx %d 100hdx %d "
3365574Smx205022 	    "10fdx %d 10hdx %d ",
3375574Smx205022 	    ngep->param_adv_autoneg,
3385574Smx205022 	    ngep->param_adv_pause, ngep->param_adv_asym_pause,
3395574Smx205022 	    ngep->param_adv_1000fdx,
3405574Smx205022 	    ngep->param_adv_100fdx, ngep->param_adv_100hdx,
3415574Smx205022 	    ngep->param_adv_10fdx, ngep->param_adv_10hdx));
3425574Smx205022 
3435574Smx205022 	control = anar = gigctrl = 0;
3445574Smx205022 
3455574Smx205022 	/*
3465574Smx205022 	 * PHY settings are normally based on the param_* variables,
3475574Smx205022 	 * but if any loopback mode is in effect, that takes precedence.
3485574Smx205022 	 *
3495574Smx205022 	 * NGE supports MAC-internal loopback, PHY-internal loopback,
3505574Smx205022 	 * and External loopback at a variety of speeds (with a special
3515574Smx205022 	 * cable).  In all cases, autoneg is turned OFF, full-duplex
3525574Smx205022 	 * is turned ON, and the speed/mastership is forced.
3535574Smx205022 	 */
3545574Smx205022 	switch (ngep->param_loop_mode) {
3555574Smx205022 	case NGE_LOOP_NONE:
3565574Smx205022 	default:
3575574Smx205022 		adv_pause = ngep->param_adv_pause;
3585574Smx205022 		adv_autoneg = ngep->param_adv_autoneg;
3595574Smx205022 		adv_asym_pause = ngep->param_adv_asym_pause;
3605574Smx205022 		if (ngep->phy_mode == MII_IN) {
3615574Smx205022 			adv_1000fdx = ngep->param_adv_1000fdx = B_FALSE;
3625574Smx205022 		}
3635574Smx205022 		adv_1000fdx = ngep->param_adv_1000fdx;
3645574Smx205022 		adv_100fdx = ngep->param_adv_100fdx;
3655574Smx205022 		adv_100hdx = ngep->param_adv_100hdx;
3665574Smx205022 		adv_10fdx = ngep->param_adv_10fdx;
3675574Smx205022 		adv_10hdx = ngep->param_adv_10hdx;
3685574Smx205022 
3695574Smx205022 		break;
3705574Smx205022 
3715574Smx205022 	case NGE_LOOP_EXTERNAL_100:
3725574Smx205022 	case NGE_LOOP_EXTERNAL_10:
3735574Smx205022 	case NGE_LOOP_INTERNAL_PHY:
3745574Smx205022 		adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
3755574Smx205022 		adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE;
3765574Smx205022 		adv_100hdx = adv_10hdx = B_FALSE;
3775574Smx205022 		ngep->param_link_duplex = LINK_DUPLEX_FULL;
3785574Smx205022 
3795574Smx205022 		switch (ngep->param_loop_mode) {
3805574Smx205022 		case NGE_LOOP_EXTERNAL_100:
3815574Smx205022 			ngep->param_link_speed = 100;
3825574Smx205022 			adv_100fdx = B_TRUE;
3835574Smx205022 			break;
3845574Smx205022 
3855574Smx205022 		case NGE_LOOP_EXTERNAL_10:
3865574Smx205022 			ngep->param_link_speed = 10;
3875574Smx205022 			adv_10fdx = B_TRUE;
3885574Smx205022 			break;
3895574Smx205022 
3905574Smx205022 		case NGE_LOOP_INTERNAL_PHY:
3915574Smx205022 			ngep->param_link_speed = 1000;
3925574Smx205022 			adv_1000fdx = B_TRUE;
3935574Smx205022 			break;
3945574Smx205022 
3955574Smx205022 		}
3965574Smx205022 	}
3975574Smx205022 	NGE_DEBUG(("nge_update_copper: autoneg %d "
3985574Smx205022 	    "pause %d asym_pause %d "
3995574Smx205022 	    "1000fdx %d "
4005574Smx205022 	    "100fdx %d 100hdx %d "
4015574Smx205022 	    "10fdx %d 10hdx %d ",
4025574Smx205022 	    adv_autoneg,
4035574Smx205022 	    adv_pause, adv_asym_pause,
4045574Smx205022 	    adv_1000fdx,
4055574Smx205022 	    adv_100fdx, adv_100hdx,
4065574Smx205022 	    adv_10fdx, adv_10hdx));
4075574Smx205022 
4085574Smx205022 	/*
4095574Smx205022 	 * We should have at least one technology capability set;
4105574Smx205022 	 * if not, we select a default of 10Mb/s half-duplex
4115574Smx205022 	 */
4125574Smx205022 	if (!adv_1000fdx && !adv_100fdx && !adv_10fdx &&
4135574Smx205022 	    !adv_100hdx && !adv_10hdx)
4145574Smx205022 		adv_10hdx = B_TRUE;
4155574Smx205022 
4165574Smx205022 	/*
4175574Smx205022 	 * Now transform the adv_* variables into the proper settings
4185574Smx205022 	 * of the PHY registers ...
4195574Smx205022 	 *
4205574Smx205022 	 * If autonegotiation is (now) enabled, we want to trigger
4215574Smx205022 	 * a new autonegotiation cycle once the PHY has been
4225574Smx205022 	 * programmed with the capabilities to be advertised.
4235574Smx205022 	 */
4245574Smx205022 	if (adv_autoneg)
4255574Smx205022 		control |= MII_CONTROL_ANE|MII_CONTROL_RSAN;
4265574Smx205022 
4275574Smx205022 	if (adv_1000fdx)
4285574Smx205022 		control |= MII_CONTROL_1000MB|MII_CONTROL_FDUPLEX;
4295574Smx205022 	else if (adv_100fdx)
4305574Smx205022 		control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX;
4315574Smx205022 	else if (adv_100hdx)
4325574Smx205022 		control |= MII_CONTROL_100MB;
4335574Smx205022 	else if (adv_10fdx)
4345574Smx205022 		control |= MII_CONTROL_FDUPLEX;
4355574Smx205022 	else if (adv_10hdx)
4365574Smx205022 		control |= 0;
4375574Smx205022 	else
4385574Smx205022 		{ _NOTE(EMPTY); }	/* Can't get here anyway ...	*/
4395574Smx205022 
4405574Smx205022 	if (adv_1000fdx)
4415574Smx205022 		gigctrl |= MII_1000BT_CTL_ADV_FDX;
4425574Smx205022 	if (adv_100fdx)
4435574Smx205022 		anar |= MII_ABILITY_100BASE_TX_FD;
4445574Smx205022 	if (adv_100hdx)
4455574Smx205022 		anar |= MII_ABILITY_100BASE_TX;
4465574Smx205022 	if (adv_10fdx)
4475574Smx205022 		anar |= MII_ABILITY_10BASE_T_FD;
4485574Smx205022 	if (adv_10hdx)
4495574Smx205022 		anar |= MII_ABILITY_10BASE_T;
4505574Smx205022 
4515574Smx205022 	if (adv_pause)
4525574Smx205022 		anar |= MII_ABILITY_PAUSE;
4535574Smx205022 	if (adv_asym_pause)
454*9860Sgdamore@opensolaris.org 		anar |= MII_ABILITY_ASMPAUSE;
4555574Smx205022 
4565574Smx205022 	/*
4575574Smx205022 	 * Munge in any other fixed bits we require ...
4585574Smx205022 	 */
4595574Smx205022 	anar |= MII_AN_SELECTOR_8023;
4605574Smx205022 
4615574Smx205022 	/*
4625574Smx205022 	 * Restart the PHY and write the new values.
4635574Smx205022 	 */
4645574Smx205022 	nge_mii_put16(ngep, MII_AN_ADVERT, anar);
4655574Smx205022 	nge_mii_put16(ngep, MII_CONTROL, control);
4665574Smx205022 	nge_mii_put16(ngep, MII_1000BASE_T_CONTROL, gigctrl);
4676366Smx205022 	if (!nge_phy_restart(ngep))
4686366Smx205022 		nge_error(ngep, "nge_update_copper: failed to restart phy");
4695574Smx205022 	/*
4705574Smx205022 	 * Loopback bit in control register is not reset sticky
4715574Smx205022 	 * write it after PHY restart.
4725574Smx205022 	 */
4735574Smx205022 	if (ngep->param_loop_mode == NGE_LOOP_INTERNAL_PHY) {
4745574Smx205022 		control = nge_mii_get16(ngep, MII_CONTROL);
4755574Smx205022 		control |= MII_CONTROL_LOOPBACK;
4765574Smx205022 		nge_mii_put16(ngep, MII_CONTROL, control);
4775574Smx205022 	}
4785574Smx205022 }
4795574Smx205022 
4805574Smx205022 static boolean_t
nge_check_copper(nge_t * ngep)4815574Smx205022 nge_check_copper(nge_t *ngep)
4825574Smx205022 {
4835574Smx205022 	uint16_t mii_status;
4845574Smx205022 	uint16_t mii_exstatus;
4855574Smx205022 	uint16_t mii_excontrol;
4865574Smx205022 	uint16_t anar;
4875574Smx205022 	uint16_t lpan;
4885574Smx205022 	uint_t speed;
4895574Smx205022 	uint_t duplex;
4905574Smx205022 	boolean_t linkup;
4915574Smx205022 	nge_mii_cs mii_cs;
4925574Smx205022 	nge_mintr_src mintr_src;
4935574Smx205022 
4945574Smx205022 	speed = UNKOWN_SPEED;
4955574Smx205022 	duplex = UNKOWN_DUPLEX;
4965574Smx205022 	/*
4975574Smx205022 	 * Read the status from the PHY (which is self-clearing
4985574Smx205022 	 * on read!); also read & clear the main (Ethernet) MAC status
4995574Smx205022 	 * (the relevant bits of this are write-one-to-clear).
5005574Smx205022 	 */
5015574Smx205022 	mii_status = nge_mii_get16(ngep, MII_STATUS);
5025574Smx205022 	mii_cs.cs_val = nge_reg_get32(ngep, NGE_MII_CS);
5035574Smx205022 	mintr_src.src_val = nge_reg_get32(ngep, NGE_MINTR_SRC);
5045574Smx205022 	nge_reg_put32(ngep, NGE_MINTR_SRC, mintr_src.src_val);
5055574Smx205022 
5065574Smx205022 	NGE_DEBUG(("nge_check_copper: link %d/%s, MII status 0x%x "
5075574Smx205022 	    "(was 0x%x)", ngep->link_state,
5085574Smx205022 	    UPORDOWN(ngep->param_link_up), mii_status,
5095574Smx205022 	    ngep->phy_gen_status));
5105574Smx205022 
5115574Smx205022 	do {
5125574Smx205022 		/*
5135574Smx205022 		 * If the PHY status changed, record the time
5145574Smx205022 		 */
5155574Smx205022 		switch (ngep->phy_mode) {
5165574Smx205022 		default:
5175574Smx205022 		case RGMII_IN:
5185574Smx205022 
5195574Smx205022 			/*
5205574Smx205022 			 * Judge the giga speed by reading control
5215574Smx205022 			 * and status register
5225574Smx205022 			 */
5235574Smx205022 			mii_excontrol = nge_mii_get16(ngep,
5245574Smx205022 			    MII_1000BASE_T_CONTROL);
5255574Smx205022 			mii_exstatus = nge_mii_get16(ngep,
5265574Smx205022 			    MII_1000BASE_T_STATUS);
5275574Smx205022 			if ((mii_excontrol & MII_1000BT_CTL_ADV_FDX) &&
5285574Smx205022 			    (mii_exstatus & MII_1000BT_STAT_LP_FDX_CAP)) {
5295574Smx205022 				speed  = NGE_1000M;
5305574Smx205022 				duplex = NGE_FD;
5315574Smx205022 			} else {
5325574Smx205022 				anar = nge_mii_get16(ngep, MII_AN_ADVERT);
5335574Smx205022 				lpan = nge_mii_get16(ngep, MII_AN_LPABLE);
5345574Smx205022 				if (lpan != 0)
5355574Smx205022 					anar = (anar & lpan);
5365574Smx205022 				if (anar & MII_100BASET_FD) {
5375574Smx205022 					speed = NGE_100M;
5385574Smx205022 					duplex = NGE_FD;
5395574Smx205022 				} else if (anar & MII_100BASET_HD) {
5405574Smx205022 					speed = NGE_100M;
5415574Smx205022 					duplex = NGE_HD;
5425574Smx205022 				} else if (anar & MII_10BASET_FD) {
5435574Smx205022 					speed = NGE_10M;
5445574Smx205022 					duplex = NGE_FD;
5455574Smx205022 				} else if (anar & MII_10BASET_HD) {
5465574Smx205022 					speed = NGE_10M;
5475574Smx205022 					duplex = NGE_HD;
5485574Smx205022 				}
5495574Smx205022 			}
5505574Smx205022 			break;
5515574Smx205022 		case MII_IN:
5525574Smx205022 			anar = nge_mii_get16(ngep, MII_AN_ADVERT);
5535574Smx205022 			lpan = nge_mii_get16(ngep, MII_AN_LPABLE);
5545574Smx205022 			if (lpan != 0)
5555574Smx205022 				anar = (anar & lpan);
5565574Smx205022 
5575574Smx205022 			if (anar & MII_100BASET_FD) {
5585574Smx205022 				speed = NGE_100M;
5595574Smx205022 				duplex = NGE_FD;
5605574Smx205022 			} else if (anar & MII_100BASET_HD) {
5615574Smx205022 				speed = NGE_100M;
5625574Smx205022 				duplex = NGE_HD;
5635574Smx205022 			} else if (anar & MII_10BASET_FD) {
5645574Smx205022 				speed = NGE_10M;
5655574Smx205022 				duplex = NGE_FD;
5665574Smx205022 			} else if (anar & MII_10BASET_HD) {
5675574Smx205022 				speed = NGE_10M;
5685574Smx205022 				duplex = NGE_HD;
5695574Smx205022 			}
5705574Smx205022 			break;
5715574Smx205022 		}
5725574Smx205022 
5735574Smx205022 
5745574Smx205022 		/*
5755574Smx205022 		 * We will only consider the link UP if all the readings
5765574Smx205022 		 * are consistent and give meaningful results ...
5775574Smx205022 		 */
5785574Smx205022 		linkup = nge_copper_link_speed[speed] > 0;
5795574Smx205022 		linkup &= nge_copper_link_duplex[duplex] != LINK_DUPLEX_UNKNOWN;
5805574Smx205022 		linkup &= BIS(mii_status, MII_STATUS_LINKUP);
5815574Smx205022 		linkup &= BIS(mii_cs.cs_val, MII_STATUS_LINKUP);
5825574Smx205022 
5835574Smx205022 		/*
5845574Smx205022 		 * Record current register values, then reread status
5855574Smx205022 		 * register & loop until it stabilises ...
5865574Smx205022 		 */
5875574Smx205022 		ngep->phy_gen_status = mii_status;
5885574Smx205022 		mii_status = nge_mii_get16(ngep, MII_STATUS);
5895574Smx205022 	} while (mii_status != ngep->phy_gen_status);
5905574Smx205022 
5915574Smx205022 	/* Get the Link Partner Ability */
5925574Smx205022 	mii_exstatus = nge_mii_get16(ngep, MII_1000BASE_T_STATUS);
5935574Smx205022 	lpan = nge_mii_get16(ngep, MII_AN_LPABLE);
5945574Smx205022 	if (mii_exstatus & MII_1000BT_STAT_LP_FDX_CAP) {
5955574Smx205022 		ngep->param_lp_autoneg = B_TRUE;
5965574Smx205022 		ngep->param_link_autoneg = B_TRUE;
5975574Smx205022 		ngep->param_lp_1000fdx = B_TRUE;
5985574Smx205022 	}
5995574Smx205022 	if (mii_exstatus & MII_1000BT_STAT_LP_HDX_CAP) {
6005574Smx205022 		ngep->param_lp_autoneg = B_TRUE;
6015574Smx205022 		ngep->param_link_autoneg = B_TRUE;
6025574Smx205022 		ngep->param_lp_1000hdx = B_TRUE;
6035574Smx205022 	}
6045574Smx205022 	if (lpan & MII_100BASET_FD)
6055574Smx205022 		ngep->param_lp_100fdx = B_TRUE;
6065574Smx205022 	if (lpan & MII_100BASET_HD)
6075574Smx205022 		ngep->param_lp_100hdx = B_TRUE;
6085574Smx205022 	if (lpan & MII_10BASET_FD)
6095574Smx205022 		ngep->param_lp_10fdx = B_TRUE;
6105574Smx205022 	if (lpan & MII_10BASET_HD)
6115574Smx205022 		ngep->param_lp_10hdx = B_TRUE;
6125574Smx205022 	if (lpan & MII_LP_ASYM_PAUSE)
6135574Smx205022 		ngep->param_lp_asym_pause = B_TRUE;
6145574Smx205022 	if (lpan & MII_LP_PAUSE)
6155574Smx205022 		ngep->param_lp_pause = B_TRUE;
6165574Smx205022 	if (linkup) {
6175574Smx205022 		ngep->param_link_up = linkup;
6185574Smx205022 		ngep->param_link_speed = nge_copper_link_speed[speed];
6195574Smx205022 		ngep->param_link_duplex = nge_copper_link_duplex[duplex];
6205574Smx205022 	} else {
6215574Smx205022 		ngep->param_link_up = B_FALSE;
6225574Smx205022 		ngep->param_link_speed = 0;
6235574Smx205022 		ngep->param_link_duplex = LINK_DUPLEX_UNKNOWN;
6245574Smx205022 	}
6255574Smx205022 	NGE_DEBUG(("nge_check_copper: link now %s speed %d duplex %d",
6265574Smx205022 	    UPORDOWN(ngep->param_link_up),
6275574Smx205022 	    ngep->param_link_speed,
6285574Smx205022 	    ngep->param_link_duplex));
6295574Smx205022 
6305574Smx205022 	return (B_FALSE);
6315574Smx205022 }
6325574Smx205022 
6335574Smx205022 /*
6345574Smx205022  * Because the network chipset embedded in Ck8-04 bridge is only a mac chipset,
6355574Smx205022  * the different vendor can use different media(serdes and copper).
6365574Smx205022  * To make it easier to extend the driver to support more platforms with ck8-04,
6375574Smx205022  * For example, one platform with serdes support,
6385574Smx205022  * wrapper phy operation functions.
6395574Smx205022  * But now, only supply copper phy operations.
6405574Smx205022  */
6415574Smx205022 static const phys_ops_t copper_ops = {
6425574Smx205022 	nge_phy_restart,
6435574Smx205022 	nge_update_copper,
6445574Smx205022 	nge_check_copper
6455574Smx205022 };
6465574Smx205022 
6475574Smx205022 /*
6485574Smx205022  * Here we have to determine which media we're using (copper or serdes).
6495574Smx205022  * Once that's done, we can initialise the physical layer appropriately.
6505574Smx205022  */
6515574Smx205022 void
nge_phys_init(nge_t * ngep)6525574Smx205022 nge_phys_init(nge_t *ngep)
6535574Smx205022 {
6545574Smx205022 	nge_mac2phy m2p;
6555574Smx205022 	NGE_TRACE(("nge_phys_init($%p)", (void *)ngep));
6565574Smx205022 
6575574Smx205022 	/* Get the phy type from MAC2PHY register */
6585574Smx205022 	m2p.m2p_val = nge_reg_get32(ngep, NGE_MAC2PHY);
6595574Smx205022 	ngep->phy_mode = m2p.m2p_bits.in_type;
6605574Smx205022 	if ((ngep->phy_mode != RGMII_IN) && (ngep->phy_mode != MII_IN)) {
6615574Smx205022 		ngep->phy_mode = RGMII_IN;
6625574Smx205022 		m2p.m2p_bits.in_type = RGMII_IN;
6635574Smx205022 		nge_reg_put32(ngep, NGE_MAC2PHY, m2p.m2p_val);
6645574Smx205022 	}
6655574Smx205022 
6665574Smx205022 	/*
6675574Smx205022 	 * Probe for the type of the  PHY.
6685574Smx205022 	 */
6695574Smx205022 	ngep->phy_xmii_addr = 1;
6705574Smx205022 	(void) nge_phy_probe(ngep);
6715574Smx205022 	ngep->chipinfo.flags |= CHIP_FLAG_COPPER;
6725574Smx205022 	ngep->physops = &copper_ops;
6735574Smx205022 	(*(ngep->physops->phys_restart))(ngep);
6745574Smx205022 }
675