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