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
nge_mii_access(nge_t * ngep,nge_regno_t regno,uint16_t data,uint32_t cmd)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
nge_mii_get16(nge_t * ngep,nge_regno_t regno)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
nge_mii_put16(nge_t * ngep,nge_regno_t regno,uint16_t data)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
nge_phy_probe(nge_t * ngep)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
nge_phy_recover(nge_t * ngep)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
nge_phy_reset(nge_t * ngep)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
nge_phy_restart(nge_t * ngep)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
nge_update_copper(nge_t * ngep)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
nge_check_copper(nge_t * ngep)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
nge_phys_init(nge_t * ngep)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