xref: /onnv-gate/usr/src/uts/common/io/dmfe/dmfe_mii.c (revision 6990:d24af98bb8ea)
15181Sgd78059 /*
25181Sgd78059  * CDDL HEADER START
35181Sgd78059  *
45181Sgd78059  * The contents of this file are subject to the terms of the
55181Sgd78059  * Common Development and Distribution License (the "License").
65181Sgd78059  * You may not use this file except in compliance with the License.
75181Sgd78059  *
85181Sgd78059  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95181Sgd78059  * or http://www.opensolaris.org/os/licensing.
105181Sgd78059  * See the License for the specific language governing permissions
115181Sgd78059  * and limitations under the License.
125181Sgd78059  *
135181Sgd78059  * When distributing Covered Code, include this CDDL HEADER in each
145181Sgd78059  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155181Sgd78059  * If applicable, add the following below this CDDL HEADER, with the
165181Sgd78059  * fields enclosed by brackets "[]" replaced with your own identifying
175181Sgd78059  * information: Portions Copyright [yyyy] [name of copyright owner]
185181Sgd78059  *
195181Sgd78059  * CDDL HEADER END
205181Sgd78059  */
215181Sgd78059 /*
22*6990Sgd78059  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235181Sgd78059  * Use is subject to license terms.
245181Sgd78059  */
255181Sgd78059 
265181Sgd78059 #pragma ident	"%Z%%M%	%I%	%E% SMI"
275181Sgd78059 
285181Sgd78059 #include "dmfe_impl.h"
295181Sgd78059 
305181Sgd78059 /*
315181Sgd78059  * The bit-twiddling required by the MII interface makes the functions
325181Sgd78059  * in this file relatively slow, so they should probably only be called
335181Sgd78059  * from base/low-pri code.  However, there's nothing here that really
345181Sgd78059  * won't work at hi-pri, AFAIK; and 'relatively slow' only means that
355181Sgd78059  * they have microsecond busy-waits all over the place.
365181Sgd78059  *
375181Sgd78059  * dmfe_recheck_link(), on the other hand, uses delay() and loops for
385181Sgd78059  * up to dmfe_restart_time_us microseconds (currently 12 seconds), so
395181Sgd78059  * it should only be called from user (ioctl) or factotum context.
405181Sgd78059  *
415181Sgd78059  * Time parameters:
425181Sgd78059  *
435181Sgd78059  *	RESTART_TIME is the time in microseconds to allow for the link
445181Sgd78059  *	to go down and recover after changing the PHY parameters.
455181Sgd78059  *
465181Sgd78059  *	RESTART_POLL is the interval between checks on the link state
475181Sgd78059  *	while waiting for up to RESTART_TIME in total.
485181Sgd78059  *
495181Sgd78059  *	SETTLE_TIME is the time to allow for the PHY to stabilise
505181Sgd78059  *	after a change from LINK DOWN to LINK UP; multiple changes
515181Sgd78059  *	within this time are coalesced into one (in case the link
525181Sgd78059  *	goes UP-DOWN-UP as negotiation tries different speeds, etc).
535181Sgd78059  *
545181Sgd78059  * Patchable globals:
555181Sgd78059  *	dmfe_restart_time_us:	RESTART_TIME
565181Sgd78059  *	dmfe_restart_poll_us:	RESTART_POLL
575181Sgd78059  *	dmfe_mii_settle_time:	SETTLE_TIME
585181Sgd78059  */
595181Sgd78059 
605181Sgd78059 #define	RESTART_POLL		600000		/* microseconds		*/
615181Sgd78059 #define	RESTART_TIME		12000000	/* microseconds		*/
625181Sgd78059 #define	SETTLE_TIME		3000000		/* microseconds		*/
635181Sgd78059 
645181Sgd78059 #define	MII_AN_SELECTOR_8023	1
655181Sgd78059 #define	MII_STATUS_INVAL	0xffffU
665181Sgd78059 
675181Sgd78059 static clock_t dmfe_restart_poll_us = RESTART_POLL;
685181Sgd78059 static clock_t dmfe_restart_time_us = RESTART_TIME;
695181Sgd78059 static clock_t dmfe_mii_settle_time = SETTLE_TIME;
705181Sgd78059 static const int mii_reg_size = 16;			/* bits		*/
715181Sgd78059 
725181Sgd78059 #define	DMFE_DBG	DMFE_DBG_MII	/* debug flag for this code	*/
735181Sgd78059 
745181Sgd78059 /*
755181Sgd78059  * Type of transceiver currently in use.  The IEEE 802.3 std aPhyType
765181Sgd78059  * enumerates the following set
775181Sgd78059  */
785181Sgd78059 enum xcvr_type {
795181Sgd78059 	XCVR_TYPE_UNDEFINED	= 0,	/* undefined, or not yet known	*/
805181Sgd78059 	XCVR_TYPE_10BASE_T	= 7,	/* 10 Mbps copper		*/
815181Sgd78059 	XCVR_TYPE_100BASE_X	= 24	/* 100 Mbps copper		*/
825181Sgd78059 };
835181Sgd78059 
845181Sgd78059 /*
855181Sgd78059  * ======== Low-level SROM access ========
865181Sgd78059  */
875181Sgd78059 
885181Sgd78059 /*
895181Sgd78059  * EEPROM access is here because it shares register functionality with MII.
905181Sgd78059  * NB: <romaddr> is a byte address but must be 16-bit aligned.
915181Sgd78059  *     <cnt> is a byte count, and must be a multiple of 2.
925181Sgd78059  */
935181Sgd78059 void
945181Sgd78059 dmfe_read_eeprom(dmfe_t *dmfep, uint16_t raddr, uint8_t *ptr, int cnt)
955181Sgd78059 {
965181Sgd78059 	uint16_t value;
975181Sgd78059 	uint16_t bit;
985181Sgd78059 
995181Sgd78059 	/* only a whole number of words for now */
1005181Sgd78059 	ASSERT((cnt % 2) == 0);
1015181Sgd78059 	ASSERT((raddr % 2) == 0);
1025181Sgd78059 	ASSERT(cnt > 0);
1035181Sgd78059 	ASSERT(((raddr + cnt) / 2) < (HIGH_ADDRESS_BIT << 1));
1045181Sgd78059 
1055181Sgd78059 	raddr /= 2;	/* make it a word address */
1065181Sgd78059 
1075181Sgd78059 	/* loop over multiple words... rom access in 16-bit increments */
1085181Sgd78059 	while (cnt > 0) {
1095181Sgd78059 
1105181Sgd78059 		/* select the eeprom */
1115181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM);
1125181Sgd78059 		drv_usecwait(1);
1135181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
1145181Sgd78059 		drv_usecwait(1);
1155181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS | SEL_CLK);
1165181Sgd78059 		drv_usecwait(1);
1175181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
1185181Sgd78059 		drv_usecwait(1);
1195181Sgd78059 
1205181Sgd78059 		/* send 3 bit read command */
1215181Sgd78059 		for (bit = HIGH_CMD_BIT; bit != 0; bit >>= 1) {
1225181Sgd78059 
1235181Sgd78059 			value = (bit & EEPROM_READ_CMD) ? DATA_IN : 0;
1245181Sgd78059 
1255181Sgd78059 			/* strobe the bit in */
1265181Sgd78059 			dmfe_chip_put32(dmfep, ETHER_ROM_REG,
1275181Sgd78059 			    READ_EEPROM_CS | value);
1285181Sgd78059 			drv_usecwait(1);
1295181Sgd78059 			dmfe_chip_put32(dmfep, ETHER_ROM_REG,
1305181Sgd78059 			    READ_EEPROM_CS | SEL_CLK | value);
1315181Sgd78059 			drv_usecwait(1);
1325181Sgd78059 			dmfe_chip_put32(dmfep, ETHER_ROM_REG,
1335181Sgd78059 			    READ_EEPROM_CS | value);
1345181Sgd78059 			drv_usecwait(1);
1355181Sgd78059 		}
1365181Sgd78059 
1375181Sgd78059 		/* send 6 bit address */
1385181Sgd78059 		for (bit = HIGH_ADDRESS_BIT; bit != 0; bit >>= 1) {
1395181Sgd78059 			value = (bit & raddr) ? DATA_IN : 0;
1405181Sgd78059 
1415181Sgd78059 			/* strobe the bit in */
1425181Sgd78059 			dmfe_chip_put32(dmfep, ETHER_ROM_REG,
1435181Sgd78059 			    READ_EEPROM_CS | value);
1445181Sgd78059 			drv_usecwait(1);
1455181Sgd78059 			dmfe_chip_put32(dmfep, ETHER_ROM_REG,
1465181Sgd78059 			    READ_EEPROM_CS | SEL_CLK | value);
1475181Sgd78059 			drv_usecwait(1);
1485181Sgd78059 			dmfe_chip_put32(dmfep, ETHER_ROM_REG,
1495181Sgd78059 			    READ_EEPROM_CS | value);
1505181Sgd78059 			drv_usecwait(1);
1515181Sgd78059 		}
1525181Sgd78059 
1535181Sgd78059 		/* shift out data */
1545181Sgd78059 		value = 0;
1555181Sgd78059 		for (bit = HIGH_DATA_BIT; bit != 0; bit >>= 1) {
1565181Sgd78059 
1575181Sgd78059 			dmfe_chip_put32(dmfep, ETHER_ROM_REG,
1585181Sgd78059 			    READ_EEPROM_CS | SEL_CLK);
1595181Sgd78059 			drv_usecwait(1);
1605181Sgd78059 
1615181Sgd78059 			if (dmfe_chip_get32(dmfep, ETHER_ROM_REG) & DATA_OUT)
1625181Sgd78059 				value |= bit;
1635181Sgd78059 			drv_usecwait(1);
1645181Sgd78059 
1655181Sgd78059 			dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
1665181Sgd78059 			drv_usecwait(1);
1675181Sgd78059 		}
1685181Sgd78059 
1695181Sgd78059 		/* turn off EEPROM access */
1705181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM);
1715181Sgd78059 		drv_usecwait(1);
1725181Sgd78059 
1735181Sgd78059 		/* this makes it endian neutral */
1745181Sgd78059 		*ptr++ = value & 0xff;
1755181Sgd78059 		*ptr++ = (value >> 8);
1765181Sgd78059 
1775181Sgd78059 		cnt -= 2;
1785181Sgd78059 		raddr++;
1795181Sgd78059 	}
1805181Sgd78059 }
1815181Sgd78059 
1825181Sgd78059 /*
1835181Sgd78059  * ======== Lowest-level bit-twiddling to drive MII interface ========
1845181Sgd78059  */
1855181Sgd78059 
1865181Sgd78059 /*
1875181Sgd78059  * Poke <nbits> (up to 32) bits from <mii_data> along the MII control lines.
1885181Sgd78059  * Note: the data is taken starting with the MSB of <mii_data> and working
1895181Sgd78059  * down through progressively less significant bits.
1905181Sgd78059  */
1915181Sgd78059 static void
1925181Sgd78059 dmfe_poke_mii(dmfe_t *dmfep, uint32_t mii_data, uint_t nbits)
1935181Sgd78059 {
1945181Sgd78059 	uint32_t dbit;
1955181Sgd78059 
1965181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
1975181Sgd78059 
1985181Sgd78059 	for (; nbits > 0; mii_data <<= 1, --nbits) {
1995181Sgd78059 		/*
2005181Sgd78059 		 * Extract the MSB of <mii_data> and shift it to the
2015181Sgd78059 		 * proper bit position in the MII-poking register
2025181Sgd78059 		 */
2035181Sgd78059 		dbit = mii_data >> 31;
2045181Sgd78059 		dbit <<= MII_DATA_OUT_SHIFT;
2055181Sgd78059 		ASSERT((dbit & ~MII_DATA_OUT) == 0);
2065181Sgd78059 
2075181Sgd78059 		/*
2085181Sgd78059 		 * Drive the bit across the wire ...
2095181Sgd78059 		 */
2105181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG,
2115181Sgd78059 		    MII_WRITE | dbit);			/* Clock Low	*/
2125181Sgd78059 		drv_usecwait(MII_DELAY);
2135181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG,
2145181Sgd78059 		    MII_WRITE | MII_CLOCK | dbit);	/* Clock High	*/
2155181Sgd78059 		drv_usecwait(MII_DELAY);
2165181Sgd78059 	}
2175181Sgd78059 
2185181Sgd78059 	dmfe_chip_put32(dmfep, ETHER_ROM_REG,
2195181Sgd78059 	    MII_WRITE | dbit);				/* Clock Low	*/
2205181Sgd78059 	drv_usecwait(MII_DELAY);
2215181Sgd78059 }
2225181Sgd78059 
2235181Sgd78059 /*
2245181Sgd78059  * Put the MDIO port in tri-state for the turn around bits
2255181Sgd78059  * in MII read and at end of MII management sequence.
2265181Sgd78059  */
2275181Sgd78059 static void
2285181Sgd78059 dmfe_tristate_mii(dmfe_t *dmfep)
2295181Sgd78059 {
2305181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
2315181Sgd78059 
2325181Sgd78059 	dmfe_chip_put32(dmfep, ETHER_ROM_REG, MII_TRISTATE);
2335181Sgd78059 	drv_usecwait(MII_DELAY);
2345181Sgd78059 	dmfe_chip_put32(dmfep, ETHER_ROM_REG, MII_TRISTATE | MII_CLOCK);
2355181Sgd78059 	drv_usecwait(MII_DELAY);
2365181Sgd78059 }
2375181Sgd78059 
2385181Sgd78059 
2395181Sgd78059 /*
2405181Sgd78059  * ======== Next level: issue an MII access command/get a response ========
2415181Sgd78059  */
2425181Sgd78059 
2435181Sgd78059 static void
2445181Sgd78059 dmfe_mii_command(dmfe_t *dmfep, uint32_t command_word, int nbits)
2455181Sgd78059 {
2465181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
2475181Sgd78059 
2485181Sgd78059 	/* Write Preamble & Command & return to tristate */
2495181Sgd78059 	dmfe_poke_mii(dmfep, MII_PREAMBLE, 2*mii_reg_size);
2505181Sgd78059 	dmfe_poke_mii(dmfep, command_word, nbits);
2515181Sgd78059 	dmfe_tristate_mii(dmfep);
2525181Sgd78059 }
2535181Sgd78059 
2545181Sgd78059 static uint16_t
2555181Sgd78059 dmfe_mii_response(dmfe_t *dmfep)
2565181Sgd78059 {
2575181Sgd78059 	boolean_t ack;
258*6990Sgd78059 	uint16_t data;
2595181Sgd78059 	uint32_t tmp;
2605181Sgd78059 	int i;
2615181Sgd78059 
2625181Sgd78059 	/* Check that the PHY generated a zero bit on the 2nd clock */
2635181Sgd78059 	tmp = dmfe_chip_get32(dmfep, ETHER_ROM_REG);
2645181Sgd78059 	ack = (tmp & MII_DATA_IN) == 0;
2655181Sgd78059 
2665181Sgd78059 	/* read data WORD */
2675181Sgd78059 	for (data = 0, i = 0; i < mii_reg_size; ++i) {
2685181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG, MII_READ);
2695181Sgd78059 		drv_usecwait(MII_DELAY);
2705181Sgd78059 		dmfe_chip_put32(dmfep, ETHER_ROM_REG, MII_READ | MII_CLOCK);
2715181Sgd78059 		drv_usecwait(MII_DELAY);
2725181Sgd78059 		tmp = dmfe_chip_get32(dmfep, ETHER_ROM_REG);
2735181Sgd78059 		data <<= 1;
2745181Sgd78059 		data |= (tmp >> MII_DATA_IN_SHIFT) & 1;
2755181Sgd78059 	}
2765181Sgd78059 
2775181Sgd78059 	/* leave the interface tristated */
2785181Sgd78059 	dmfe_tristate_mii(dmfep);
2795181Sgd78059 
2805181Sgd78059 	return (ack ? data : ~0);
2815181Sgd78059 }
2825181Sgd78059 
2835181Sgd78059 /*
2845181Sgd78059  * ======== Next level: 16-bit PHY register access routines ========
2855181Sgd78059  */
2865181Sgd78059 
2875181Sgd78059 static void
2885181Sgd78059 dmfe_phy_write(dmfe_t *dmfep, uint_t reg_num, uint_t reg_dat)
2895181Sgd78059 {
2905181Sgd78059 	uint32_t command_word;
2915181Sgd78059 
2925181Sgd78059 	/* Issue MII command */
2935181Sgd78059 	command_word = MII_WRITE_FRAME;
2945181Sgd78059 	command_word |= dmfep->phy_addr << MII_PHY_ADDR_SHIFT;
2955181Sgd78059 	command_word |= reg_num << MII_REG_ADDR_SHIFT;
2965181Sgd78059 	command_word |= reg_dat;
2975181Sgd78059 	dmfe_mii_command(dmfep, command_word, 2*mii_reg_size);
2985181Sgd78059 }
2995181Sgd78059 
3005181Sgd78059 static uint16_t
3015181Sgd78059 dmfe_phy_read(dmfe_t *dmfep, uint_t reg_num)
3025181Sgd78059 {
3035181Sgd78059 	uint32_t command_word;
3045181Sgd78059 
3055181Sgd78059 	/* Issue MII command */
3065181Sgd78059 	command_word = MII_READ_FRAME;
3075181Sgd78059 	command_word |= dmfep->phy_addr << MII_PHY_ADDR_SHIFT;
3085181Sgd78059 	command_word |= reg_num << MII_REG_ADDR_SHIFT;
3095181Sgd78059 	dmfe_mii_command(dmfep, command_word, mii_reg_size-2);
3105181Sgd78059 
3115181Sgd78059 	return (dmfe_mii_response(dmfep));
3125181Sgd78059 }
3135181Sgd78059 
3145181Sgd78059 /*
3155181Sgd78059  * ======== Next level: PHY control operations ========
3165181Sgd78059  */
3175181Sgd78059 
3185181Sgd78059 /*
3195181Sgd78059  * Reset the PHYceiver, using a wierd sequence of accesses to CR12
3205181Sgd78059  *
3215181Sgd78059  * This could be done using MII accesses; but this should be quicker ....
3225181Sgd78059  */
3235181Sgd78059 static void
3245181Sgd78059 dmfe_phy_reset(dmfe_t *dmfep)
3255181Sgd78059 {
3265181Sgd78059 	DMFE_TRACE(("dmfe_phy_reset($%p)", (void *)dmfep));
3275181Sgd78059 
3285181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
3295181Sgd78059 
3305181Sgd78059 	dmfe_chip_put32(dmfep, PHY_STATUS_REG, GPS_WRITE_ENABLE|GPS_PHY_RESET);
3315181Sgd78059 	drv_usecwait(10);
3325181Sgd78059 	dmfe_chip_put32(dmfep, PHY_STATUS_REG, GPS_PHY_RESET);
3335181Sgd78059 	drv_usecwait(10);
3345181Sgd78059 	dmfe_chip_put32(dmfep, PHY_STATUS_REG, 0);
3355181Sgd78059 	drv_usecwait(10);
3365181Sgd78059 }
3375181Sgd78059 
3385181Sgd78059 /*
3395181Sgd78059  * Read the MII_STATUS register (BMSR)
3405181Sgd78059  */
3415181Sgd78059 static uint16_t
3425181Sgd78059 dmfe_mii_status(dmfe_t *dmfep)
3435181Sgd78059 {
3445181Sgd78059 	uint16_t bmsr;
3455181Sgd78059 
3465181Sgd78059 	bmsr = dmfe_phy_read(dmfep, MII_STATUS);
3475181Sgd78059 
3485181Sgd78059 	DMFE_DEBUG(("dmfe_mii_status: bmsr 0x%x", bmsr));
3495181Sgd78059 
3505181Sgd78059 	return (bmsr);
3515181Sgd78059 }
3525181Sgd78059 
3535181Sgd78059 /*
3545181Sgd78059  * Returns true if PHY at address <phy_addr> is present and accessible.
3555181Sgd78059  * We determine whether the PHY is there by looking for at least one
3565181Sgd78059  * set bit, and at least one clear bit, in the value returned from its
3575181Sgd78059  * status register (i.e. BMSR is not all zeroes or all ones).
3585181Sgd78059  */
3595181Sgd78059 static boolean_t
3605181Sgd78059 dmfe_probe_phy(dmfe_t *dmfep)
3615181Sgd78059 {
3625181Sgd78059 	uint16_t bmsr;
3635181Sgd78059 
3645181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
3655181Sgd78059 
3665181Sgd78059 	/* Clear any latched bits by reading twice */
3675181Sgd78059 	bmsr = dmfe_mii_status(dmfep);
3685181Sgd78059 	bmsr = dmfe_mii_status(dmfep);
3695181Sgd78059 
3705181Sgd78059 	DMFE_DEBUG(("dmfe_probe_phy($%p, %d) BMSR 0x%x",
3715181Sgd78059 	    (void *)dmfep, dmfep->phy_addr, bmsr));
3725181Sgd78059 
3735181Sgd78059 	/*
3745181Sgd78059 	 * At least one bit in BMSR should be set (for the device
3755181Sgd78059 	 * capabilities) and at least one clear (one of the error
3765181Sgd78059 	 * bits).  Unconnected devices tend to show 0xffff, but
3775181Sgd78059 	 * 0x0000 has also been seen.
3785181Sgd78059 	 */
3795181Sgd78059 	return (bmsr != 0 && bmsr != MII_STATUS_INVAL);
3805181Sgd78059 }
3815181Sgd78059 
3825181Sgd78059 static boolean_t
3835181Sgd78059 dmfe_find_phy(dmfe_t *dmfep)
3845181Sgd78059 {
3855181Sgd78059 	int mii_addr;
3865181Sgd78059 
3875181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
3885181Sgd78059 
3895181Sgd78059 	/*
3905181Sgd78059 	 * Verify that the PHY responds to MII accesses.  It *should*
3915181Sgd78059 	 * be at MII address 1, but the Davicom internal PHY can be
3925181Sgd78059 	 * reprogrammed to appear at a different address, so we'll
3935181Sgd78059 	 * check all 32 possible addresses if necessary (in the order
3945181Sgd78059 	 * 1, 2, 3..31, 0)
3955181Sgd78059 	 */
3965181Sgd78059 	for (mii_addr = 1; ; ) {
3975181Sgd78059 		dmfep->phy_addr = mii_addr % 32;
3985181Sgd78059 		if (dmfe_probe_phy(dmfep))
3995181Sgd78059 			break;
4005181Sgd78059 		if (++mii_addr > 32) {
4015181Sgd78059 			DMFE_DEBUG(("No PHY found"));
4025181Sgd78059 			return (B_FALSE);
4035181Sgd78059 		}
4045181Sgd78059 	}
4055181Sgd78059 
4065181Sgd78059 	dmfep->phy_id = dmfe_phy_read(dmfep, MII_PHYIDH) << 16;
4075181Sgd78059 	dmfep->phy_id |= dmfe_phy_read(dmfep, MII_PHYIDL);
4085181Sgd78059 
4095181Sgd78059 	DMFE_DEBUG(("PHY at address %d, id 0x%x", mii_addr, dmfep->phy_id));
4105181Sgd78059 
4115181Sgd78059 	switch (PHY_MANUFACTURER(dmfep->phy_id)) {
4125181Sgd78059 	case OUI_DAVICOM:
4135181Sgd78059 		return (B_TRUE);
4145181Sgd78059 
4155181Sgd78059 	default:
4165181Sgd78059 		dmfe_warning(dmfep, "unsupported (non-Davicom) PHY found!");
4175181Sgd78059 		return (B_FALSE);
4185181Sgd78059 	}
4195181Sgd78059 }
4205181Sgd78059 
4215181Sgd78059 #undef	DMFE_DBG
4225181Sgd78059 
4235181Sgd78059 #define	DMFE_DBG	DMFE_DBG_LINK	/* debug flag for this code	*/
4245181Sgd78059 
4255181Sgd78059 /*
4265181Sgd78059  * ======== Top-level PHY management routines ========
4275181Sgd78059  */
4285181Sgd78059 
4295181Sgd78059 /*
4305181Sgd78059  * (Re)initalise the PHY's speed/duplex/autonegotiation registers, basing
4315181Sgd78059  * the required settings on the various param_* variables that can be poked
4325181Sgd78059  * via the NDD interface.
4335181Sgd78059  *
4345181Sgd78059  * NOTE: the Tx/Rx processes should be STOPPED when this routine is called
4355181Sgd78059  */
4365181Sgd78059 void
4375181Sgd78059 dmfe_update_phy(dmfe_t *dmfep)
4385181Sgd78059 {
4395181Sgd78059 	uint16_t control;
4405181Sgd78059 	uint16_t anar;
4415181Sgd78059 
4425181Sgd78059 	DMFE_DEBUG(("dmfe_update_phy: autoneg %d 100fdx %d 100hdx %d "
4435181Sgd78059 	    "10fdx %d 10hdx %d", dmfep->param_autoneg,
4445181Sgd78059 	    dmfep->param_anar_100fdx, dmfep->param_anar_100hdx,
4455181Sgd78059 	    dmfep->param_anar_10fdx, dmfep->param_anar_10hdx));
4465181Sgd78059 
4475181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
4485181Sgd78059 
4495181Sgd78059 	/*
4505181Sgd78059 	 * NDD initialisation will have already set up the param_*
4515181Sgd78059 	 * variables based on the values of the various properties.
4525181Sgd78059 	 * Here we have to transform these into the proper settings
4535181Sgd78059 	 * of the PHY registers ...
4545181Sgd78059 	 */
4555181Sgd78059 	anar = control = 0;
4565181Sgd78059 
4575181Sgd78059 	if (dmfep->param_anar_100fdx)
4585181Sgd78059 		control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX;
4595181Sgd78059 	else if (dmfep->param_anar_100hdx)
4605181Sgd78059 		control |= MII_CONTROL_100MB;
4615181Sgd78059 	else if (dmfep->param_anar_10fdx)
4625181Sgd78059 		control |= MII_CONTROL_FDUPLEX;
4635181Sgd78059 
4645181Sgd78059 	if (dmfep->param_anar_100fdx)
4655181Sgd78059 		anar |= MII_ABILITY_100BASE_TX_FD;
4665181Sgd78059 	if (dmfep->param_anar_100hdx)
4675181Sgd78059 		anar |= MII_ABILITY_100BASE_TX;
4685181Sgd78059 	if (dmfep->param_anar_10fdx)
4695181Sgd78059 		anar |= MII_ABILITY_10BASE_T_FD;
4705181Sgd78059 	if (dmfep->param_anar_10hdx)
4715181Sgd78059 		anar |= MII_ABILITY_10BASE_T;
4725181Sgd78059 
4735181Sgd78059 	if (anar == 0) {
4745181Sgd78059 		/*
4755181Sgd78059 		 * A stupid combination of settings has left us with no
4765181Sgd78059 		 * options - so select the default (100Mb/s half-duplex)
4775181Sgd78059 		 * for now and re-enable ALL autonegotiation options.
4785181Sgd78059 		 */
4795181Sgd78059 		control |= MII_CONTROL_100MB;
4805181Sgd78059 		anar |= MII_ABILITY_100BASE_TX_FD;
4815181Sgd78059 		anar |= MII_ABILITY_100BASE_TX;
4825181Sgd78059 		anar |= MII_ABILITY_10BASE_T_FD;
4835181Sgd78059 		anar |= MII_ABILITY_10BASE_T;
4845181Sgd78059 	}
4855181Sgd78059 
4865181Sgd78059 	if ((dmfep->opmode & LOOPBACK_MODE_MASK) != LOOPBACK_OFF) {
4875181Sgd78059 		/*
4885181Sgd78059 		 * If loopback is selected at the MAC level, we have
4895181Sgd78059 		 * to make sure that the settings are consistent at
4905181Sgd78059 		 * the PHY, and also keep autonegotiation switched OFF,
4915181Sgd78059 		 * otherwise we can get all sorts of strange effects
4925181Sgd78059 		 * including continuous link change interrupts :-(
4935181Sgd78059 		 */
4945181Sgd78059 		control |= MII_CONTROL_LOOPBACK;
4955181Sgd78059 	} else if (dmfep->param_autoneg) {
4965181Sgd78059 		/*
4975181Sgd78059 		 * Autonegotiation is only possible if loopback is OFF
4985181Sgd78059 		 */
4995181Sgd78059 		control |= MII_CONTROL_ANE;
5005181Sgd78059 	}
5015181Sgd78059 
5025181Sgd78059 	DMFE_DEBUG(("dmfe_update_phy: anar 0x%x control 0x%x", anar, control));
5035181Sgd78059 
5045181Sgd78059 	anar |= MII_AN_SELECTOR_8023;
5055181Sgd78059 	if ((anar != dmfep->phy_anar_w) || (control != dmfep->phy_control) ||
5065181Sgd78059 	    (dmfep->update_phy)) {
5075181Sgd78059 		/*
5085181Sgd78059 		 * Something's changed; reset the PHY and write the new
5095181Sgd78059 		 * values to the PHY CONTROL and ANAR registers.  This
5105181Sgd78059 		 * will probably cause the link to go down, and then back
5115181Sgd78059 		 * up again once the link is stable and autonegotiation
5125181Sgd78059 		 * (if enabled) is complete.  We should get a link state
5135181Sgd78059 		 * change at the end; but in any case the ticker will keep
5145181Sgd78059 		 * an eye on what's going on ...
5155181Sgd78059 		 */
5165181Sgd78059 		dmfe_phy_reset(dmfep);
5175181Sgd78059 		dmfe_phy_write(dmfep, MII_CONTROL, control);
5185181Sgd78059 		dmfe_phy_write(dmfep, MII_AN_ADVERT, anar);
5195181Sgd78059 	}
5205181Sgd78059 
5215181Sgd78059 	/*
5225181Sgd78059 	 * If autonegotiation is (now) enabled, we want to trigger
5235181Sgd78059 	 * a new autonegotiation cycle now that the PHY has been
5245181Sgd78059 	 * programmed with the capabilities to be advertised.
5255181Sgd78059 	 */
5265181Sgd78059 	if (control & MII_CONTROL_ANE)
5275181Sgd78059 		dmfe_phy_write(dmfep, MII_CONTROL, control | MII_CONTROL_RSAN);
5285181Sgd78059 
5295181Sgd78059 	/*
5305181Sgd78059 	 * Save the values written in the shadow copies of the CONTROL
5315181Sgd78059 	 * and ANAR registers, and clear the shadow BMSR 'cos it's no
5325181Sgd78059 	 * longer valid.
5335181Sgd78059 	 */
5345181Sgd78059 	dmfep->phy_control = control;
5355181Sgd78059 	dmfep->phy_anar_w = anar;
5365181Sgd78059 	dmfep->phy_bmsr = 0;
5375181Sgd78059 }
5385181Sgd78059 
5395181Sgd78059 
5405181Sgd78059 /*
5415181Sgd78059  * PHY initialisation, called only once
5425181Sgd78059  *
5435181Sgd78059  * Discover the MII address of the PHY (should be 1).
5445181Sgd78059  * Initialise according to preset NDD parameters.
5455181Sgd78059  * Return status
5465181Sgd78059  */
5475181Sgd78059 boolean_t
5485181Sgd78059 dmfe_init_phy(dmfe_t *dmfep)
5495181Sgd78059 {
5505181Sgd78059 	boolean_t ok;
5515181Sgd78059 
5525181Sgd78059 	mutex_enter(dmfep->milock);
5535181Sgd78059 	ok = dmfe_find_phy(dmfep);
5545181Sgd78059 	if (ok)
5555181Sgd78059 		dmfe_update_phy(dmfep);
5565181Sgd78059 	mutex_exit(dmfep->milock);
5575181Sgd78059 
5585181Sgd78059 	return (ok);
5595181Sgd78059 }
5605181Sgd78059 
5615181Sgd78059 /*
5625181Sgd78059  *	========== Active Media Determination Routines ==========
5635181Sgd78059  */
5645181Sgd78059 
5655181Sgd78059 
5665181Sgd78059 /*
5675181Sgd78059  * Check whether the BMSR has changed.  If it hasn't, this routine
5685181Sgd78059  * just returns B_FALSE (no further action required).  Otherwise,
5695181Sgd78059  * it records the time when the change was seen and returns B_TRUE.
5705181Sgd78059  *
5715181Sgd78059  * This routine needs only the <milock>, although <oplock> may
5725181Sgd78059  * also be held.  This is why full processing of the link change
5735181Sgd78059  * is left to dmfe_recheck_link() below.
5745181Sgd78059  */
5755181Sgd78059 static boolean_t
5765181Sgd78059 dmfe_check_bmsr(dmfe_t *dmfep)
5775181Sgd78059 {
5785181Sgd78059 	uint16_t new_bmsr;
5795181Sgd78059 
5805181Sgd78059 	DMFE_TRACE(("dmfe_check_bmsr($%p)", (void *)dmfep));
5815181Sgd78059 
5825181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
5835181Sgd78059 
5845181Sgd78059 	/*
5855181Sgd78059 	 * Read the BMSR and check it against the previous value
5865181Sgd78059 	 */
5875181Sgd78059 	new_bmsr = dmfe_mii_status(dmfep);
5885181Sgd78059 	DMFE_DEBUG(("dmfe_check_bmsr: bmsr 0x%x -> 0x%x",
5895181Sgd78059 	    dmfep->phy_bmsr, new_bmsr));
5905181Sgd78059 
5915181Sgd78059 	/*
5925181Sgd78059 	 * Record new value and timestamp if it's changed
5935181Sgd78059 	 */
5945181Sgd78059 	if (new_bmsr != dmfep->phy_bmsr) {
5955181Sgd78059 		dmfep->phy_bmsr = new_bmsr;
5965181Sgd78059 		dmfep->phy_bmsr_lbolt = ddi_get_lbolt();
5975181Sgd78059 		return (B_TRUE);
5985181Sgd78059 	}
5995181Sgd78059 
6005181Sgd78059 	return (B_FALSE);
6015181Sgd78059 }
6025181Sgd78059 
6035181Sgd78059 /*
6045181Sgd78059  * 'Quick' link check routine
6055181Sgd78059  *
6065181Sgd78059  * Call whenever the link state may have changed, or periodically to
6075181Sgd78059  * poll for link up/down events.  Returns B_FALSE if nothing interesting
6085181Sgd78059  * has happened.  Otherwise, it returns B_TRUE, telling the caller to
6095181Sgd78059  * call dmfe_recheck_link() (below).  If the link state is UNKNOWN, we
6105181Sgd78059  * return B_TRUE anyway, even if the BMSR hasn't changed - but only after
6115181Sgd78059  * going through the motions, 'cos the read of the BMSR has side-effects -
6125181Sgd78059  * some of the BMSR bits are latching-until-read, and dmfe_check_bmsr()
6135181Sgd78059  * also records the time of any change to the BMSR!
6145181Sgd78059  */
6155181Sgd78059 boolean_t
6165181Sgd78059 dmfe_check_link(dmfe_t *dmfep)
6175181Sgd78059 {
6185181Sgd78059 	if (dmfe_check_bmsr(dmfep))
6195181Sgd78059 		return (B_TRUE);
6205181Sgd78059 	return (dmfep->link_state == LINK_STATE_UNKNOWN);
6215181Sgd78059 }
6225181Sgd78059 
6235181Sgd78059 
6245181Sgd78059 /*
6255181Sgd78059  * Update all parameters and statistics after a link state change.
6265181Sgd78059  */
6275181Sgd78059 static void
6285181Sgd78059 dmfe_media_update(dmfe_t *dmfep, link_state_t newstate, int speed, int duplex)
6295181Sgd78059 {
6305181Sgd78059 	int ks_id;
6315181Sgd78059 
6325181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
6335181Sgd78059 	ASSERT(mutex_owned(dmfep->oplock));
6345181Sgd78059 	ASSERT(newstate != dmfep->link_state);
6355181Sgd78059 
6365181Sgd78059 	switch (newstate) {
6375181Sgd78059 	case LINK_STATE_UP:
6385181Sgd78059 		dmfep->param_linkup = 1;
6395181Sgd78059 		break;
6405181Sgd78059 
6415181Sgd78059 	default:
6425181Sgd78059 		dmfep->param_linkup = 0;
6435181Sgd78059 		break;
6445181Sgd78059 	}
6455181Sgd78059 
6465181Sgd78059 	switch (speed) {
6475181Sgd78059 	case 100:
6485181Sgd78059 		dmfep->op_stats_speed = 100000000;
6495181Sgd78059 		dmfep->param_speed = speed;
6505181Sgd78059 		dmfep->phy_inuse = XCVR_100X;
6515181Sgd78059 		break;
6525181Sgd78059 
6535181Sgd78059 	case 10:
6545181Sgd78059 		dmfep->op_stats_speed = 10000000;
6555181Sgd78059 		dmfep->param_speed = speed;
6565181Sgd78059 		dmfep->phy_inuse = XCVR_10;
6575181Sgd78059 		break;
6585181Sgd78059 
6595181Sgd78059 	default:
6605181Sgd78059 		dmfep->op_stats_speed = 0;
6615181Sgd78059 		dmfep->phy_inuse = XCVR_UNDEFINED;
6625181Sgd78059 		break;
6635181Sgd78059 	}
6645181Sgd78059 
6655181Sgd78059 	dmfep->op_stats_duplex = dmfep->param_duplex = duplex;
6665181Sgd78059 
6675181Sgd78059 	if (newstate == LINK_STATE_UP)
6685181Sgd78059 		ks_id = KS_LINK_UP_CNT;
6695181Sgd78059 	else
6705181Sgd78059 		ks_id = KS_LINK_DROP_CNT;
6715181Sgd78059 	DRV_KS_INC(dmfep, ks_id);
6725181Sgd78059 }
6735181Sgd78059 
6745181Sgd78059 /*
6755181Sgd78059  * Verify and report a change in the state of the link ...
6765181Sgd78059  */
6775181Sgd78059 static void
6785181Sgd78059 dmfe_link_change(dmfe_t *dmfep, link_state_t newstate)
6795181Sgd78059 {
6805181Sgd78059 	boolean_t report;
6815181Sgd78059 	uint32_t gpsr;
6825181Sgd78059 	int speed;
6835181Sgd78059 	int duplex;
6845181Sgd78059 
6855181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
6865181Sgd78059 	ASSERT(mutex_owned(dmfep->oplock));
6875181Sgd78059 	ASSERT(newstate != dmfep->link_state);
6885181Sgd78059 
6895181Sgd78059 	switch (newstate) {
6905181Sgd78059 	case LINK_STATE_UP:
6915181Sgd78059 		gpsr = dmfe_chip_get32(dmfep, PHY_STATUS_REG);
6925181Sgd78059 		speed = gpsr & GPS_LINK_100 ? 100 : 10;
6935181Sgd78059 		duplex = (gpsr & GPS_FULL_DUPLEX) ?
6945181Sgd78059 		    LINK_DUPLEX_FULL: LINK_DUPLEX_HALF;
6955181Sgd78059 		report = B_TRUE;
6965181Sgd78059 		break;
6975181Sgd78059 
6985181Sgd78059 	default:
6995181Sgd78059 		speed = 0;
7005181Sgd78059 		duplex = LINK_DUPLEX_UNKNOWN;
7015181Sgd78059 		switch (dmfep->link_state) {
7025181Sgd78059 		case LINK_STATE_DOWN:		/* DOWN->UNKNOWN	*/
7035181Sgd78059 		case LINK_STATE_UNKNOWN:	/* UNKNOWN->DOWN	*/
7045181Sgd78059 			report = B_FALSE;
7055181Sgd78059 			break;
7065181Sgd78059 
7075181Sgd78059 		case LINK_STATE_UP:		/* UP->DOWN/UNKNOWN	*/
7085181Sgd78059 			report = B_TRUE;
7095181Sgd78059 			break;
7105181Sgd78059 		}
7115181Sgd78059 		break;
7125181Sgd78059 	}
7135181Sgd78059 
7145181Sgd78059 	/*
7155181Sgd78059 	 * Update status & report new link state if required ...
7165181Sgd78059 	 */
7175181Sgd78059 	if (report)
7185181Sgd78059 		dmfe_media_update(dmfep, newstate, speed, duplex);
7195181Sgd78059 }
7205181Sgd78059 
7215181Sgd78059 /*
7225181Sgd78059  * Examine the value most recently read from the BMSR and derive
7235181Sgd78059  * the (new) link state.
7245181Sgd78059  *
7255181Sgd78059  * This routine also incorporates heuristics determining when to
7265181Sgd78059  * accept a new state as valid and report it, based on the new
7275181Sgd78059  * (apparent) state, the old state, and the time elapsed since
7285181Sgd78059  * the last time we saw a (potential) state change.  For example,
7295181Sgd78059  * we want to accept UP->DOWN immediately, but UNKNOWN->UP only
7305181Sgd78059  * once autonegotiation is completed and the results are stable.
7315181Sgd78059  */
7325181Sgd78059 static link_state_t
7335181Sgd78059 dmfe_process_bmsr(dmfe_t *dmfep, clock_t time)
7345181Sgd78059 {
7355181Sgd78059 	link_state_t newstate;
7365181Sgd78059 	uint32_t gpsr;
7375181Sgd78059 	uint16_t bmsr;
7385181Sgd78059 	uint16_t anlpar;
7395181Sgd78059 	uint16_t anar;
7405181Sgd78059 
7415181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
7425181Sgd78059 	ASSERT(mutex_owned(dmfep->oplock));
7435181Sgd78059 
7445181Sgd78059 	/*
7455181Sgd78059 	 * Read PHY registers & publish through driver-specific kstats
7465181Sgd78059 	 * Decode abilities & publish through ndd & standard MII kstats
7475181Sgd78059 	 */
7485181Sgd78059 	dmfep->phy_anar_r = dmfe_phy_read(dmfep, MII_AN_ADVERT);
7495181Sgd78059 	dmfep->phy_aner   = dmfe_phy_read(dmfep, MII_AN_EXPANSION);
7505181Sgd78059 	dmfep->phy_anlpar = dmfe_phy_read(dmfep, MII_AN_LPABLE);
7515181Sgd78059 	dmfep->phy_dscsr  = dmfe_phy_read(dmfep, DM_SCSR);
7525181Sgd78059 
7535181Sgd78059 	DRV_KS_SET(dmfep, KS_MIIREG_BMSR, dmfep->phy_bmsr);
7545181Sgd78059 	DRV_KS_SET(dmfep, KS_MIIREG_ANAR, dmfep->phy_anar_r);
7555181Sgd78059 	DRV_KS_SET(dmfep, KS_MIIREG_ANER, dmfep->phy_aner);
7565181Sgd78059 	DRV_KS_SET(dmfep, KS_MIIREG_ANLPAR, dmfep->phy_anlpar);
7575181Sgd78059 	DRV_KS_SET(dmfep, KS_MIIREG_DSCSR, dmfep->phy_dscsr);
7585181Sgd78059 
7595181Sgd78059 	DMFE_DEBUG(("dmfe_process_bmsr: ANAR 0x%x->0x%x ANLPAR 0x%x SCSR 0x%x",
7605181Sgd78059 	    dmfep->phy_anar_w, dmfep->phy_anar_r,
7615181Sgd78059 	    dmfep->phy_anlpar, dmfep->phy_dscsr));
7625181Sgd78059 
7635181Sgd78059 	/*
7645181Sgd78059 	 * Capabilities of DM9102A
7655181Sgd78059 	 */
7665181Sgd78059 	bmsr = dmfep->phy_bmsr;
7675181Sgd78059 
7685181Sgd78059 	dmfep->param_bmsr_100T4    = BIS(bmsr, MII_STATUS_100_BASE_T4);
7695181Sgd78059 	dmfep->param_bmsr_100fdx   = BIS(bmsr, MII_STATUS_100_BASEX_FD);
7705181Sgd78059 	dmfep->param_bmsr_100hdx   = BIS(bmsr, MII_STATUS_100_BASEX);
7715181Sgd78059 	dmfep->param_bmsr_10fdx    = BIS(bmsr, MII_STATUS_10_FD);
7725181Sgd78059 	dmfep->param_bmsr_10hdx    = BIS(bmsr, MII_STATUS_10);
7735181Sgd78059 	dmfep->param_bmsr_remfault = 1;
7745181Sgd78059 	dmfep->param_bmsr_autoneg  = BIS(bmsr, MII_STATUS_CANAUTONEG);
7755181Sgd78059 
7765181Sgd78059 	/*
7775181Sgd78059 	 * Advertised abilities of DM9102A
7785181Sgd78059 	 */
7795181Sgd78059 	anar = dmfep->phy_anar_r;
7805181Sgd78059 	dmfep->param_anar_remfault = BIS(anar, MII_AN_ADVERT_REMFAULT);
7815181Sgd78059 
7825181Sgd78059 	/*
7835181Sgd78059 	 * Link Partners advertised abilities
7845181Sgd78059 	 */
7855181Sgd78059 	if ((dmfep->phy_aner & MII_AN_EXP_LPCANAN) == 0) {
7865181Sgd78059 		anlpar = 0;
7875181Sgd78059 		dmfep->param_lp_autoneg = 0;
7885181Sgd78059 	} else {
7895181Sgd78059 		anlpar = dmfep->phy_anlpar;
7905181Sgd78059 		dmfep->param_lp_autoneg = 1;
7915181Sgd78059 	}
7925181Sgd78059 
7935181Sgd78059 	dmfep->param_lp_100T4    = BIS(anlpar, MII_ABILITY_100BASE_T4);
7945181Sgd78059 	dmfep->param_lp_100fdx   = BIS(anlpar, MII_ABILITY_100BASE_TX_FD);
7955181Sgd78059 	dmfep->param_lp_100hdx   = BIS(anlpar, MII_ABILITY_100BASE_TX);
7965181Sgd78059 	dmfep->param_lp_10fdx    = BIS(anlpar, MII_ABILITY_10BASE_T_FD);
7975181Sgd78059 	dmfep->param_lp_10hdx    = BIS(anlpar, MII_ABILITY_10BASE_T);
7985181Sgd78059 	dmfep->param_lp_remfault = BIS(anlpar, MII_AN_ADVERT_REMFAULT);
7995181Sgd78059 
8005181Sgd78059 	/*
8015181Sgd78059 	 * Derive new state & time since last change
8025181Sgd78059 	 */
8035181Sgd78059 	newstate = (dmfep->phy_bmsr & MII_STATUS_LINKUP) ?
8045181Sgd78059 	    LINK_STATE_UP : LINK_STATE_DOWN;
8055181Sgd78059 	time -= dmfep->phy_bmsr_lbolt;
8065181Sgd78059 
8075181Sgd78059 	/*
8085181Sgd78059 	 * Hah! That would be just too easy ... we have to check
8095181Sgd78059 	 * for all sorts of special cases before we decide :(
8105181Sgd78059 	 */
8115181Sgd78059 	if (dmfep->phy_bmsr == MII_STATUS_INVAL)
8125181Sgd78059 		newstate = LINK_STATE_DOWN;
8135181Sgd78059 	else if ((dmfep->link_state == LINK_STATE_UP) &&
8145181Sgd78059 	    (newstate == LINK_STATE_DOWN))
8155181Sgd78059 		/*EMPTY*/;
8165181Sgd78059 	else if (time < drv_usectohz(dmfe_mii_settle_time))
8175181Sgd78059 		newstate = LINK_STATE_UNKNOWN;
8185181Sgd78059 	else if (dmfep->phy_bmsr & MII_STATUS_ANDONE)
8195181Sgd78059 		/*EMPTY*/;
8205181Sgd78059 	else if (dmfep->phy_control & MII_CONTROL_ANE)
8215181Sgd78059 		newstate = LINK_STATE_DOWN;
8225181Sgd78059 
8235181Sgd78059 	if (newstate == LINK_STATE_UP) {
8245181Sgd78059 		/*
8255181Sgd78059 		 * Link apparently UP - but get the PHY status register
8265181Sgd78059 		 * (GPSR) and make sure it also shows a consistent value.
8275181Sgd78059 		 * In particular, both the link status bits should be 1,
8285181Sgd78059 		 * and the speed bits should show one set and one clear.
8295181Sgd78059 		 * Any other combination indicates that we haven't really
8305181Sgd78059 		 * got a stable link yet ...
8315181Sgd78059 		 */
8325181Sgd78059 		gpsr = dmfe_chip_get32(dmfep, PHY_STATUS_REG);
8335181Sgd78059 		DMFE_DEBUG(("dmfe_process_bmsr: GPSR 0x%x", gpsr));
8345181Sgd78059 
8355181Sgd78059 		switch (gpsr & (GPS_LINK_STATUS|GPS_UTP_SIG)) {
8365181Sgd78059 		case GPS_LINK_STATUS|GPS_UTP_SIG:
8375181Sgd78059 			break;
8385181Sgd78059 		default:
8395181Sgd78059 			newstate = LINK_STATE_UNKNOWN;
8405181Sgd78059 			break;
8415181Sgd78059 		}
8425181Sgd78059 
8435181Sgd78059 		switch (gpsr & (GPS_LINK_10|GPS_LINK_100)) {
8445181Sgd78059 		case GPS_LINK_100:
8455181Sgd78059 		case GPS_LINK_10:
8465181Sgd78059 			break;
8475181Sgd78059 		default:
8485181Sgd78059 			newstate = LINK_STATE_UNKNOWN;
8495181Sgd78059 			break;
8505181Sgd78059 		}
8515181Sgd78059 	}
8525181Sgd78059 
8535181Sgd78059 	DMFE_DEBUG(("dmfe_process_bmsr: BMSR 0x%x state %d -> %d @ %d",
8545181Sgd78059 	    dmfep->phy_bmsr, dmfep->link_state, newstate, time));
8555181Sgd78059 
8565181Sgd78059 	return (newstate);
8575181Sgd78059 }
8585181Sgd78059 
8595181Sgd78059 /*
8605181Sgd78059  * 'Full' link check routine
8615181Sgd78059  *
8625181Sgd78059  * Call whenever dmfe_check_link() above indicates that the link
8635181Sgd78059  * state may have changed.  Handles all changes to the link state
8645181Sgd78059  * (up/down, speed/duplex changes), including multiple changes
8655181Sgd78059  * occuring within the <timeout>.  <timeout> will be zero if called
8665181Sgd78059  * from the factotum (for an unexpected change) or the number of
8675181Sgd78059  * ticks for which to wait for stability after an ioctl that changes
8685181Sgd78059  * the link parameters.  Even when <timeout> is zero, we loop while
8695181Sgd78059  * the BMSR keeps changing ...
8705181Sgd78059  *
8715181Sgd78059  * Needs both <milock> and <oplock>, and the Tx/Rx processes
8725181Sgd78059  * should already be stopped so we're not liable to confuse them
8735181Sgd78059  * by changing the PHY/MAC parameters under them ...
8745181Sgd78059  *
8755181Sgd78059  */
8765181Sgd78059 void
8775181Sgd78059 dmfe_recheck_link(dmfe_t *dmfep, boolean_t ioctl)
8785181Sgd78059 {
8795181Sgd78059 	link_state_t newstate;
8805181Sgd78059 	boolean_t again;
8815181Sgd78059 	clock_t deadline;
8825181Sgd78059 	clock_t now;
8835181Sgd78059 
8845181Sgd78059 	DMFE_TRACE(("dmfe_recheck_link($%p, %d)", (void *)dmfep, ioctl));
8855181Sgd78059 
8865181Sgd78059 	ASSERT(mutex_owned(dmfep->milock));
8875181Sgd78059 	ASSERT(mutex_owned(dmfep->oplock));
8885181Sgd78059 
8895181Sgd78059 	now = deadline = ddi_get_lbolt();
8905181Sgd78059 	if (ioctl)
8915181Sgd78059 		deadline += drv_usectohz(dmfe_restart_time_us);
8925181Sgd78059 
8935181Sgd78059 	for (; ; now = ddi_get_lbolt()) {
8945181Sgd78059 		newstate = dmfe_process_bmsr(dmfep, now);
8955181Sgd78059 		again = dmfe_check_bmsr(dmfep);
8965181Sgd78059 		if (newstate != dmfep->link_state) {
8975181Sgd78059 			dmfe_link_change(dmfep, newstate);
8985181Sgd78059 			dmfep->link_state = newstate;
8995181Sgd78059 			again = B_TRUE;
9005181Sgd78059 		}
9015181Sgd78059 		ASSERT(dmfep->link_state == newstate);
9025181Sgd78059 		if (again)
9035181Sgd78059 			continue;
9045181Sgd78059 		if (newstate == LINK_STATE_UP) {
9055181Sgd78059 			dmfep->update_phy = B_TRUE;
9065181Sgd78059 			break;
9075181Sgd78059 		}
9085181Sgd78059 		if (now >= deadline)
9095181Sgd78059 			break;
9105181Sgd78059 		delay(drv_usectohz(dmfe_restart_poll_us));
9115181Sgd78059 	}
9125181Sgd78059 }
9135181Sgd78059 
9145181Sgd78059 #undef	DMFE_DBG
915