16f3e57acSmx205022 /* 247693af9Smx205022 * CDDL HEADER START 347693af9Smx205022 * 447693af9Smx205022 * The contents of this file are subject to the terms of the 547693af9Smx205022 * Common Development and Distribution License (the "License"). 647693af9Smx205022 * You may not use this file except in compliance with the License. 747693af9Smx205022 * 847693af9Smx205022 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 947693af9Smx205022 * or http://www.opensolaris.org/os/licensing. 1047693af9Smx205022 * See the License for the specific language governing permissions 1147693af9Smx205022 * and limitations under the License. 1247693af9Smx205022 * 1347693af9Smx205022 * When distributing Covered Code, include this CDDL HEADER in each 1447693af9Smx205022 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1547693af9Smx205022 * If applicable, add the following below this CDDL HEADER, with the 1647693af9Smx205022 * fields enclosed by brackets "[]" replaced with your own identifying 1747693af9Smx205022 * information: Portions Copyright [yyyy] [name of copyright owner] 1847693af9Smx205022 * 1947693af9Smx205022 * CDDL HEADER END 206f3e57acSmx205022 */ 216f3e57acSmx205022 226f3e57acSmx205022 /* 23*bdb9230aSGarrett D'Amore * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2447693af9Smx205022 * Use is subject to license terms. 256f3e57acSmx205022 */ 266f3e57acSmx205022 276f3e57acSmx205022 #include "nge.h" 286f3e57acSmx205022 296f3e57acSmx205022 #undef NGE_DBG 306f3e57acSmx205022 #define NGE_DBG NGE_DBG_MII /* debug flag for this code */ 316f3e57acSmx205022 326f3e57acSmx205022 /* 336f3e57acSmx205022 * The arrays below can be indexed by the MODE bits from the mac2phy 346f3e57acSmx205022 * register to determine the current speed/duplex settings. 356f3e57acSmx205022 */ 366f3e57acSmx205022 static const int16_t nge_copper_link_speed[] = { 376f3e57acSmx205022 0, /* MII_AUX_STATUS_MODE_NONE */ 386f3e57acSmx205022 10, /* MII_AUX_STAT0,US_MODE_10 */ 396f3e57acSmx205022 100, /* MII_AUX_STAT0,US_MODE_100 */ 406f3e57acSmx205022 1000, /* MII_AUX_STAT0,US_MODE_1000 */ 416f3e57acSmx205022 }; 426f3e57acSmx205022 436f3e57acSmx205022 static const int8_t nge_copper_link_duplex[] = { 446f3e57acSmx205022 LINK_DUPLEX_UNKNOWN, /* MII_DUPLEX_NONE */ 456f3e57acSmx205022 LINK_DUPLEX_HALF, /* MII_DUPLEX_HALF */ 466f3e57acSmx205022 LINK_DUPLEX_FULL, /* MII_DUPLEX_FULL */ 476f3e57acSmx205022 }; 486f3e57acSmx205022 496f3e57acSmx205022 506f3e57acSmx205022 static uint16_t nge_mii_access(nge_t *ngep, nge_regno_t regno, 516f3e57acSmx205022 uint16_t data, uint32_t cmd); 526f3e57acSmx205022 #pragma inline(nge_mii_access) 536f3e57acSmx205022 546f3e57acSmx205022 static uint16_t 556f3e57acSmx205022 nge_mii_access(nge_t *ngep, nge_regno_t regno, uint16_t data, uint32_t cmd) 566f3e57acSmx205022 { 576f3e57acSmx205022 uint16_t tries; 586f3e57acSmx205022 uint16_t mdio_data; 596f3e57acSmx205022 nge_mdio_adr mdio_adr; 606f3e57acSmx205022 nge_mintr_src intr_src; 616f3e57acSmx205022 626f3e57acSmx205022 NGE_TRACE(("nge_mii_access($%p, 0x%lx, 0x%x, 0x%x)", 636f3e57acSmx205022 (void *)ngep, regno, data, cmd)); 646f3e57acSmx205022 656f3e57acSmx205022 /* 666f3e57acSmx205022 * Clear the privous interrupt event 676f3e57acSmx205022 */ 686f3e57acSmx205022 intr_src.src_val = nge_reg_get8(ngep, NGE_MINTR_SRC); 696f3e57acSmx205022 nge_reg_put8(ngep, NGE_MINTR_SRC, intr_src.src_val); 706f3e57acSmx205022 716f3e57acSmx205022 /* 726f3e57acSmx205022 * Check whether the current operation has been finished 736f3e57acSmx205022 */ 746f3e57acSmx205022 mdio_adr.adr_val = nge_reg_get16(ngep, NGE_MDIO_ADR); 756f3e57acSmx205022 for (tries = 0; tries < 30; tries ++) { 766f3e57acSmx205022 if (mdio_adr.adr_bits.mdio_clc == NGE_CLEAR) 776f3e57acSmx205022 break; 786f3e57acSmx205022 drv_usecwait(10); 796f3e57acSmx205022 mdio_adr.adr_val = nge_reg_get16(ngep, NGE_MDIO_ADR); 806f3e57acSmx205022 } 816f3e57acSmx205022 826f3e57acSmx205022 /* 836f3e57acSmx205022 * The current operation can not be finished successfully 846f3e57acSmx205022 * The driver should halt the current operation 856f3e57acSmx205022 */ 866f3e57acSmx205022 if (tries == 30) { 876f3e57acSmx205022 mdio_adr.adr_bits.mdio_clc = NGE_SET; 886f3e57acSmx205022 nge_reg_put16(ngep, NGE_MDIO_ADR, mdio_adr.adr_val); 896f3e57acSmx205022 drv_usecwait(100); 906f3e57acSmx205022 } 916f3e57acSmx205022 926f3e57acSmx205022 /* 936f3e57acSmx205022 * Assemble the operation cmd 946f3e57acSmx205022 */ 95a55f7119SMiles Xu, Sun Microsystems mdio_adr.adr_bits.phy_reg = (uint16_t)regno; 966f3e57acSmx205022 mdio_adr.adr_bits.phy_adr = ngep->phy_xmii_addr; 976f3e57acSmx205022 mdio_adr.adr_bits.mdio_rw = (cmd == NGE_MDIO_WRITE) ? 1 : 0; 986f3e57acSmx205022 996f3e57acSmx205022 1006f3e57acSmx205022 if (cmd == NGE_MDIO_WRITE) 1016f3e57acSmx205022 nge_reg_put16(ngep, NGE_MDIO_DATA, data); 1026f3e57acSmx205022 1036f3e57acSmx205022 nge_reg_put16(ngep, NGE_MDIO_ADR, mdio_adr.adr_val); 1046f3e57acSmx205022 1056f3e57acSmx205022 /* 1066f3e57acSmx205022 * To check whether the read/write operation is finished 1076f3e57acSmx205022 */ 1086f3e57acSmx205022 for (tries = 0; tries < 300; tries ++) { 1096f3e57acSmx205022 drv_usecwait(10); 1106f3e57acSmx205022 mdio_adr.adr_val = nge_reg_get16(ngep, NGE_MDIO_ADR); 1116f3e57acSmx205022 if (mdio_adr.adr_bits.mdio_clc == NGE_CLEAR) 1126f3e57acSmx205022 break; 1136f3e57acSmx205022 } 1146f3e57acSmx205022 if (tries == 300) 1156f3e57acSmx205022 return ((uint16_t)~0); 1166f3e57acSmx205022 1176f3e57acSmx205022 /* 1186f3e57acSmx205022 * Read the data from MDIO data register 1196f3e57acSmx205022 */ 1206f3e57acSmx205022 if (cmd == NGE_MDIO_READ) 1216f3e57acSmx205022 mdio_data = nge_reg_get16(ngep, NGE_MDIO_DATA); 1226f3e57acSmx205022 1236f3e57acSmx205022 /* 1246f3e57acSmx205022 * To check whether the read/write operation is valid 1256f3e57acSmx205022 */ 1266f3e57acSmx205022 intr_src.src_val = nge_reg_get8(ngep, NGE_MINTR_SRC); 1276f3e57acSmx205022 nge_reg_put8(ngep, NGE_MINTR_SRC, intr_src.src_val); 1286f3e57acSmx205022 if (intr_src.src_bits.mrei == NGE_SET) 1296f3e57acSmx205022 return ((uint16_t)~0); 1306f3e57acSmx205022 1316f3e57acSmx205022 return (mdio_data); 1326f3e57acSmx205022 } 1336f3e57acSmx205022 1346f3e57acSmx205022 uint16_t nge_mii_get16(nge_t *ngep, nge_regno_t regno); 1356f3e57acSmx205022 #pragma inline(nge_mii_get16) 1366f3e57acSmx205022 1376f3e57acSmx205022 uint16_t 1386f3e57acSmx205022 nge_mii_get16(nge_t *ngep, nge_regno_t regno) 1396f3e57acSmx205022 { 1406f3e57acSmx205022 1416f3e57acSmx205022 return (nge_mii_access(ngep, regno, 0, NGE_MDIO_READ)); 1426f3e57acSmx205022 } 1436f3e57acSmx205022 1446f3e57acSmx205022 void nge_mii_put16(nge_t *ngep, nge_regno_t regno, uint16_t data); 1456f3e57acSmx205022 #pragma inline(nge_mii_put16) 1466f3e57acSmx205022 1476f3e57acSmx205022 void 1486f3e57acSmx205022 nge_mii_put16(nge_t *ngep, nge_regno_t regno, uint16_t data) 1496f3e57acSmx205022 { 1506f3e57acSmx205022 1516f3e57acSmx205022 (void) nge_mii_access(ngep, regno, data, NGE_MDIO_WRITE); 1526f3e57acSmx205022 } 1536f3e57acSmx205022 1546f3e57acSmx205022 /* 1556f3e57acSmx205022 * Basic low-level function to probe for a PHY 1566f3e57acSmx205022 * 1576f3e57acSmx205022 * Returns TRUE if the PHY responds with valid data, FALSE otherwise 1586f3e57acSmx205022 */ 1596f3e57acSmx205022 static boolean_t 1606f3e57acSmx205022 nge_phy_probe(nge_t *ngep) 1616f3e57acSmx205022 { 1626f3e57acSmx205022 int i; 1636f3e57acSmx205022 uint16_t phy_status; 1646f3e57acSmx205022 uint16_t phyidh; 1656f3e57acSmx205022 uint16_t phyidl; 1666f3e57acSmx205022 1676f3e57acSmx205022 NGE_TRACE(("nge_phy_probe($%p)", (void *)ngep)); 1686f3e57acSmx205022 1696f3e57acSmx205022 /* 1706f3e57acSmx205022 * Scan the phys to find the right address 1716f3e57acSmx205022 * of the phy 1726f3e57acSmx205022 * 1736f3e57acSmx205022 * Probe maximum for 32 phy addresses 1746f3e57acSmx205022 */ 1756f3e57acSmx205022 for (i = 0; i < NGE_PHY_NUMBER; i++) { 1766f3e57acSmx205022 ngep->phy_xmii_addr = i; 1776f3e57acSmx205022 /* 1786f3e57acSmx205022 * Read the MII_STATUS register twice, in 1796f3e57acSmx205022 * order to clear any sticky bits (but they should 1806f3e57acSmx205022 * have been cleared by the RESET, I think). 1816f3e57acSmx205022 */ 1826f3e57acSmx205022 phy_status = nge_mii_get16(ngep, MII_STATUS); 1836f3e57acSmx205022 phy_status = nge_mii_get16(ngep, MII_STATUS); 1846f3e57acSmx205022 if (phy_status != 0xffff) { 1856f3e57acSmx205022 phyidh = nge_mii_get16(ngep, MII_PHYIDH); 1866f3e57acSmx205022 phyidl = nge_mii_get16(ngep, MII_PHYIDL); 1876f3e57acSmx205022 ngep->phy_id = 1886f3e57acSmx205022 (((uint32_t)phyidh << 16) |(phyidl & MII_IDL_MASK)); 1896f3e57acSmx205022 NGE_DEBUG(("nge_phy_probe: status 0x%x, phy id 0x%x", 1906f3e57acSmx205022 phy_status, ngep->phy_id)); 1916f3e57acSmx205022 1926f3e57acSmx205022 return (B_TRUE); 1936f3e57acSmx205022 } 1946f3e57acSmx205022 } 1956f3e57acSmx205022 1966f3e57acSmx205022 return (B_FALSE); 1976f3e57acSmx205022 } 1986f3e57acSmx205022 1996f3e57acSmx205022 2006f3e57acSmx205022 /* 2016f3e57acSmx205022 * Basic low-level function to powerup the phy and remove the isolation 2026f3e57acSmx205022 */ 2036f3e57acSmx205022 2046f3e57acSmx205022 static boolean_t 2056f3e57acSmx205022 nge_phy_recover(nge_t *ngep) 2066f3e57acSmx205022 { 2076f3e57acSmx205022 uint16_t control; 2086f3e57acSmx205022 uint16_t count; 2096f3e57acSmx205022 2106f3e57acSmx205022 NGE_TRACE(("nge_phy_recover($%p)", (void *)ngep)); 2116f3e57acSmx205022 control = nge_mii_get16(ngep, MII_CONTROL); 2126f3e57acSmx205022 control &= ~(MII_CONTROL_PWRDN | MII_CONTROL_ISOLATE); 2136f3e57acSmx205022 nge_mii_put16(ngep, MII_CONTROL, control); 2146f3e57acSmx205022 for (count = 0; ++count < 10; ) { 2156f3e57acSmx205022 drv_usecwait(5); 2166f3e57acSmx205022 control = nge_mii_get16(ngep, MII_CONTROL); 2176f3e57acSmx205022 if (BIC(control, MII_CONTROL_PWRDN)) 2186f3e57acSmx205022 return (B_TRUE); 2196f3e57acSmx205022 } 2206f3e57acSmx205022 2216f3e57acSmx205022 return (B_FALSE); 2226f3e57acSmx205022 } 2236f3e57acSmx205022 /* 2246f3e57acSmx205022 * Basic low-level function to reset the PHY. 2256f3e57acSmx205022 * Doesn't incorporate any special-case workarounds. 2266f3e57acSmx205022 * 2276f3e57acSmx205022 * Returns TRUE on success, FALSE if the RESET bit doesn't clear 2286f3e57acSmx205022 */ 2296f3e57acSmx205022 boolean_t 2306f3e57acSmx205022 nge_phy_reset(nge_t *ngep) 2316f3e57acSmx205022 { 2326f3e57acSmx205022 uint16_t control; 2336f3e57acSmx205022 uint_t count; 2346f3e57acSmx205022 2356f3e57acSmx205022 NGE_TRACE(("nge_phy_reset($%p)", (void *)ngep)); 2366f3e57acSmx205022 2376f3e57acSmx205022 ASSERT(mutex_owned(ngep->genlock)); 2386f3e57acSmx205022 2396f3e57acSmx205022 /* 2406f3e57acSmx205022 * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear 2416f3e57acSmx205022 */ 2426f3e57acSmx205022 control = nge_mii_get16(ngep, MII_CONTROL); 2436f3e57acSmx205022 control |= MII_CONTROL_RESET; 2446f3e57acSmx205022 nge_mii_put16(ngep, MII_CONTROL, control); 245c322ff79Smx205022 /* We should wait for 500ms. It's defined in the manual */ 2465164839fSmx205022 delay(drv_usectohz(500000)); 2476f3e57acSmx205022 for (count = 0; ++count < 10; ) { 2486f3e57acSmx205022 drv_usecwait(5); 2496f3e57acSmx205022 control = nge_mii_get16(ngep, MII_CONTROL); 2506f3e57acSmx205022 if (BIC(control, MII_CONTROL_RESET)) 2516f3e57acSmx205022 return (B_TRUE); 2526f3e57acSmx205022 } 2536f3e57acSmx205022 NGE_DEBUG(("nge_phy_reset: FAILED, control now 0x%x", control)); 2546f3e57acSmx205022 2556f3e57acSmx205022 return (B_FALSE); 2566f3e57acSmx205022 } 2576f3e57acSmx205022 258c322ff79Smx205022 static boolean_t 2596f3e57acSmx205022 nge_phy_restart(nge_t *ngep) 2606f3e57acSmx205022 { 2616f3e57acSmx205022 uint16_t mii_reg; 2626f3e57acSmx205022 263c322ff79Smx205022 if (!nge_phy_recover(ngep)) 264c322ff79Smx205022 return (B_FALSE); 265c322ff79Smx205022 if (!nge_phy_reset(ngep)) 266c322ff79Smx205022 return (B_FALSE); 267c322ff79Smx205022 268*bdb9230aSGarrett D'Amore if (MII_PHY_MFG(ngep->phy_id) == MII_ID_CICADA) { 2696f3e57acSmx205022 if (ngep->phy_mode == RGMII_IN) { 2706f3e57acSmx205022 mii_reg = nge_mii_get16(ngep, 2716f3e57acSmx205022 MII_CICADA_EXT_CONTROL); 2726f3e57acSmx205022 mii_reg &= ~(MII_CICADA_MODE_SELECT_BITS 2736f3e57acSmx205022 | MII_CICADA_POWER_SUPPLY_BITS); 2746f3e57acSmx205022 mii_reg |= (MII_CICADA_MODE_SELECT_RGMII 2756f3e57acSmx205022 | MII_CICADA_POWER_SUPPLY_2_5V); 2766f3e57acSmx205022 nge_mii_put16(ngep, MII_CICADA_EXT_CONTROL, mii_reg); 2776f3e57acSmx205022 2786f3e57acSmx205022 mii_reg = nge_mii_get16(ngep, 2796f3e57acSmx205022 MII_CICADA_AUXCTRL_STATUS); 2806f3e57acSmx205022 mii_reg |= MII_CICADA_PIN_PRORITY_SETTING; 2816f3e57acSmx205022 nge_mii_put16(ngep, MII_CICADA_AUXCTRL_STATUS, 2826f3e57acSmx205022 mii_reg); 2836f3e57acSmx205022 } else { 2846f3e57acSmx205022 mii_reg = nge_mii_get16(ngep, 2856f3e57acSmx205022 MII_CICADA_10BASET_CONTROL); 2866f3e57acSmx205022 mii_reg |= MII_CICADA_DISABLE_ECHO_MODE; 2876f3e57acSmx205022 nge_mii_put16(ngep, 2886f3e57acSmx205022 MII_CICADA_10BASET_CONTROL, mii_reg); 2896f3e57acSmx205022 2906f3e57acSmx205022 mii_reg = nge_mii_get16(ngep, 2916f3e57acSmx205022 MII_CICADA_BYPASS_CONTROL); 2926f3e57acSmx205022 mii_reg &= (~CICADA_125MHZ_CLOCK_ENABLE); 2936f3e57acSmx205022 nge_mii_put16(ngep, MII_CICADA_BYPASS_CONTROL, mii_reg); 2946f3e57acSmx205022 } 2956f3e57acSmx205022 } 296c322ff79Smx205022 297c322ff79Smx205022 return (B_TRUE); 2986f3e57acSmx205022 } 2996f3e57acSmx205022 3006f3e57acSmx205022 /* 3016f3e57acSmx205022 * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities 3026f3e57acSmx205022 * and advertisements with the required settings as specified by the various 3036f3e57acSmx205022 * param_* variables that can be poked via the NDD interface. 3046f3e57acSmx205022 * 3056f3e57acSmx205022 * We always reset the PHY and reprogram *all* the relevant registers, 3066f3e57acSmx205022 * not just those changed. This should cause the link to go down, and then 3076f3e57acSmx205022 * back up again once the link is stable and autonegotiation (if enabled) 3086f3e57acSmx205022 * is complete. We should get a link state change interrupt somewhere along 3096f3e57acSmx205022 * the way ... 3106f3e57acSmx205022 * 3116f3e57acSmx205022 * NOTE: <genlock> must already be held by the caller 3126f3e57acSmx205022 */ 3136f3e57acSmx205022 static void 3146f3e57acSmx205022 nge_update_copper(nge_t *ngep) 3156f3e57acSmx205022 { 3166f3e57acSmx205022 uint16_t control; 3176f3e57acSmx205022 uint16_t gigctrl; 3186f3e57acSmx205022 uint16_t anar; 3196f3e57acSmx205022 boolean_t adv_autoneg; 3206f3e57acSmx205022 boolean_t adv_pause; 3216f3e57acSmx205022 boolean_t adv_asym_pause; 3226f3e57acSmx205022 boolean_t adv_1000fdx; 3236f3e57acSmx205022 boolean_t adv_100fdx; 3246f3e57acSmx205022 boolean_t adv_100hdx; 3256f3e57acSmx205022 boolean_t adv_10fdx; 3266f3e57acSmx205022 boolean_t adv_10hdx; 3276f3e57acSmx205022 3286f3e57acSmx205022 NGE_TRACE(("nge_update_copper($%p)", (void *)ngep)); 3296f3e57acSmx205022 3306f3e57acSmx205022 ASSERT(mutex_owned(ngep->genlock)); 3316f3e57acSmx205022 3326f3e57acSmx205022 NGE_DEBUG(("nge_update_copper: autoneg %d " 3336f3e57acSmx205022 "pause %d asym_pause %d " 3346f3e57acSmx205022 "1000fdx %d " 3356f3e57acSmx205022 "100fdx %d 100hdx %d " 3366f3e57acSmx205022 "10fdx %d 10hdx %d ", 3376f3e57acSmx205022 ngep->param_adv_autoneg, 3386f3e57acSmx205022 ngep->param_adv_pause, ngep->param_adv_asym_pause, 3396f3e57acSmx205022 ngep->param_adv_1000fdx, 3406f3e57acSmx205022 ngep->param_adv_100fdx, ngep->param_adv_100hdx, 3416f3e57acSmx205022 ngep->param_adv_10fdx, ngep->param_adv_10hdx)); 3426f3e57acSmx205022 3436f3e57acSmx205022 control = anar = gigctrl = 0; 3446f3e57acSmx205022 3456f3e57acSmx205022 /* 3466f3e57acSmx205022 * PHY settings are normally based on the param_* variables, 3476f3e57acSmx205022 * but if any loopback mode is in effect, that takes precedence. 3486f3e57acSmx205022 * 3496f3e57acSmx205022 * NGE supports MAC-internal loopback, PHY-internal loopback, 3506f3e57acSmx205022 * and External loopback at a variety of speeds (with a special 3516f3e57acSmx205022 * cable). In all cases, autoneg is turned OFF, full-duplex 3526f3e57acSmx205022 * is turned ON, and the speed/mastership is forced. 3536f3e57acSmx205022 */ 3546f3e57acSmx205022 switch (ngep->param_loop_mode) { 3556f3e57acSmx205022 case NGE_LOOP_NONE: 3566f3e57acSmx205022 default: 3576f3e57acSmx205022 adv_pause = ngep->param_adv_pause; 3586f3e57acSmx205022 adv_autoneg = ngep->param_adv_autoneg; 3596f3e57acSmx205022 adv_asym_pause = ngep->param_adv_asym_pause; 3606f3e57acSmx205022 if (ngep->phy_mode == MII_IN) { 3616f3e57acSmx205022 adv_1000fdx = ngep->param_adv_1000fdx = B_FALSE; 3626f3e57acSmx205022 } 3636f3e57acSmx205022 adv_1000fdx = ngep->param_adv_1000fdx; 3646f3e57acSmx205022 adv_100fdx = ngep->param_adv_100fdx; 3656f3e57acSmx205022 adv_100hdx = ngep->param_adv_100hdx; 3666f3e57acSmx205022 adv_10fdx = ngep->param_adv_10fdx; 3676f3e57acSmx205022 adv_10hdx = ngep->param_adv_10hdx; 3686f3e57acSmx205022 3696f3e57acSmx205022 break; 3706f3e57acSmx205022 3716f3e57acSmx205022 case NGE_LOOP_EXTERNAL_100: 3726f3e57acSmx205022 case NGE_LOOP_EXTERNAL_10: 3736f3e57acSmx205022 case NGE_LOOP_INTERNAL_PHY: 3746f3e57acSmx205022 adv_autoneg = adv_pause = adv_asym_pause = B_FALSE; 3756f3e57acSmx205022 adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE; 3766f3e57acSmx205022 adv_100hdx = adv_10hdx = B_FALSE; 3776f3e57acSmx205022 ngep->param_link_duplex = LINK_DUPLEX_FULL; 3786f3e57acSmx205022 3796f3e57acSmx205022 switch (ngep->param_loop_mode) { 3806f3e57acSmx205022 case NGE_LOOP_EXTERNAL_100: 3816f3e57acSmx205022 ngep->param_link_speed = 100; 3826f3e57acSmx205022 adv_100fdx = B_TRUE; 3836f3e57acSmx205022 break; 3846f3e57acSmx205022 3856f3e57acSmx205022 case NGE_LOOP_EXTERNAL_10: 3866f3e57acSmx205022 ngep->param_link_speed = 10; 3876f3e57acSmx205022 adv_10fdx = B_TRUE; 3886f3e57acSmx205022 break; 3896f3e57acSmx205022 3906f3e57acSmx205022 case NGE_LOOP_INTERNAL_PHY: 3916f3e57acSmx205022 ngep->param_link_speed = 1000; 3926f3e57acSmx205022 adv_1000fdx = B_TRUE; 3936f3e57acSmx205022 break; 3946f3e57acSmx205022 3956f3e57acSmx205022 } 3966f3e57acSmx205022 } 3976f3e57acSmx205022 NGE_DEBUG(("nge_update_copper: autoneg %d " 3986f3e57acSmx205022 "pause %d asym_pause %d " 3996f3e57acSmx205022 "1000fdx %d " 4006f3e57acSmx205022 "100fdx %d 100hdx %d " 4016f3e57acSmx205022 "10fdx %d 10hdx %d ", 4026f3e57acSmx205022 adv_autoneg, 4036f3e57acSmx205022 adv_pause, adv_asym_pause, 4046f3e57acSmx205022 adv_1000fdx, 4056f3e57acSmx205022 adv_100fdx, adv_100hdx, 4066f3e57acSmx205022 adv_10fdx, adv_10hdx)); 4076f3e57acSmx205022 4086f3e57acSmx205022 /* 4096f3e57acSmx205022 * We should have at least one technology capability set; 4106f3e57acSmx205022 * if not, we select a default of 10Mb/s half-duplex 4116f3e57acSmx205022 */ 4126f3e57acSmx205022 if (!adv_1000fdx && !adv_100fdx && !adv_10fdx && 4136f3e57acSmx205022 !adv_100hdx && !adv_10hdx) 4146f3e57acSmx205022 adv_10hdx = B_TRUE; 4156f3e57acSmx205022 4166f3e57acSmx205022 /* 4176f3e57acSmx205022 * Now transform the adv_* variables into the proper settings 4186f3e57acSmx205022 * of the PHY registers ... 4196f3e57acSmx205022 * 4206f3e57acSmx205022 * If autonegotiation is (now) enabled, we want to trigger 4216f3e57acSmx205022 * a new autonegotiation cycle once the PHY has been 4226f3e57acSmx205022 * programmed with the capabilities to be advertised. 4236f3e57acSmx205022 */ 4246f3e57acSmx205022 if (adv_autoneg) 4256f3e57acSmx205022 control |= MII_CONTROL_ANE|MII_CONTROL_RSAN; 4266f3e57acSmx205022 4276f3e57acSmx205022 if (adv_1000fdx) 4286f3e57acSmx205022 control |= MII_CONTROL_1000MB|MII_CONTROL_FDUPLEX; 4296f3e57acSmx205022 else if (adv_100fdx) 4306f3e57acSmx205022 control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX; 4316f3e57acSmx205022 else if (adv_100hdx) 4326f3e57acSmx205022 control |= MII_CONTROL_100MB; 4336f3e57acSmx205022 else if (adv_10fdx) 4346f3e57acSmx205022 control |= MII_CONTROL_FDUPLEX; 4356f3e57acSmx205022 else if (adv_10hdx) 4366f3e57acSmx205022 control |= 0; 4376f3e57acSmx205022 else 4386f3e57acSmx205022 { _NOTE(EMPTY); } /* Can't get here anyway ... */ 4396f3e57acSmx205022 4406f3e57acSmx205022 if (adv_1000fdx) 4416f3e57acSmx205022 gigctrl |= MII_1000BT_CTL_ADV_FDX; 4426f3e57acSmx205022 if (adv_100fdx) 4436f3e57acSmx205022 anar |= MII_ABILITY_100BASE_TX_FD; 4446f3e57acSmx205022 if (adv_100hdx) 4456f3e57acSmx205022 anar |= MII_ABILITY_100BASE_TX; 4466f3e57acSmx205022 if (adv_10fdx) 4476f3e57acSmx205022 anar |= MII_ABILITY_10BASE_T_FD; 4486f3e57acSmx205022 if (adv_10hdx) 4496f3e57acSmx205022 anar |= MII_ABILITY_10BASE_T; 4506f3e57acSmx205022 4516f3e57acSmx205022 if (adv_pause) 4526f3e57acSmx205022 anar |= MII_ABILITY_PAUSE; 4536f3e57acSmx205022 if (adv_asym_pause) 454*bdb9230aSGarrett D'Amore anar |= MII_ABILITY_ASMPAUSE; 4556f3e57acSmx205022 4566f3e57acSmx205022 /* 4576f3e57acSmx205022 * Munge in any other fixed bits we require ... 4586f3e57acSmx205022 */ 4596f3e57acSmx205022 anar |= MII_AN_SELECTOR_8023; 4606f3e57acSmx205022 4616f3e57acSmx205022 /* 4626f3e57acSmx205022 * Restart the PHY and write the new values. 4636f3e57acSmx205022 */ 4646f3e57acSmx205022 nge_mii_put16(ngep, MII_AN_ADVERT, anar); 4656f3e57acSmx205022 nge_mii_put16(ngep, MII_CONTROL, control); 4666f3e57acSmx205022 nge_mii_put16(ngep, MII_1000BASE_T_CONTROL, gigctrl); 467c322ff79Smx205022 if (!nge_phy_restart(ngep)) 468c322ff79Smx205022 nge_error(ngep, "nge_update_copper: failed to restart phy"); 4696f3e57acSmx205022 /* 4706f3e57acSmx205022 * Loopback bit in control register is not reset sticky 4716f3e57acSmx205022 * write it after PHY restart. 4726f3e57acSmx205022 */ 4736f3e57acSmx205022 if (ngep->param_loop_mode == NGE_LOOP_INTERNAL_PHY) { 4746f3e57acSmx205022 control = nge_mii_get16(ngep, MII_CONTROL); 4756f3e57acSmx205022 control |= MII_CONTROL_LOOPBACK; 4766f3e57acSmx205022 nge_mii_put16(ngep, MII_CONTROL, control); 4776f3e57acSmx205022 } 4786f3e57acSmx205022 } 4796f3e57acSmx205022 4806f3e57acSmx205022 static boolean_t 4816f3e57acSmx205022 nge_check_copper(nge_t *ngep) 4826f3e57acSmx205022 { 4836f3e57acSmx205022 uint16_t mii_status; 4846f3e57acSmx205022 uint16_t mii_exstatus; 4856f3e57acSmx205022 uint16_t mii_excontrol; 4866f3e57acSmx205022 uint16_t anar; 4876f3e57acSmx205022 uint16_t lpan; 4886f3e57acSmx205022 uint_t speed; 4896f3e57acSmx205022 uint_t duplex; 4906f3e57acSmx205022 boolean_t linkup; 4916f3e57acSmx205022 nge_mii_cs mii_cs; 4926f3e57acSmx205022 nge_mintr_src mintr_src; 4936f3e57acSmx205022 4946f3e57acSmx205022 speed = UNKOWN_SPEED; 4956f3e57acSmx205022 duplex = UNKOWN_DUPLEX; 4966f3e57acSmx205022 /* 4976f3e57acSmx205022 * Read the status from the PHY (which is self-clearing 4986f3e57acSmx205022 * on read!); also read & clear the main (Ethernet) MAC status 4996f3e57acSmx205022 * (the relevant bits of this are write-one-to-clear). 5006f3e57acSmx205022 */ 5016f3e57acSmx205022 mii_status = nge_mii_get16(ngep, MII_STATUS); 5026f3e57acSmx205022 mii_cs.cs_val = nge_reg_get32(ngep, NGE_MII_CS); 5036f3e57acSmx205022 mintr_src.src_val = nge_reg_get32(ngep, NGE_MINTR_SRC); 5046f3e57acSmx205022 nge_reg_put32(ngep, NGE_MINTR_SRC, mintr_src.src_val); 5056f3e57acSmx205022 5066f3e57acSmx205022 NGE_DEBUG(("nge_check_copper: link %d/%s, MII status 0x%x " 5076f3e57acSmx205022 "(was 0x%x)", ngep->link_state, 5086f3e57acSmx205022 UPORDOWN(ngep->param_link_up), mii_status, 5096f3e57acSmx205022 ngep->phy_gen_status)); 5106f3e57acSmx205022 5116f3e57acSmx205022 do { 5126f3e57acSmx205022 /* 5136f3e57acSmx205022 * If the PHY status changed, record the time 5146f3e57acSmx205022 */ 5156f3e57acSmx205022 switch (ngep->phy_mode) { 5166f3e57acSmx205022 default: 5176f3e57acSmx205022 case RGMII_IN: 5186f3e57acSmx205022 5196f3e57acSmx205022 /* 5206f3e57acSmx205022 * Judge the giga speed by reading control 5216f3e57acSmx205022 * and status register 5226f3e57acSmx205022 */ 5236f3e57acSmx205022 mii_excontrol = nge_mii_get16(ngep, 5246f3e57acSmx205022 MII_1000BASE_T_CONTROL); 5256f3e57acSmx205022 mii_exstatus = nge_mii_get16(ngep, 5266f3e57acSmx205022 MII_1000BASE_T_STATUS); 5276f3e57acSmx205022 if ((mii_excontrol & MII_1000BT_CTL_ADV_FDX) && 5286f3e57acSmx205022 (mii_exstatus & MII_1000BT_STAT_LP_FDX_CAP)) { 5296f3e57acSmx205022 speed = NGE_1000M; 5306f3e57acSmx205022 duplex = NGE_FD; 5316f3e57acSmx205022 } else { 5326f3e57acSmx205022 anar = nge_mii_get16(ngep, MII_AN_ADVERT); 5336f3e57acSmx205022 lpan = nge_mii_get16(ngep, MII_AN_LPABLE); 5346f3e57acSmx205022 if (lpan != 0) 5356f3e57acSmx205022 anar = (anar & lpan); 5366f3e57acSmx205022 if (anar & MII_100BASET_FD) { 5376f3e57acSmx205022 speed = NGE_100M; 5386f3e57acSmx205022 duplex = NGE_FD; 5396f3e57acSmx205022 } else if (anar & MII_100BASET_HD) { 5406f3e57acSmx205022 speed = NGE_100M; 5416f3e57acSmx205022 duplex = NGE_HD; 5426f3e57acSmx205022 } else if (anar & MII_10BASET_FD) { 5436f3e57acSmx205022 speed = NGE_10M; 5446f3e57acSmx205022 duplex = NGE_FD; 5456f3e57acSmx205022 } else if (anar & MII_10BASET_HD) { 5466f3e57acSmx205022 speed = NGE_10M; 5476f3e57acSmx205022 duplex = NGE_HD; 5486f3e57acSmx205022 } 5496f3e57acSmx205022 } 5506f3e57acSmx205022 break; 5516f3e57acSmx205022 case MII_IN: 5526f3e57acSmx205022 anar = nge_mii_get16(ngep, MII_AN_ADVERT); 5536f3e57acSmx205022 lpan = nge_mii_get16(ngep, MII_AN_LPABLE); 5546f3e57acSmx205022 if (lpan != 0) 5556f3e57acSmx205022 anar = (anar & lpan); 5566f3e57acSmx205022 5576f3e57acSmx205022 if (anar & MII_100BASET_FD) { 5586f3e57acSmx205022 speed = NGE_100M; 5596f3e57acSmx205022 duplex = NGE_FD; 5606f3e57acSmx205022 } else if (anar & MII_100BASET_HD) { 5616f3e57acSmx205022 speed = NGE_100M; 5626f3e57acSmx205022 duplex = NGE_HD; 5636f3e57acSmx205022 } else if (anar & MII_10BASET_FD) { 5646f3e57acSmx205022 speed = NGE_10M; 5656f3e57acSmx205022 duplex = NGE_FD; 5666f3e57acSmx205022 } else if (anar & MII_10BASET_HD) { 5676f3e57acSmx205022 speed = NGE_10M; 5686f3e57acSmx205022 duplex = NGE_HD; 5696f3e57acSmx205022 } 5706f3e57acSmx205022 break; 5716f3e57acSmx205022 } 5726f3e57acSmx205022 5736f3e57acSmx205022 5746f3e57acSmx205022 /* 5756f3e57acSmx205022 * We will only consider the link UP if all the readings 5766f3e57acSmx205022 * are consistent and give meaningful results ... 5776f3e57acSmx205022 */ 5786f3e57acSmx205022 linkup = nge_copper_link_speed[speed] > 0; 5796f3e57acSmx205022 linkup &= nge_copper_link_duplex[duplex] != LINK_DUPLEX_UNKNOWN; 5806f3e57acSmx205022 linkup &= BIS(mii_status, MII_STATUS_LINKUP); 5816f3e57acSmx205022 linkup &= BIS(mii_cs.cs_val, MII_STATUS_LINKUP); 5826f3e57acSmx205022 5836f3e57acSmx205022 /* 5846f3e57acSmx205022 * Record current register values, then reread status 5856f3e57acSmx205022 * register & loop until it stabilises ... 5866f3e57acSmx205022 */ 5876f3e57acSmx205022 ngep->phy_gen_status = mii_status; 5886f3e57acSmx205022 mii_status = nge_mii_get16(ngep, MII_STATUS); 5896f3e57acSmx205022 } while (mii_status != ngep->phy_gen_status); 5906f3e57acSmx205022 5916f3e57acSmx205022 /* Get the Link Partner Ability */ 5926f3e57acSmx205022 mii_exstatus = nge_mii_get16(ngep, MII_1000BASE_T_STATUS); 5936f3e57acSmx205022 lpan = nge_mii_get16(ngep, MII_AN_LPABLE); 5946f3e57acSmx205022 if (mii_exstatus & MII_1000BT_STAT_LP_FDX_CAP) { 5956f3e57acSmx205022 ngep->param_lp_autoneg = B_TRUE; 5966f3e57acSmx205022 ngep->param_link_autoneg = B_TRUE; 5976f3e57acSmx205022 ngep->param_lp_1000fdx = B_TRUE; 5986f3e57acSmx205022 } 5996f3e57acSmx205022 if (mii_exstatus & MII_1000BT_STAT_LP_HDX_CAP) { 6006f3e57acSmx205022 ngep->param_lp_autoneg = B_TRUE; 6016f3e57acSmx205022 ngep->param_link_autoneg = B_TRUE; 6026f3e57acSmx205022 ngep->param_lp_1000hdx = B_TRUE; 6036f3e57acSmx205022 } 6046f3e57acSmx205022 if (lpan & MII_100BASET_FD) 6056f3e57acSmx205022 ngep->param_lp_100fdx = B_TRUE; 6066f3e57acSmx205022 if (lpan & MII_100BASET_HD) 6076f3e57acSmx205022 ngep->param_lp_100hdx = B_TRUE; 6086f3e57acSmx205022 if (lpan & MII_10BASET_FD) 6096f3e57acSmx205022 ngep->param_lp_10fdx = B_TRUE; 6106f3e57acSmx205022 if (lpan & MII_10BASET_HD) 6116f3e57acSmx205022 ngep->param_lp_10hdx = B_TRUE; 6126f3e57acSmx205022 if (lpan & MII_LP_ASYM_PAUSE) 6136f3e57acSmx205022 ngep->param_lp_asym_pause = B_TRUE; 6146f3e57acSmx205022 if (lpan & MII_LP_PAUSE) 6156f3e57acSmx205022 ngep->param_lp_pause = B_TRUE; 6166f3e57acSmx205022 if (linkup) { 6176f3e57acSmx205022 ngep->param_link_up = linkup; 6186f3e57acSmx205022 ngep->param_link_speed = nge_copper_link_speed[speed]; 6196f3e57acSmx205022 ngep->param_link_duplex = nge_copper_link_duplex[duplex]; 6206f3e57acSmx205022 } else { 6216f3e57acSmx205022 ngep->param_link_up = B_FALSE; 6226f3e57acSmx205022 ngep->param_link_speed = 0; 6236f3e57acSmx205022 ngep->param_link_duplex = LINK_DUPLEX_UNKNOWN; 6246f3e57acSmx205022 } 6256f3e57acSmx205022 NGE_DEBUG(("nge_check_copper: link now %s speed %d duplex %d", 6266f3e57acSmx205022 UPORDOWN(ngep->param_link_up), 6276f3e57acSmx205022 ngep->param_link_speed, 6286f3e57acSmx205022 ngep->param_link_duplex)); 6296f3e57acSmx205022 6306f3e57acSmx205022 return (B_FALSE); 6316f3e57acSmx205022 } 6326f3e57acSmx205022 6336f3e57acSmx205022 /* 6346f3e57acSmx205022 * Because the network chipset embedded in Ck8-04 bridge is only a mac chipset, 6356f3e57acSmx205022 * the different vendor can use different media(serdes and copper). 6366f3e57acSmx205022 * To make it easier to extend the driver to support more platforms with ck8-04, 6376f3e57acSmx205022 * For example, one platform with serdes support, 6386f3e57acSmx205022 * wrapper phy operation functions. 6396f3e57acSmx205022 * But now, only supply copper phy operations. 6406f3e57acSmx205022 */ 6416f3e57acSmx205022 static const phys_ops_t copper_ops = { 6426f3e57acSmx205022 nge_phy_restart, 6436f3e57acSmx205022 nge_update_copper, 6446f3e57acSmx205022 nge_check_copper 6456f3e57acSmx205022 }; 6466f3e57acSmx205022 6476f3e57acSmx205022 /* 6486f3e57acSmx205022 * Here we have to determine which media we're using (copper or serdes). 6496f3e57acSmx205022 * Once that's done, we can initialise the physical layer appropriately. 6506f3e57acSmx205022 */ 6516f3e57acSmx205022 void 6526f3e57acSmx205022 nge_phys_init(nge_t *ngep) 6536f3e57acSmx205022 { 6546f3e57acSmx205022 nge_mac2phy m2p; 6556f3e57acSmx205022 NGE_TRACE(("nge_phys_init($%p)", (void *)ngep)); 6566f3e57acSmx205022 6576f3e57acSmx205022 /* Get the phy type from MAC2PHY register */ 6586f3e57acSmx205022 m2p.m2p_val = nge_reg_get32(ngep, NGE_MAC2PHY); 6596f3e57acSmx205022 ngep->phy_mode = m2p.m2p_bits.in_type; 6606f3e57acSmx205022 if ((ngep->phy_mode != RGMII_IN) && (ngep->phy_mode != MII_IN)) { 6616f3e57acSmx205022 ngep->phy_mode = RGMII_IN; 6626f3e57acSmx205022 m2p.m2p_bits.in_type = RGMII_IN; 6636f3e57acSmx205022 nge_reg_put32(ngep, NGE_MAC2PHY, m2p.m2p_val); 6646f3e57acSmx205022 } 6656f3e57acSmx205022 6666f3e57acSmx205022 /* 6676f3e57acSmx205022 * Probe for the type of the PHY. 6686f3e57acSmx205022 */ 6696f3e57acSmx205022 ngep->phy_xmii_addr = 1; 6706f3e57acSmx205022 (void) nge_phy_probe(ngep); 6716f3e57acSmx205022 ngep->chipinfo.flags |= CHIP_FLAG_COPPER; 6726f3e57acSmx205022 ngep->physops = &copper_ops; 6736f3e57acSmx205022 (*(ngep->physops->phys_restart))(ngep); 6746f3e57acSmx205022 } 675