10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51369Sdduvall * Common Development and Distribution License (the "License").
61369Sdduvall * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
211369Sdduvall
220Sstevel@tonic-gate /*
23*13026SYong.Tan@Sun.COM * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
262675Szh199473 #include "bge_impl.h"
270Sstevel@tonic-gate
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate * Bit test macros, returning boolean_t values
300Sstevel@tonic-gate */
310Sstevel@tonic-gate #define BIS(w, b) (((w) & (b)) ? B_TRUE : B_FALSE)
320Sstevel@tonic-gate #define BIC(w, b) (((w) & (b)) ? B_FALSE : B_TRUE)
330Sstevel@tonic-gate #define UPORDOWN(x) ((x) ? "up" : "down")
340Sstevel@tonic-gate
350Sstevel@tonic-gate /*
360Sstevel@tonic-gate * ========== Copper (PHY) support ==========
370Sstevel@tonic-gate */
380Sstevel@tonic-gate
390Sstevel@tonic-gate #define BGE_DBG BGE_DBG_PHY /* debug flag for this code */
400Sstevel@tonic-gate
410Sstevel@tonic-gate /*
420Sstevel@tonic-gate * #defines:
430Sstevel@tonic-gate * BGE_COPPER_WIRESPEED controls whether the Broadcom WireSpeed(tm)
440Sstevel@tonic-gate * feature is enabled. We need to recheck whether this can be
450Sstevel@tonic-gate * enabled; at one time it seemed to interact unpleasantly with the
460Sstevel@tonic-gate * loopback modes.
470Sstevel@tonic-gate *
480Sstevel@tonic-gate * BGE_COPPER_IDLEOFF controls whether the (copper) PHY power is
490Sstevel@tonic-gate * turned off when the PHY is idled i.e. during driver suspend().
500Sstevel@tonic-gate * For now this is disabled because the chip doesn't seem to
510Sstevel@tonic-gate * resume cleanly if the PHY power is turned off.
520Sstevel@tonic-gate */
530Sstevel@tonic-gate #define BGE_COPPER_WIRESPEED B_TRUE
540Sstevel@tonic-gate #define BGE_COPPER_IDLEOFF B_FALSE
550Sstevel@tonic-gate
560Sstevel@tonic-gate /*
570Sstevel@tonic-gate * The arrays below can be indexed by the MODE bits from the Auxiliary
580Sstevel@tonic-gate * Status register to determine the current speed/duplex settings.
590Sstevel@tonic-gate */
600Sstevel@tonic-gate static const int16_t bge_copper_link_speed[] = {
610Sstevel@tonic-gate 0, /* MII_AUX_STATUS_MODE_NONE */
620Sstevel@tonic-gate 10, /* MII_AUX_STATUS_MODE_10_H */
630Sstevel@tonic-gate 10, /* MII_AUX_STATUS_MODE_10_F */
640Sstevel@tonic-gate 100, /* MII_AUX_STATUS_MODE_100_H */
650Sstevel@tonic-gate 0, /* MII_AUX_STATUS_MODE_100_4 */
660Sstevel@tonic-gate 100, /* MII_AUX_STATUS_MODE_100_F */
670Sstevel@tonic-gate 1000, /* MII_AUX_STATUS_MODE_1000_H */
680Sstevel@tonic-gate 1000 /* MII_AUX_STATUS_MODE_1000_F */
690Sstevel@tonic-gate };
700Sstevel@tonic-gate
710Sstevel@tonic-gate static const int8_t bge_copper_link_duplex[] = {
720Sstevel@tonic-gate LINK_DUPLEX_UNKNOWN, /* MII_AUX_STATUS_MODE_NONE */
730Sstevel@tonic-gate LINK_DUPLEX_HALF, /* MII_AUX_STATUS_MODE_10_H */
740Sstevel@tonic-gate LINK_DUPLEX_FULL, /* MII_AUX_STATUS_MODE_10_F */
750Sstevel@tonic-gate LINK_DUPLEX_HALF, /* MII_AUX_STATUS_MODE_100_H */
760Sstevel@tonic-gate LINK_DUPLEX_UNKNOWN, /* MII_AUX_STATUS_MODE_100_4 */
770Sstevel@tonic-gate LINK_DUPLEX_FULL, /* MII_AUX_STATUS_MODE_100_F */
780Sstevel@tonic-gate LINK_DUPLEX_HALF, /* MII_AUX_STATUS_MODE_1000_H */
790Sstevel@tonic-gate LINK_DUPLEX_FULL /* MII_AUX_STATUS_MODE_1000_F */
800Sstevel@tonic-gate };
810Sstevel@tonic-gate
827678SYong.Tan@Sun.COM static const int16_t bge_copper_link_speed_5906[] = {
837678SYong.Tan@Sun.COM 0, /* MII_AUX_STATUS_MODE_NONE */
847678SYong.Tan@Sun.COM 10, /* MII_AUX_STATUS_MODE_10_H */
857678SYong.Tan@Sun.COM 10, /* MII_AUX_STATUS_MODE_10_F */
867678SYong.Tan@Sun.COM 100, /* MII_AUX_STATUS_MODE_100_H */
877678SYong.Tan@Sun.COM 0, /* MII_AUX_STATUS_MODE_100_4 */
887678SYong.Tan@Sun.COM 100, /* MII_AUX_STATUS_MODE_100_F */
897678SYong.Tan@Sun.COM 0, /* MII_AUX_STATUS_MODE_1000_H */
907678SYong.Tan@Sun.COM 0 /* MII_AUX_STATUS_MODE_1000_F */
917678SYong.Tan@Sun.COM };
927678SYong.Tan@Sun.COM
937678SYong.Tan@Sun.COM static const int8_t bge_copper_link_duplex_5906[] = {
947678SYong.Tan@Sun.COM LINK_DUPLEX_UNKNOWN, /* MII_AUX_STATUS_MODE_NONE */
957678SYong.Tan@Sun.COM LINK_DUPLEX_HALF, /* MII_AUX_STATUS_MODE_10_H */
967678SYong.Tan@Sun.COM LINK_DUPLEX_FULL, /* MII_AUX_STATUS_MODE_10_F */
977678SYong.Tan@Sun.COM LINK_DUPLEX_HALF, /* MII_AUX_STATUS_MODE_100_H */
987678SYong.Tan@Sun.COM LINK_DUPLEX_UNKNOWN, /* MII_AUX_STATUS_MODE_100_4 */
997678SYong.Tan@Sun.COM LINK_DUPLEX_FULL, /* MII_AUX_STATUS_MODE_100_F */
1007678SYong.Tan@Sun.COM LINK_DUPLEX_UNKNOWN, /* MII_AUX_STATUS_MODE_1000_H */
1017678SYong.Tan@Sun.COM LINK_DUPLEX_UNKNOWN /* MII_AUX_STATUS_MODE_1000_F */
1027678SYong.Tan@Sun.COM };
1037678SYong.Tan@Sun.COM
1040Sstevel@tonic-gate #if BGE_DEBUGGING
1050Sstevel@tonic-gate
1060Sstevel@tonic-gate static void
bge_phydump(bge_t * bgep,uint16_t mii_status,uint16_t aux)1070Sstevel@tonic-gate bge_phydump(bge_t *bgep, uint16_t mii_status, uint16_t aux)
1080Sstevel@tonic-gate {
1090Sstevel@tonic-gate uint16_t regs[32];
1100Sstevel@tonic-gate int i;
1110Sstevel@tonic-gate
1120Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
1130Sstevel@tonic-gate
1140Sstevel@tonic-gate for (i = 0; i < 32; ++i)
1150Sstevel@tonic-gate switch (i) {
1160Sstevel@tonic-gate default:
1170Sstevel@tonic-gate regs[i] = bge_mii_get16(bgep, i);
1180Sstevel@tonic-gate break;
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate case MII_STATUS:
1210Sstevel@tonic-gate regs[i] = mii_status;
1220Sstevel@tonic-gate break;
1230Sstevel@tonic-gate
1240Sstevel@tonic-gate case MII_AUX_STATUS:
1250Sstevel@tonic-gate regs[i] = aux;
1260Sstevel@tonic-gate break;
1270Sstevel@tonic-gate
1280Sstevel@tonic-gate case 0x0b: case 0x0c: case 0x0d: case 0x0e:
1290Sstevel@tonic-gate case 0x15: case 0x16: case 0x17:
1300Sstevel@tonic-gate case 0x1c:
1310Sstevel@tonic-gate case 0x1f:
1320Sstevel@tonic-gate /* reserved registers -- don't read these */
1330Sstevel@tonic-gate regs[i] = 0;
1340Sstevel@tonic-gate break;
1350Sstevel@tonic-gate }
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate for (i = 0; i < 32; i += 8)
1380Sstevel@tonic-gate BGE_DEBUG(("bge_phydump: "
1396219Sgh162552 "0x%04x %04x %04x %04x %04x %04x %04x %04x",
1406219Sgh162552 regs[i+0], regs[i+1], regs[i+2], regs[i+3],
1416219Sgh162552 regs[i+4], regs[i+5], regs[i+6], regs[i+7]));
1420Sstevel@tonic-gate }
1430Sstevel@tonic-gate
1440Sstevel@tonic-gate #endif /* BGE_DEBUGGING */
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate /*
1470Sstevel@tonic-gate * Basic low-level function to probe for a PHY
1480Sstevel@tonic-gate *
1490Sstevel@tonic-gate * Returns TRUE if the PHY responds with valid data, FALSE otherwise
1500Sstevel@tonic-gate */
1510Sstevel@tonic-gate static boolean_t
bge_phy_probe(bge_t * bgep)1520Sstevel@tonic-gate bge_phy_probe(bge_t *bgep)
1530Sstevel@tonic-gate {
1546219Sgh162552 uint16_t miicfg;
1556219Sgh162552 uint32_t nicsig, niccfg;
1560Sstevel@tonic-gate
1570Sstevel@tonic-gate BGE_TRACE(("bge_phy_probe($%p)", (void *)bgep));
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
1600Sstevel@tonic-gate
1616219Sgh162552 nicsig = bge_nic_read32(bgep, BGE_NIC_DATA_SIG_ADDR);
1626219Sgh162552 if (nicsig == BGE_NIC_DATA_SIG) {
1636219Sgh162552 niccfg = bge_nic_read32(bgep, BGE_NIC_DATA_NIC_CFG_ADDR);
1646219Sgh162552 switch (niccfg & BGE_NIC_CFG_PHY_TYPE_MASK) {
1656219Sgh162552 default:
1666219Sgh162552 case BGE_NIC_CFG_PHY_TYPE_COPPER:
1676219Sgh162552 return (B_TRUE);
1686219Sgh162552 case BGE_NIC_CFG_PHY_TYPE_FIBER:
1696219Sgh162552 return (B_FALSE);
1706219Sgh162552 }
1716219Sgh162552 } else {
1726219Sgh162552 /*
1736219Sgh162552 * Read the MII_STATUS register twice, in
1746219Sgh162552 * order to clear any sticky bits (but they should
1756219Sgh162552 * have been cleared by the RESET, I think).
1766219Sgh162552 */
1776219Sgh162552 miicfg = bge_mii_get16(bgep, MII_STATUS);
1786219Sgh162552 miicfg = bge_mii_get16(bgep, MII_STATUS);
1796219Sgh162552 BGE_DEBUG(("bge_phy_probe: status 0x%x", miicfg));
1800Sstevel@tonic-gate
1816219Sgh162552 /*
1826219Sgh162552 * Now check the value read; it should have at least one bit set
1836219Sgh162552 * (for the device capabilities) and at least one clear (one of
1846219Sgh162552 * the error bits). So if we see all 0s or all 1s, there's a
1856219Sgh162552 * problem. In particular, bge_mii_get16() returns all 1s if
1866219Sgh162552 * communications fails ...
1876219Sgh162552 */
1886219Sgh162552 switch (miicfg) {
1896219Sgh162552 case 0x0000:
1906219Sgh162552 case 0xffff:
1916219Sgh162552 return (B_FALSE);
1920Sstevel@tonic-gate
1936219Sgh162552 default :
1946219Sgh162552 return (B_TRUE);
1956219Sgh162552 }
1960Sstevel@tonic-gate }
1970Sstevel@tonic-gate }
1980Sstevel@tonic-gate
1990Sstevel@tonic-gate /*
2000Sstevel@tonic-gate * Basic low-level function to reset the PHY.
2010Sstevel@tonic-gate * Doesn't incorporate any special-case workarounds.
2020Sstevel@tonic-gate *
2030Sstevel@tonic-gate * Returns TRUE on success, FALSE if the RESET bit doesn't clear
2040Sstevel@tonic-gate */
2050Sstevel@tonic-gate static boolean_t
bge_phy_reset(bge_t * bgep)2060Sstevel@tonic-gate bge_phy_reset(bge_t *bgep)
2070Sstevel@tonic-gate {
2080Sstevel@tonic-gate uint16_t control;
2090Sstevel@tonic-gate uint_t count;
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate BGE_TRACE(("bge_phy_reset($%p)", (void *)bgep));
2120Sstevel@tonic-gate
2130Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
2140Sstevel@tonic-gate
2157678SYong.Tan@Sun.COM if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
2167678SYong.Tan@Sun.COM drv_usecwait(40);
2177678SYong.Tan@Sun.COM /* put PHY into ready state */
2187678SYong.Tan@Sun.COM bge_reg_clr32(bgep, MISC_CONFIG_REG, MISC_CONFIG_EPHY_IDDQ);
2197678SYong.Tan@Sun.COM (void) bge_reg_get32(bgep, MISC_CONFIG_REG); /* flush */
2207678SYong.Tan@Sun.COM drv_usecwait(40);
2217678SYong.Tan@Sun.COM }
2227678SYong.Tan@Sun.COM
2230Sstevel@tonic-gate /*
2240Sstevel@tonic-gate * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear
2250Sstevel@tonic-gate */
2260Sstevel@tonic-gate bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_RESET);
2270Sstevel@tonic-gate for (count = 0; ++count < 1000; ) {
2280Sstevel@tonic-gate drv_usecwait(5);
2290Sstevel@tonic-gate control = bge_mii_get16(bgep, MII_CONTROL);
2300Sstevel@tonic-gate if (BIC(control, MII_CONTROL_RESET))
2310Sstevel@tonic-gate return (B_TRUE);
2320Sstevel@tonic-gate }
2330Sstevel@tonic-gate
2347678SYong.Tan@Sun.COM if (DEVICE_5906_SERIES_CHIPSETS(bgep))
2357808SYong.Tan@Sun.COM (void) bge_adj_volt_5906(bgep);
2367678SYong.Tan@Sun.COM
2370Sstevel@tonic-gate BGE_DEBUG(("bge_phy_reset: FAILED, control now 0x%x", control));
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate return (B_FALSE);
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate /*
2430Sstevel@tonic-gate * Basic low-level function to powerdown the PHY, if supported
2440Sstevel@tonic-gate * If powerdown support is compiled out, this function does nothing.
2450Sstevel@tonic-gate */
2460Sstevel@tonic-gate static void
bge_phy_powerdown(bge_t * bgep)2470Sstevel@tonic-gate bge_phy_powerdown(bge_t *bgep)
2480Sstevel@tonic-gate {
2490Sstevel@tonic-gate BGE_TRACE(("bge_phy_powerdown"));
2500Sstevel@tonic-gate #if BGE_COPPER_IDLEOFF
2510Sstevel@tonic-gate bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_PWRDN);
2520Sstevel@tonic-gate #endif /* BGE_COPPER_IDLEOFF */
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate
2550Sstevel@tonic-gate /*
2560Sstevel@tonic-gate * The following functions are based on sample code provided by
2570Sstevel@tonic-gate * Broadcom (20-June-2003), and implement workarounds said to be
2580Sstevel@tonic-gate * required on the early revisions of the BCM5703/4C.
2590Sstevel@tonic-gate *
2600Sstevel@tonic-gate * The registers and values used are mostly UNDOCUMENTED, and
2610Sstevel@tonic-gate * therefore don't have symbolic names ;-(
2620Sstevel@tonic-gate *
2630Sstevel@tonic-gate * Many of the comments are straight out of the Broadcom code:
2640Sstevel@tonic-gate * even where the code has been restructured, the original
2650Sstevel@tonic-gate * comments have been preserved in order to explain what these
2660Sstevel@tonic-gate * undocumented registers & values are all about ...
2670Sstevel@tonic-gate */
2680Sstevel@tonic-gate
2690Sstevel@tonic-gate static void
bge_phy_macro_wait(bge_t * bgep)2700Sstevel@tonic-gate bge_phy_macro_wait(bge_t *bgep)
2710Sstevel@tonic-gate {
2720Sstevel@tonic-gate uint_t count;
2730Sstevel@tonic-gate
2740Sstevel@tonic-gate for (count = 100; --count; )
2750Sstevel@tonic-gate if ((bge_mii_get16(bgep, 0x16) & 0x1000) == 0)
2760Sstevel@tonic-gate break;
2770Sstevel@tonic-gate }
2780Sstevel@tonic-gate
2790Sstevel@tonic-gate /*
2800Sstevel@tonic-gate * PHY test data pattern:
2810Sstevel@tonic-gate *
2820Sstevel@tonic-gate * For 5703/04, each DFE TAP has 21-bits (low word 15, hi word 6)
2830Sstevel@tonic-gate * For 5705, each DFE TAP has 19-bits (low word 15, hi word 4)
2840Sstevel@tonic-gate * For simplicity, we check only 19-bits, so we don't have to
2850Sstevel@tonic-gate * distinguish which chip it is.
2860Sstevel@tonic-gate * the LO word contains 15 bits, make sure pattern data is < 0x7fff
2870Sstevel@tonic-gate * the HI word contains 6 bits, make sure pattern data is < 0x003f
2880Sstevel@tonic-gate */
2890Sstevel@tonic-gate #define N_CHANNELS 4
2900Sstevel@tonic-gate #define N_TAPS 3
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate static struct {
2930Sstevel@tonic-gate uint16_t lo;
2940Sstevel@tonic-gate uint16_t hi;
2950Sstevel@tonic-gate } tap_data[N_CHANNELS][N_TAPS] = {
2960Sstevel@tonic-gate {
2970Sstevel@tonic-gate { 0x5555, 0x0005 }, /* ch0, TAP 0, LO/HI pattern */
2980Sstevel@tonic-gate { 0x2aaa, 0x000a }, /* ch0, TAP 1, LO/HI pattern */
2990Sstevel@tonic-gate { 0x3456, 0x0003 } /* ch0, TAP 2, LO/HI pattern */
3000Sstevel@tonic-gate },
3010Sstevel@tonic-gate {
3020Sstevel@tonic-gate { 0x2aaa, 0x000a }, /* ch1, TAP 0, LO/HI pattern */
3030Sstevel@tonic-gate { 0x3333, 0x0003 }, /* ch1, TAP 1, LO/HI pattern */
3040Sstevel@tonic-gate { 0x789a, 0x0005 } /* ch1, TAP 2, LO/HI pattern */
3050Sstevel@tonic-gate },
3060Sstevel@tonic-gate {
3070Sstevel@tonic-gate { 0x5a5a, 0x0005 }, /* ch2, TAP 0, LO/HI pattern */
3080Sstevel@tonic-gate { 0x2a6a, 0x000a }, /* ch2, TAP 1, LO/HI pattern */
3090Sstevel@tonic-gate { 0x1bcd, 0x0003 } /* ch2, TAP 2, LO/HI pattern */
3100Sstevel@tonic-gate },
3110Sstevel@tonic-gate {
3120Sstevel@tonic-gate { 0x2a5a, 0x000a }, /* ch3, TAP 0, LO/HI pattern */
3130Sstevel@tonic-gate { 0x33c3, 0x0003 }, /* ch3, TAP 1, LO/HI pattern */
3140Sstevel@tonic-gate { 0x2ef1, 0x0005 } /* ch3, TAP 2, LO/HI pattern */
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate };
3170Sstevel@tonic-gate
3180Sstevel@tonic-gate /*
3190Sstevel@tonic-gate * Check whether the PHY has locked up after a RESET.
3200Sstevel@tonic-gate *
3210Sstevel@tonic-gate * Returns TRUE if it did, FALSE is it's OK ;-)
3220Sstevel@tonic-gate */
3230Sstevel@tonic-gate static boolean_t
bge_phy_locked_up(bge_t * bgep)3240Sstevel@tonic-gate bge_phy_locked_up(bge_t *bgep)
3250Sstevel@tonic-gate {
3260Sstevel@tonic-gate uint16_t dataLo;
3270Sstevel@tonic-gate uint16_t dataHi;
3280Sstevel@tonic-gate uint_t chan;
3290Sstevel@tonic-gate uint_t tap;
3300Sstevel@tonic-gate
3310Sstevel@tonic-gate /*
3320Sstevel@tonic-gate * Check TAPs for all 4 channels, as soon as we see a lockup
3330Sstevel@tonic-gate * we'll stop checking.
3340Sstevel@tonic-gate */
3350Sstevel@tonic-gate for (chan = 0; chan < N_CHANNELS; ++chan) {
3360Sstevel@tonic-gate /* Select channel and set TAP index to 0 */
3370Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
3380Sstevel@tonic-gate /* Freeze filter again just to be safe */
3390Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0002);
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate /*
3420Sstevel@tonic-gate * Write fixed pattern to the RAM, 3 TAPs for
3430Sstevel@tonic-gate * each channel, each TAP have 2 WORDs (LO/HI)
3440Sstevel@tonic-gate */
3450Sstevel@tonic-gate for (tap = 0; tap < N_TAPS; ++tap) {
3460Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, tap_data[chan][tap].lo);
3470Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, tap_data[chan][tap].hi);
3480Sstevel@tonic-gate }
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate /*
3510Sstevel@tonic-gate * Active PHY's Macro operation to write DFE
3520Sstevel@tonic-gate * TAP from RAM, and wait for Macro to complete.
3530Sstevel@tonic-gate */
3540Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0202);
3550Sstevel@tonic-gate bge_phy_macro_wait(bgep);
3560Sstevel@tonic-gate
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate * Done with write phase, now begin read phase.
3590Sstevel@tonic-gate */
3600Sstevel@tonic-gate
3610Sstevel@tonic-gate /* Select channel and set TAP index to 0 */
3620Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200);
3630Sstevel@tonic-gate
3640Sstevel@tonic-gate /*
3650Sstevel@tonic-gate * Active PHY's Macro operation to load DFE
3660Sstevel@tonic-gate * TAP to RAM, and wait for Macro to complete
3670Sstevel@tonic-gate */
3680Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0082);
3690Sstevel@tonic-gate bge_phy_macro_wait(bgep);
3700Sstevel@tonic-gate
3710Sstevel@tonic-gate /* Enable "pre-fetch" */
3720Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0802);
3730Sstevel@tonic-gate bge_phy_macro_wait(bgep);
3740Sstevel@tonic-gate
3750Sstevel@tonic-gate /*
3760Sstevel@tonic-gate * Read back the TAP values. 3 TAPs for each
3770Sstevel@tonic-gate * channel, each TAP have 2 WORDs (LO/HI)
3780Sstevel@tonic-gate */
3790Sstevel@tonic-gate for (tap = 0; tap < N_TAPS; ++tap) {
3800Sstevel@tonic-gate /*
3810Sstevel@tonic-gate * Read Lo/Hi then wait for 'done' is faster.
3820Sstevel@tonic-gate * For DFE TAP, the HI word contains 6 bits,
3830Sstevel@tonic-gate * LO word contains 15 bits
3840Sstevel@tonic-gate */
3850Sstevel@tonic-gate dataLo = bge_mii_get16(bgep, 0x15) & 0x7fff;
3860Sstevel@tonic-gate dataHi = bge_mii_get16(bgep, 0x15) & 0x003f;
3870Sstevel@tonic-gate bge_phy_macro_wait(bgep);
3880Sstevel@tonic-gate
3890Sstevel@tonic-gate /*
3900Sstevel@tonic-gate * Check if what we wrote is what we read back.
3910Sstevel@tonic-gate * If failed, then the PHY is locked up, we need
3920Sstevel@tonic-gate * to do PHY reset again
3930Sstevel@tonic-gate */
3940Sstevel@tonic-gate if (dataLo != tap_data[chan][tap].lo)
3950Sstevel@tonic-gate return (B_TRUE); /* wedged! */
3960Sstevel@tonic-gate
3970Sstevel@tonic-gate if (dataHi != tap_data[chan][tap].hi)
3980Sstevel@tonic-gate return (B_TRUE); /* wedged! */
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate }
4010Sstevel@tonic-gate
4020Sstevel@tonic-gate /*
4030Sstevel@tonic-gate * The PHY isn't locked up ;-)
4040Sstevel@tonic-gate */
4050Sstevel@tonic-gate return (B_FALSE);
4060Sstevel@tonic-gate }
4070Sstevel@tonic-gate
4080Sstevel@tonic-gate /*
4090Sstevel@tonic-gate * Special-case code to reset the PHY on the 5702/5703/5704C/5705/5782.
4100Sstevel@tonic-gate * Tries up to 5 times to recover from failure to reset or PHY lockup.
4110Sstevel@tonic-gate *
4120Sstevel@tonic-gate * Returns TRUE on success, FALSE if there's an unrecoverable problem
4130Sstevel@tonic-gate */
4140Sstevel@tonic-gate static boolean_t
bge_phy_reset_and_check(bge_t * bgep)4150Sstevel@tonic-gate bge_phy_reset_and_check(bge_t *bgep)
4160Sstevel@tonic-gate {
4170Sstevel@tonic-gate boolean_t reset_success;
4180Sstevel@tonic-gate boolean_t phy_locked;
4190Sstevel@tonic-gate uint16_t extctrl;
42011065SRiz.Nadaf@Sun.COM uint16_t gigctrl;
4210Sstevel@tonic-gate uint_t retries;
4220Sstevel@tonic-gate
4230Sstevel@tonic-gate for (retries = 0; retries < 5; ++retries) {
4240Sstevel@tonic-gate /* Issue a phy reset, and wait for reset to complete */
4250Sstevel@tonic-gate /* Assuming reset is successful first */
4260Sstevel@tonic-gate reset_success = bge_phy_reset(bgep);
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate /*
4290Sstevel@tonic-gate * Now go check the DFE TAPs to see if locked up, but
4300Sstevel@tonic-gate * first, we need to set up PHY so we can read DFE
4310Sstevel@tonic-gate * TAPs.
4320Sstevel@tonic-gate */
4330Sstevel@tonic-gate
4340Sstevel@tonic-gate /*
4350Sstevel@tonic-gate * Disable Transmitter and Interrupt, while we play
4360Sstevel@tonic-gate * with the PHY registers, so the link partner won't
4370Sstevel@tonic-gate * see any strange data and the Driver won't see any
4380Sstevel@tonic-gate * interrupts.
4390Sstevel@tonic-gate */
4400Sstevel@tonic-gate extctrl = bge_mii_get16(bgep, 0x10);
4410Sstevel@tonic-gate bge_mii_put16(bgep, 0x10, extctrl | 0x3000);
4420Sstevel@tonic-gate
4430Sstevel@tonic-gate /* Setup Full-Duplex, 1000 mbps */
4440Sstevel@tonic-gate bge_mii_put16(bgep, 0x0, 0x0140);
4450Sstevel@tonic-gate
4460Sstevel@tonic-gate /* Set to Master mode */
44711065SRiz.Nadaf@Sun.COM gigctrl = bge_mii_get16(bgep, 0x9);
4480Sstevel@tonic-gate bge_mii_put16(bgep, 0x9, 0x1800);
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate /* Enable SM_DSP_CLOCK & 6dB */
4510Sstevel@tonic-gate bge_mii_put16(bgep, 0x18, 0x0c00); /* "the ADC fix" */
4520Sstevel@tonic-gate
4530Sstevel@tonic-gate /* Work-arounds */
4540Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x201f);
4550Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, 0x2aaa);
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate /* More workarounds */
4580Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x000a);
4590Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, 0x0323); /* "the Gamma fix" */
4600Sstevel@tonic-gate
4610Sstevel@tonic-gate /* Blocks the PHY control access */
4620Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x8005);
4630Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, 0x0800);
4640Sstevel@tonic-gate
4650Sstevel@tonic-gate /* Test whether PHY locked up ;-( */
4660Sstevel@tonic-gate phy_locked = bge_phy_locked_up(bgep);
4670Sstevel@tonic-gate if (reset_success && !phy_locked)
4680Sstevel@tonic-gate break;
4690Sstevel@tonic-gate
4700Sstevel@tonic-gate /*
4710Sstevel@tonic-gate * Some problem here ... log it & retry
4720Sstevel@tonic-gate */
4730Sstevel@tonic-gate if (!reset_success)
4740Sstevel@tonic-gate BGE_REPORT((bgep, "PHY didn't reset!"));
4750Sstevel@tonic-gate if (phy_locked)
4760Sstevel@tonic-gate BGE_REPORT((bgep, "PHY locked up!"));
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate
4790Sstevel@tonic-gate /* Remove block phy control */
4800Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x8005);
4810Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, 0x0000);
4820Sstevel@tonic-gate
4830Sstevel@tonic-gate /* Unfreeze DFE TAP filter for all channels */
4840Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x8200);
4850Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0000);
4860Sstevel@tonic-gate
4870Sstevel@tonic-gate /* Restore PHY back to operating state */
4880Sstevel@tonic-gate bge_mii_put16(bgep, 0x18, 0x0400);
4890Sstevel@tonic-gate
49011065SRiz.Nadaf@Sun.COM /* Restore 1000BASE-T Control Register */
49111065SRiz.Nadaf@Sun.COM bge_mii_put16(bgep, 0x9, gigctrl);
49211065SRiz.Nadaf@Sun.COM
4930Sstevel@tonic-gate /* Enable transmitter and interrupt */
4940Sstevel@tonic-gate extctrl = bge_mii_get16(bgep, 0x10);
4950Sstevel@tonic-gate bge_mii_put16(bgep, 0x10, extctrl & ~0x3000);
4960Sstevel@tonic-gate
4977678SYong.Tan@Sun.COM if (DEVICE_5906_SERIES_CHIPSETS(bgep))
4987808SYong.Tan@Sun.COM (void) bge_adj_volt_5906(bgep);
4997678SYong.Tan@Sun.COM
5001865Sdilpreet if (!reset_success)
5011865Sdilpreet bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
5021865Sdilpreet else if (phy_locked)
5031865Sdilpreet bge_fm_ereport(bgep, DDI_FM_DEVICE_INVAL_STATE);
5040Sstevel@tonic-gate return (reset_success && !phy_locked);
5050Sstevel@tonic-gate }
5060Sstevel@tonic-gate
5070Sstevel@tonic-gate static void
bge_phy_tweak_gmii(bge_t * bgep)5080Sstevel@tonic-gate bge_phy_tweak_gmii(bge_t *bgep)
5090Sstevel@tonic-gate {
5100Sstevel@tonic-gate /* Tweak GMII timing */
5110Sstevel@tonic-gate bge_mii_put16(bgep, 0x1c, 0x8d68);
5120Sstevel@tonic-gate bge_mii_put16(bgep, 0x1c, 0x8d68);
5130Sstevel@tonic-gate }
5140Sstevel@tonic-gate
5152135Szh199473 /* Bit Error Rate reduction fix */
5162135Szh199473 static void
bge_phy_bit_err_fix(bge_t * bgep)5172135Szh199473 bge_phy_bit_err_fix(bge_t *bgep)
5182135Szh199473 {
5192135Szh199473 bge_mii_put16(bgep, 0x18, 0x0c00);
5202135Szh199473 bge_mii_put16(bgep, 0x17, 0x000a);
5212135Szh199473 bge_mii_put16(bgep, 0x15, 0x310b);
5222135Szh199473 bge_mii_put16(bgep, 0x17, 0x201f);
5232135Szh199473 bge_mii_put16(bgep, 0x15, 0x9506);
5242135Szh199473 bge_mii_put16(bgep, 0x17, 0x401f);
5252135Szh199473 bge_mii_put16(bgep, 0x15, 0x14e2);
5262135Szh199473 bge_mii_put16(bgep, 0x18, 0x0400);
5272135Szh199473 }
5282135Szh199473
5290Sstevel@tonic-gate /*
5300Sstevel@tonic-gate * End of Broadcom-derived workaround code *
5310Sstevel@tonic-gate */
5320Sstevel@tonic-gate
5331865Sdilpreet static int
bge_restart_copper(bge_t * bgep,boolean_t powerdown)5340Sstevel@tonic-gate bge_restart_copper(bge_t *bgep, boolean_t powerdown)
5350Sstevel@tonic-gate {
5360Sstevel@tonic-gate uint16_t phy_status;
5370Sstevel@tonic-gate boolean_t reset_ok;
53810162SYong.Tan@Sun.COM uint16_t extctrl, auxctrl;
5390Sstevel@tonic-gate
5400Sstevel@tonic-gate BGE_TRACE(("bge_restart_copper($%p, %d)", (void *)bgep, powerdown));
5410Sstevel@tonic-gate
5420Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
5430Sstevel@tonic-gate
5440Sstevel@tonic-gate switch (MHCR_CHIP_ASIC_REV(bgep->chipid.asic_rev)) {
5450Sstevel@tonic-gate default:
5460Sstevel@tonic-gate /*
5470Sstevel@tonic-gate * Shouldn't happen; it means we don't recognise this chip.
5480Sstevel@tonic-gate * It's probably a new one, so we'll try our best anyway ...
5490Sstevel@tonic-gate */
5500Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5703:
5510Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5704:
5520Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5705:
5532675Szh199473 case MHCR_CHIP_ASIC_REV_5752:
5540Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5714:
555388Sly149593 case MHCR_CHIP_ASIC_REV_5715:
5560Sstevel@tonic-gate reset_ok = bge_phy_reset_and_check(bgep);
5570Sstevel@tonic-gate break;
5580Sstevel@tonic-gate
5597678SYong.Tan@Sun.COM case MHCR_CHIP_ASIC_REV_5906:
5600Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5700:
5610Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5701:
5629042SYong.Tan@Sun.COM case MHCR_CHIP_ASIC_REV_5723:
5639731SYong.Tan@Sun.COM case MHCR_CHIP_ASIC_REV_5721_5751:
5640Sstevel@tonic-gate /*
5650Sstevel@tonic-gate * Just a plain reset; the "check" code breaks these chips
5660Sstevel@tonic-gate */
5670Sstevel@tonic-gate reset_ok = bge_phy_reset(bgep);
5681865Sdilpreet if (!reset_ok)
5691865Sdilpreet bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE);
5700Sstevel@tonic-gate break;
5710Sstevel@tonic-gate }
5721865Sdilpreet if (!reset_ok) {
5731865Sdilpreet BGE_REPORT((bgep, "PHY failed to reset correctly"));
5741865Sdilpreet return (DDI_FAILURE);
5751865Sdilpreet }
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate /*
5780Sstevel@tonic-gate * Step 5: disable WOL (not required after RESET)
5790Sstevel@tonic-gate *
5800Sstevel@tonic-gate * Step 6: refer to errata
5810Sstevel@tonic-gate */
5820Sstevel@tonic-gate switch (bgep->chipid.asic_rev) {
5830Sstevel@tonic-gate default:
5840Sstevel@tonic-gate break;
5850Sstevel@tonic-gate
5860Sstevel@tonic-gate case MHCR_CHIP_REV_5704_A0:
5870Sstevel@tonic-gate bge_phy_tweak_gmii(bgep);
5880Sstevel@tonic-gate break;
5890Sstevel@tonic-gate }
5900Sstevel@tonic-gate
5912135Szh199473 switch (MHCR_CHIP_ASIC_REV(bgep->chipid.asic_rev)) {
5922135Szh199473 case MHCR_CHIP_ASIC_REV_5705:
5932135Szh199473 case MHCR_CHIP_ASIC_REV_5721_5751:
5942135Szh199473 bge_phy_bit_err_fix(bgep);
5952135Szh199473 break;
5962135Szh199473 }
5972135Szh199473
59810464SYong.Tan@Sun.COM if (!(bgep->chipid.flags & CHIP_FLAG_NO_JUMBO) &&
59910464SYong.Tan@Sun.COM (bgep->chipid.default_mtu > BGE_DEFAULT_MTU)) {
60010162SYong.Tan@Sun.COM /* Set the GMII Fifo Elasticity to high latency */
60110162SYong.Tan@Sun.COM extctrl = bge_mii_get16(bgep, 0x10);
60210162SYong.Tan@Sun.COM bge_mii_put16(bgep, 0x10, extctrl | 0x1);
60310162SYong.Tan@Sun.COM
60410162SYong.Tan@Sun.COM /* Allow reception of extended length packets */
60510162SYong.Tan@Sun.COM bge_mii_put16(bgep, MII_AUX_CONTROL, 0x0007);
60610162SYong.Tan@Sun.COM auxctrl = bge_mii_get16(bgep, MII_AUX_CONTROL);
60710162SYong.Tan@Sun.COM auxctrl |= 0x4000;
60810162SYong.Tan@Sun.COM bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl);
60910162SYong.Tan@Sun.COM }
61010162SYong.Tan@Sun.COM
6110Sstevel@tonic-gate /*
6120Sstevel@tonic-gate * Step 7: read the MII_INTR_STATUS register twice,
6130Sstevel@tonic-gate * in order to clear any sticky bits (but they should
6140Sstevel@tonic-gate * have been cleared by the RESET, I think), and we're
6150Sstevel@tonic-gate * not using PHY interrupts anyway.
6160Sstevel@tonic-gate *
6170Sstevel@tonic-gate * Step 8: enable the PHY to interrupt on link status
6180Sstevel@tonic-gate * change (not required)
6190Sstevel@tonic-gate *
6200Sstevel@tonic-gate * Step 9: configure PHY LED Mode - not applicable?
6210Sstevel@tonic-gate *
6220Sstevel@tonic-gate * Step 10: read the MII_STATUS register twice, in
6230Sstevel@tonic-gate * order to clear any sticky bits (but they should
6240Sstevel@tonic-gate * have been cleared by the RESET, I think).
6250Sstevel@tonic-gate */
6260Sstevel@tonic-gate phy_status = bge_mii_get16(bgep, MII_STATUS);
6270Sstevel@tonic-gate phy_status = bge_mii_get16(bgep, MII_STATUS);
6280Sstevel@tonic-gate BGE_DEBUG(("bge_restart_copper: status 0x%x", phy_status));
6290Sstevel@tonic-gate
6300Sstevel@tonic-gate /*
6310Sstevel@tonic-gate * Finally, shut down the PHY, if required
6320Sstevel@tonic-gate */
6330Sstevel@tonic-gate if (powerdown)
6340Sstevel@tonic-gate bge_phy_powerdown(bgep);
6351865Sdilpreet return (DDI_SUCCESS);
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate
6380Sstevel@tonic-gate /*
6390Sstevel@tonic-gate * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities
6400Sstevel@tonic-gate * and advertisements with the required settings as specified by the various
6410Sstevel@tonic-gate * param_* variables that can be poked via the NDD interface.
6420Sstevel@tonic-gate *
6430Sstevel@tonic-gate * We always reset the PHY and reprogram *all* the relevant registers,
6440Sstevel@tonic-gate * not just those changed. This should cause the link to go down, and then
6450Sstevel@tonic-gate * back up again once the link is stable and autonegotiation (if enabled)
6460Sstevel@tonic-gate * is complete. We should get a link state change interrupt somewhere along
6470Sstevel@tonic-gate * the way ...
6480Sstevel@tonic-gate *
6490Sstevel@tonic-gate * NOTE: <genlock> must already be held by the caller
6500Sstevel@tonic-gate */
6511865Sdilpreet static int
bge_update_copper(bge_t * bgep)6520Sstevel@tonic-gate bge_update_copper(bge_t *bgep)
6530Sstevel@tonic-gate {
6540Sstevel@tonic-gate boolean_t adv_autoneg;
6550Sstevel@tonic-gate boolean_t adv_pause;
6560Sstevel@tonic-gate boolean_t adv_asym_pause;
6570Sstevel@tonic-gate boolean_t adv_1000fdx;
6580Sstevel@tonic-gate boolean_t adv_1000hdx;
6590Sstevel@tonic-gate boolean_t adv_100fdx;
6600Sstevel@tonic-gate boolean_t adv_100hdx;
6610Sstevel@tonic-gate boolean_t adv_10fdx;
6620Sstevel@tonic-gate boolean_t adv_10hdx;
6630Sstevel@tonic-gate
6640Sstevel@tonic-gate uint16_t control;
6650Sstevel@tonic-gate uint16_t gigctrl;
6660Sstevel@tonic-gate uint16_t auxctrl;
6670Sstevel@tonic-gate uint16_t anar;
6680Sstevel@tonic-gate
6690Sstevel@tonic-gate BGE_TRACE(("bge_update_copper($%p)", (void *)bgep));
6700Sstevel@tonic-gate
6710Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
6720Sstevel@tonic-gate
6730Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: autoneg %d "
6746219Sgh162552 "pause %d asym_pause %d "
6756219Sgh162552 "1000fdx %d 1000hdx %d "
6766219Sgh162552 "100fdx %d 100hdx %d "
6776219Sgh162552 "10fdx %d 10hdx %d ",
6786219Sgh162552 bgep->param_adv_autoneg,
6796219Sgh162552 bgep->param_adv_pause, bgep->param_adv_asym_pause,
6806219Sgh162552 bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
6816219Sgh162552 bgep->param_adv_100fdx, bgep->param_adv_100hdx,
6826219Sgh162552 bgep->param_adv_10fdx, bgep->param_adv_10hdx));
6830Sstevel@tonic-gate
6840Sstevel@tonic-gate control = gigctrl = auxctrl = anar = 0;
6850Sstevel@tonic-gate
6860Sstevel@tonic-gate /*
6870Sstevel@tonic-gate * PHY settings are normally based on the param_* variables,
6880Sstevel@tonic-gate * but if any loopback mode is in effect, that takes precedence.
6890Sstevel@tonic-gate *
6900Sstevel@tonic-gate * BGE supports MAC-internal loopback, PHY-internal loopback,
6910Sstevel@tonic-gate * and External loopback at a variety of speeds (with a special
6920Sstevel@tonic-gate * cable). In all cases, autoneg is turned OFF, full-duplex
6930Sstevel@tonic-gate * is turned ON, and the speed/mastership is forced.
6940Sstevel@tonic-gate */
6950Sstevel@tonic-gate switch (bgep->param_loop_mode) {
6960Sstevel@tonic-gate case BGE_LOOP_NONE:
6970Sstevel@tonic-gate default:
6980Sstevel@tonic-gate adv_autoneg = bgep->param_adv_autoneg;
6990Sstevel@tonic-gate adv_pause = bgep->param_adv_pause;
7000Sstevel@tonic-gate adv_asym_pause = bgep->param_adv_asym_pause;
7010Sstevel@tonic-gate adv_1000fdx = bgep->param_adv_1000fdx;
7020Sstevel@tonic-gate adv_1000hdx = bgep->param_adv_1000hdx;
7030Sstevel@tonic-gate adv_100fdx = bgep->param_adv_100fdx;
7040Sstevel@tonic-gate adv_100hdx = bgep->param_adv_100hdx;
7050Sstevel@tonic-gate adv_10fdx = bgep->param_adv_10fdx;
7060Sstevel@tonic-gate adv_10hdx = bgep->param_adv_10hdx;
7070Sstevel@tonic-gate break;
7080Sstevel@tonic-gate
7090Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_1000:
7100Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_100:
7110Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_10:
7120Sstevel@tonic-gate case BGE_LOOP_INTERNAL_PHY:
7130Sstevel@tonic-gate case BGE_LOOP_INTERNAL_MAC:
7140Sstevel@tonic-gate adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
7150Sstevel@tonic-gate adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE;
7160Sstevel@tonic-gate adv_1000hdx = adv_100hdx = adv_10hdx = B_FALSE;
7170Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_FULL;
7180Sstevel@tonic-gate
7190Sstevel@tonic-gate switch (bgep->param_loop_mode) {
7200Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_1000:
7210Sstevel@tonic-gate bgep->param_link_speed = 1000;
7220Sstevel@tonic-gate adv_1000fdx = B_TRUE;
7230Sstevel@tonic-gate auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
7249860Sgdamore@opensolaris.org gigctrl |= MII_MSCONTROL_MANUAL;
7259860Sgdamore@opensolaris.org gigctrl |= MII_MSCONTROL_MASTER;
7260Sstevel@tonic-gate break;
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_100:
7290Sstevel@tonic-gate bgep->param_link_speed = 100;
7300Sstevel@tonic-gate adv_100fdx = B_TRUE;
7310Sstevel@tonic-gate auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
7320Sstevel@tonic-gate break;
7330Sstevel@tonic-gate
7340Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_10:
7350Sstevel@tonic-gate bgep->param_link_speed = 10;
7360Sstevel@tonic-gate adv_10fdx = B_TRUE;
7370Sstevel@tonic-gate auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK;
7380Sstevel@tonic-gate break;
7390Sstevel@tonic-gate
7400Sstevel@tonic-gate case BGE_LOOP_INTERNAL_PHY:
7410Sstevel@tonic-gate bgep->param_link_speed = 1000;
7420Sstevel@tonic-gate adv_1000fdx = B_TRUE;
7430Sstevel@tonic-gate control = MII_CONTROL_LOOPBACK;
7440Sstevel@tonic-gate break;
7450Sstevel@tonic-gate
7460Sstevel@tonic-gate case BGE_LOOP_INTERNAL_MAC:
7470Sstevel@tonic-gate bgep->param_link_speed = 1000;
7480Sstevel@tonic-gate adv_1000fdx = B_TRUE;
7490Sstevel@tonic-gate break;
7500Sstevel@tonic-gate }
7510Sstevel@tonic-gate }
7520Sstevel@tonic-gate
7530Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: autoneg %d "
7546219Sgh162552 "pause %d asym_pause %d "
7556219Sgh162552 "1000fdx %d 1000hdx %d "
7566219Sgh162552 "100fdx %d 100hdx %d "
7576219Sgh162552 "10fdx %d 10hdx %d ",
7586219Sgh162552 adv_autoneg,
7596219Sgh162552 adv_pause, adv_asym_pause,
7606219Sgh162552 adv_1000fdx, adv_1000hdx,
7616219Sgh162552 adv_100fdx, adv_100hdx,
7626219Sgh162552 adv_10fdx, adv_10hdx));
7630Sstevel@tonic-gate
7640Sstevel@tonic-gate /*
7650Sstevel@tonic-gate * We should have at least one technology capability set;
7660Sstevel@tonic-gate * if not, we select a default of 1000Mb/s full-duplex
7670Sstevel@tonic-gate */
7680Sstevel@tonic-gate if (!adv_1000fdx && !adv_100fdx && !adv_10fdx &&
7690Sstevel@tonic-gate !adv_1000hdx && !adv_100hdx && !adv_10hdx)
7700Sstevel@tonic-gate adv_1000fdx = B_TRUE;
7710Sstevel@tonic-gate
7720Sstevel@tonic-gate /*
7730Sstevel@tonic-gate * Now transform the adv_* variables into the proper settings
7740Sstevel@tonic-gate * of the PHY registers ...
7750Sstevel@tonic-gate *
7760Sstevel@tonic-gate * If autonegotiation is (now) enabled, we want to trigger
7770Sstevel@tonic-gate * a new autonegotiation cycle once the PHY has been
7780Sstevel@tonic-gate * programmed with the capabilities to be advertised.
7790Sstevel@tonic-gate */
7800Sstevel@tonic-gate if (adv_autoneg)
7810Sstevel@tonic-gate control |= MII_CONTROL_ANE|MII_CONTROL_RSAN;
7820Sstevel@tonic-gate
7830Sstevel@tonic-gate if (adv_1000fdx)
7849860Sgdamore@opensolaris.org control |= MII_CONTROL_1GB|MII_CONTROL_FDUPLEX;
7850Sstevel@tonic-gate else if (adv_1000hdx)
7869860Sgdamore@opensolaris.org control |= MII_CONTROL_1GB;
7870Sstevel@tonic-gate else if (adv_100fdx)
7880Sstevel@tonic-gate control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX;
7890Sstevel@tonic-gate else if (adv_100hdx)
7900Sstevel@tonic-gate control |= MII_CONTROL_100MB;
7910Sstevel@tonic-gate else if (adv_10fdx)
7920Sstevel@tonic-gate control |= MII_CONTROL_FDUPLEX;
7930Sstevel@tonic-gate else if (adv_10hdx)
7940Sstevel@tonic-gate control |= 0;
7950Sstevel@tonic-gate else
7960Sstevel@tonic-gate { _NOTE(EMPTY); } /* Can't get here anyway ... */
7970Sstevel@tonic-gate
7980Sstevel@tonic-gate if (adv_1000fdx)
7999860Sgdamore@opensolaris.org gigctrl |= MII_MSCONTROL_1000T_FD;
8000Sstevel@tonic-gate if (adv_1000hdx)
8019860Sgdamore@opensolaris.org gigctrl |= MII_MSCONTROL_1000T;
8020Sstevel@tonic-gate
8030Sstevel@tonic-gate if (adv_100fdx)
8040Sstevel@tonic-gate anar |= MII_ABILITY_100BASE_TX_FD;
8050Sstevel@tonic-gate if (adv_100hdx)
8060Sstevel@tonic-gate anar |= MII_ABILITY_100BASE_TX;
8070Sstevel@tonic-gate if (adv_10fdx)
8080Sstevel@tonic-gate anar |= MII_ABILITY_10BASE_T_FD;
8090Sstevel@tonic-gate if (adv_10hdx)
8100Sstevel@tonic-gate anar |= MII_ABILITY_10BASE_T;
8110Sstevel@tonic-gate
8120Sstevel@tonic-gate if (adv_pause)
8130Sstevel@tonic-gate anar |= MII_ABILITY_PAUSE;
8140Sstevel@tonic-gate if (adv_asym_pause)
8159860Sgdamore@opensolaris.org anar |= MII_ABILITY_ASMPAUSE;
8160Sstevel@tonic-gate
8170Sstevel@tonic-gate /*
8180Sstevel@tonic-gate * Munge in any other fixed bits we require ...
8190Sstevel@tonic-gate */
8200Sstevel@tonic-gate anar |= MII_AN_SELECTOR_8023;
8210Sstevel@tonic-gate auxctrl |= MII_AUX_CTRL_NORM_TX_MODE;
8220Sstevel@tonic-gate auxctrl |= MII_AUX_CTRL_NORMAL;
8230Sstevel@tonic-gate
8240Sstevel@tonic-gate /*
8250Sstevel@tonic-gate * Restart the PHY and write the new values. Note the
8260Sstevel@tonic-gate * time, so that we can say whether subsequent link state
8270Sstevel@tonic-gate * changes can be attributed to our reprogramming the PHY
8280Sstevel@tonic-gate */
8291865Sdilpreet if ((*bgep->physops->phys_restart)(bgep, B_FALSE) == DDI_FAILURE)
8301865Sdilpreet return (DDI_FAILURE);
8310Sstevel@tonic-gate bge_mii_put16(bgep, MII_AN_ADVERT, anar);
83210162SYong.Tan@Sun.COM if (auxctrl & MII_AUX_CTRL_NORM_EXT_LOOPBACK)
83310162SYong.Tan@Sun.COM bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl);
8349860Sgdamore@opensolaris.org bge_mii_put16(bgep, MII_MSCONTROL, gigctrl);
83511065SRiz.Nadaf@Sun.COM bge_mii_put16(bgep, MII_CONTROL, control);
8360Sstevel@tonic-gate
8370Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: anar <- 0x%x", anar));
8380Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: auxctrl <- 0x%x", auxctrl));
8390Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: gigctrl <- 0x%x", gigctrl));
84011065SRiz.Nadaf@Sun.COM BGE_DEBUG(("bge_update_copper: control <- 0x%x", control));
8410Sstevel@tonic-gate
8420Sstevel@tonic-gate #if BGE_COPPER_WIRESPEED
8430Sstevel@tonic-gate /*
8440Sstevel@tonic-gate * Enable the 'wire-speed' feature, if the chip supports it
8450Sstevel@tonic-gate * and we haven't got (any) loopback mode selected.
8460Sstevel@tonic-gate */
8470Sstevel@tonic-gate switch (bgep->chipid.device) {
8480Sstevel@tonic-gate case DEVICE_ID_5700:
8490Sstevel@tonic-gate case DEVICE_ID_5700x:
8500Sstevel@tonic-gate case DEVICE_ID_5705C:
8510Sstevel@tonic-gate case DEVICE_ID_5782:
8520Sstevel@tonic-gate /*
8530Sstevel@tonic-gate * These chips are known or assumed not to support it
8540Sstevel@tonic-gate */
8550Sstevel@tonic-gate break;
8560Sstevel@tonic-gate
8570Sstevel@tonic-gate default:
8580Sstevel@tonic-gate /*
8590Sstevel@tonic-gate * All other Broadcom chips are expected to support it.
8600Sstevel@tonic-gate */
8610Sstevel@tonic-gate if (bgep->param_loop_mode == BGE_LOOP_NONE)
8620Sstevel@tonic-gate bge_mii_put16(bgep, MII_AUX_CONTROL,
8636219Sgh162552 MII_AUX_CTRL_MISC_WRITE_ENABLE |
8646219Sgh162552 MII_AUX_CTRL_MISC_WIRE_SPEED |
8656219Sgh162552 MII_AUX_CTRL_MISC);
8660Sstevel@tonic-gate break;
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate #endif /* BGE_COPPER_WIRESPEED */
8691865Sdilpreet return (DDI_SUCCESS);
8700Sstevel@tonic-gate }
8710Sstevel@tonic-gate
8720Sstevel@tonic-gate static boolean_t
bge_check_copper(bge_t * bgep,boolean_t recheck)8730Sstevel@tonic-gate bge_check_copper(bge_t *bgep, boolean_t recheck)
8740Sstevel@tonic-gate {
8750Sstevel@tonic-gate uint32_t emac_status;
8760Sstevel@tonic-gate uint16_t mii_status;
8770Sstevel@tonic-gate uint16_t aux;
8780Sstevel@tonic-gate uint_t mode;
8790Sstevel@tonic-gate boolean_t linkup;
8800Sstevel@tonic-gate
8810Sstevel@tonic-gate /*
8820Sstevel@tonic-gate * Step 10: read the status from the PHY (which is self-clearing
8830Sstevel@tonic-gate * on read!); also read & clear the main (Ethernet) MAC status
8840Sstevel@tonic-gate * (the relevant bits of this are write-one-to-clear).
8850Sstevel@tonic-gate */
8860Sstevel@tonic-gate mii_status = bge_mii_get16(bgep, MII_STATUS);
8870Sstevel@tonic-gate emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG);
8880Sstevel@tonic-gate bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status);
8890Sstevel@tonic-gate
8900Sstevel@tonic-gate BGE_DEBUG(("bge_check_copper: link %d/%s, MII status 0x%x "
8916219Sgh162552 "(was 0x%x), Ethernet MAC status 0x%x",
8926219Sgh162552 bgep->link_state, UPORDOWN(bgep->param_link_up), mii_status,
8936219Sgh162552 bgep->phy_gen_status, emac_status));
8940Sstevel@tonic-gate
8950Sstevel@tonic-gate /*
8960Sstevel@tonic-gate * If the PHY status hasn't changed since last we looked, and
8970Sstevel@tonic-gate * we not forcing a recheck (i.e. the link state was already
8980Sstevel@tonic-gate * known), there's nothing to do.
8990Sstevel@tonic-gate */
9000Sstevel@tonic-gate if (mii_status == bgep->phy_gen_status && !recheck)
9010Sstevel@tonic-gate return (B_FALSE);
9020Sstevel@tonic-gate
9030Sstevel@tonic-gate do {
9040Sstevel@tonic-gate /*
9050Sstevel@tonic-gate * Step 11: read AUX STATUS register to find speed/duplex
9060Sstevel@tonic-gate */
9070Sstevel@tonic-gate aux = bge_mii_get16(bgep, MII_AUX_STATUS);
9080Sstevel@tonic-gate BGE_CDB(bge_phydump, (bgep, mii_status, aux));
9090Sstevel@tonic-gate
9100Sstevel@tonic-gate /*
9110Sstevel@tonic-gate * We will only consider the link UP if all the readings
9120Sstevel@tonic-gate * are consistent and give meaningful results ...
9130Sstevel@tonic-gate */
9140Sstevel@tonic-gate mode = aux & MII_AUX_STATUS_MODE_MASK;
9150Sstevel@tonic-gate mode >>= MII_AUX_STATUS_MODE_SHIFT;
9167678SYong.Tan@Sun.COM if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
9177678SYong.Tan@Sun.COM linkup = BIS(aux, MII_AUX_STATUS_LINKUP);
9187678SYong.Tan@Sun.COM linkup &= BIS(mii_status, MII_STATUS_LINKUP);
9197678SYong.Tan@Sun.COM } else {
9207678SYong.Tan@Sun.COM linkup = bge_copper_link_speed[mode] > 0;
9217678SYong.Tan@Sun.COM linkup &= bge_copper_link_duplex[mode] !=
9227678SYong.Tan@Sun.COM LINK_DUPLEX_UNKNOWN;
9237678SYong.Tan@Sun.COM linkup &= BIS(aux, MII_AUX_STATUS_LINKUP);
9247678SYong.Tan@Sun.COM linkup &= BIS(mii_status, MII_STATUS_LINKUP);
9257678SYong.Tan@Sun.COM }
9260Sstevel@tonic-gate
9270Sstevel@tonic-gate BGE_DEBUG(("bge_check_copper: MII status 0x%x aux 0x%x "
9286219Sgh162552 "=> mode %d (%s)",
9296219Sgh162552 mii_status, aux,
9306219Sgh162552 mode, UPORDOWN(linkup)));
9310Sstevel@tonic-gate
9320Sstevel@tonic-gate /*
9330Sstevel@tonic-gate * Record current register values, then reread status
9340Sstevel@tonic-gate * register & loop until it stabilises ...
9350Sstevel@tonic-gate */
9360Sstevel@tonic-gate bgep->phy_aux_status = aux;
9370Sstevel@tonic-gate bgep->phy_gen_status = mii_status;
9380Sstevel@tonic-gate mii_status = bge_mii_get16(bgep, MII_STATUS);
9390Sstevel@tonic-gate } while (mii_status != bgep->phy_gen_status);
9400Sstevel@tonic-gate
9410Sstevel@tonic-gate /*
9420Sstevel@tonic-gate * Assume very little ...
9430Sstevel@tonic-gate */
9440Sstevel@tonic-gate bgep->param_lp_autoneg = B_FALSE;
9450Sstevel@tonic-gate bgep->param_lp_1000fdx = B_FALSE;
9460Sstevel@tonic-gate bgep->param_lp_1000hdx = B_FALSE;
9470Sstevel@tonic-gate bgep->param_lp_100fdx = B_FALSE;
9480Sstevel@tonic-gate bgep->param_lp_100hdx = B_FALSE;
9490Sstevel@tonic-gate bgep->param_lp_10fdx = B_FALSE;
9500Sstevel@tonic-gate bgep->param_lp_10hdx = B_FALSE;
9510Sstevel@tonic-gate bgep->param_lp_pause = B_FALSE;
9520Sstevel@tonic-gate bgep->param_lp_asym_pause = B_FALSE;
9530Sstevel@tonic-gate bgep->param_link_autoneg = B_FALSE;
9540Sstevel@tonic-gate bgep->param_link_tx_pause = B_FALSE;
9550Sstevel@tonic-gate if (bgep->param_adv_autoneg)
9560Sstevel@tonic-gate bgep->param_link_rx_pause = B_FALSE;
9570Sstevel@tonic-gate else
9580Sstevel@tonic-gate bgep->param_link_rx_pause = bgep->param_adv_pause;
9590Sstevel@tonic-gate
9600Sstevel@tonic-gate /*
9610Sstevel@tonic-gate * Discover all the link partner's abilities.
9622135Szh199473 * These are scattered through various registers ...
9630Sstevel@tonic-gate */
9640Sstevel@tonic-gate if (BIS(aux, MII_AUX_STATUS_LP_ANEG_ABLE)) {
9650Sstevel@tonic-gate bgep->param_lp_autoneg = B_TRUE;
9660Sstevel@tonic-gate bgep->param_link_autoneg = B_TRUE;
9670Sstevel@tonic-gate bgep->param_link_tx_pause = BIS(aux, MII_AUX_STATUS_TX_PAUSE);
9680Sstevel@tonic-gate bgep->param_link_rx_pause = BIS(aux, MII_AUX_STATUS_RX_PAUSE);
9690Sstevel@tonic-gate
9709860Sgdamore@opensolaris.org aux = bge_mii_get16(bgep, MII_MSSTATUS);
9719860Sgdamore@opensolaris.org bgep->param_lp_1000fdx = BIS(aux, MII_MSSTATUS_LP1000T_FD);
9729860Sgdamore@opensolaris.org bgep->param_lp_1000hdx = BIS(aux, MII_MSSTATUS_LP1000T);
9730Sstevel@tonic-gate
9740Sstevel@tonic-gate aux = bge_mii_get16(bgep, MII_AN_LPABLE);
9750Sstevel@tonic-gate bgep->param_lp_100fdx = BIS(aux, MII_ABILITY_100BASE_TX_FD);
9760Sstevel@tonic-gate bgep->param_lp_100hdx = BIS(aux, MII_ABILITY_100BASE_TX);
9770Sstevel@tonic-gate bgep->param_lp_10fdx = BIS(aux, MII_ABILITY_10BASE_T_FD);
9780Sstevel@tonic-gate bgep->param_lp_10hdx = BIS(aux, MII_ABILITY_10BASE_T);
9790Sstevel@tonic-gate bgep->param_lp_pause = BIS(aux, MII_ABILITY_PAUSE);
9809860Sgdamore@opensolaris.org bgep->param_lp_asym_pause = BIS(aux, MII_ABILITY_ASMPAUSE);
9810Sstevel@tonic-gate }
9820Sstevel@tonic-gate
9830Sstevel@tonic-gate /*
9840Sstevel@tonic-gate * Step 12: update ndd-visible state parameters, BUT!
9850Sstevel@tonic-gate * we don't transfer the new state to <link_state> just yet;
9860Sstevel@tonic-gate * instead we mark the <link_state> as UNKNOWN, and our caller
9870Sstevel@tonic-gate * will resolve it once the status has stopped changing and
9880Sstevel@tonic-gate * been stable for several seconds.
9890Sstevel@tonic-gate */
9900Sstevel@tonic-gate BGE_DEBUG(("bge_check_copper: link was %s speed %d duplex %d",
9916219Sgh162552 UPORDOWN(bgep->param_link_up),
9926219Sgh162552 bgep->param_link_speed,
9936219Sgh162552 bgep->param_link_duplex));
9940Sstevel@tonic-gate
9950Sstevel@tonic-gate if (!linkup)
9960Sstevel@tonic-gate mode = MII_AUX_STATUS_MODE_NONE;
9970Sstevel@tonic-gate bgep->param_link_up = linkup;
9980Sstevel@tonic-gate bgep->link_state = LINK_STATE_UNKNOWN;
9997678SYong.Tan@Sun.COM if (DEVICE_5906_SERIES_CHIPSETS(bgep)) {
10007678SYong.Tan@Sun.COM if (bgep->phy_aux_status & MII_AUX_STATUS_NEG_ENABLED_5906) {
10017678SYong.Tan@Sun.COM bgep->param_link_speed =
10027678SYong.Tan@Sun.COM bge_copper_link_speed_5906[mode];
10037678SYong.Tan@Sun.COM bgep->param_link_duplex =
10047678SYong.Tan@Sun.COM bge_copper_link_duplex_5906[mode];
10057678SYong.Tan@Sun.COM } else {
10067678SYong.Tan@Sun.COM bgep->param_link_speed = (bgep->phy_aux_status &
10077678SYong.Tan@Sun.COM MII_AUX_STATUS_SPEED_IND_5906) ? 100 : 10;
10087678SYong.Tan@Sun.COM bgep->param_link_duplex = (bgep->phy_aux_status &
10097678SYong.Tan@Sun.COM MII_AUX_STATUS_DUPLEX_IND_5906) ? LINK_DUPLEX_FULL :
10107678SYong.Tan@Sun.COM LINK_DUPLEX_HALF;
10117678SYong.Tan@Sun.COM }
10127678SYong.Tan@Sun.COM } else {
10137678SYong.Tan@Sun.COM bgep->param_link_speed = bge_copper_link_speed[mode];
10147678SYong.Tan@Sun.COM bgep->param_link_duplex = bge_copper_link_duplex[mode];
10157678SYong.Tan@Sun.COM }
10160Sstevel@tonic-gate
10170Sstevel@tonic-gate BGE_DEBUG(("bge_check_copper: link now %s speed %d duplex %d",
10186219Sgh162552 UPORDOWN(bgep->param_link_up),
10196219Sgh162552 bgep->param_link_speed,
10206219Sgh162552 bgep->param_link_duplex));
10210Sstevel@tonic-gate
10220Sstevel@tonic-gate return (B_TRUE);
10230Sstevel@tonic-gate }
10240Sstevel@tonic-gate
10250Sstevel@tonic-gate static const phys_ops_t copper_ops = {
10260Sstevel@tonic-gate bge_restart_copper,
10270Sstevel@tonic-gate bge_update_copper,
10280Sstevel@tonic-gate bge_check_copper
10290Sstevel@tonic-gate };
10300Sstevel@tonic-gate
10310Sstevel@tonic-gate
10320Sstevel@tonic-gate /*
10330Sstevel@tonic-gate * ========== SerDes support ==========
10340Sstevel@tonic-gate */
10350Sstevel@tonic-gate
10360Sstevel@tonic-gate #undef BGE_DBG
10370Sstevel@tonic-gate #define BGE_DBG BGE_DBG_SERDES /* debug flag for this code */
10380Sstevel@tonic-gate
10390Sstevel@tonic-gate /*
10400Sstevel@tonic-gate * Reinitialise the SerDes interface. Note that it normally powers
10410Sstevel@tonic-gate * up in the disabled state, so we need to explicitly activate it.
10420Sstevel@tonic-gate */
10431865Sdilpreet static int
bge_restart_serdes(bge_t * bgep,boolean_t powerdown)10440Sstevel@tonic-gate bge_restart_serdes(bge_t *bgep, boolean_t powerdown)
10450Sstevel@tonic-gate {
10460Sstevel@tonic-gate uint32_t macmode;
10470Sstevel@tonic-gate
10480Sstevel@tonic-gate BGE_TRACE(("bge_restart_serdes($%p, %d)", (void *)bgep, powerdown));
10490Sstevel@tonic-gate
10500Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
10510Sstevel@tonic-gate
10520Sstevel@tonic-gate /*
10530Sstevel@tonic-gate * Ensure that the main Ethernet MAC mode register is programmed
10540Sstevel@tonic-gate * appropriately for the SerDes interface ...
10550Sstevel@tonic-gate */
10560Sstevel@tonic-gate macmode = bge_reg_get32(bgep, ETHERNET_MAC_MODE_REG);
10577561SCrisson.Hu@Sun.COM if (DEVICE_5714_SERIES_CHIPSETS(bgep)) {
10587561SCrisson.Hu@Sun.COM macmode |= ETHERNET_MODE_LINK_POLARITY;
10597561SCrisson.Hu@Sun.COM macmode &= ~ETHERNET_MODE_PORTMODE_MASK;
10607561SCrisson.Hu@Sun.COM macmode |= ETHERNET_MODE_PORTMODE_GMII;
10617561SCrisson.Hu@Sun.COM } else {
10627561SCrisson.Hu@Sun.COM macmode &= ~ETHERNET_MODE_LINK_POLARITY;
10637561SCrisson.Hu@Sun.COM macmode &= ~ETHERNET_MODE_PORTMODE_MASK;
10647561SCrisson.Hu@Sun.COM macmode |= ETHERNET_MODE_PORTMODE_TBI;
10657561SCrisson.Hu@Sun.COM }
10660Sstevel@tonic-gate bge_reg_put32(bgep, ETHERNET_MAC_MODE_REG, macmode);
10670Sstevel@tonic-gate
10680Sstevel@tonic-gate /*
10690Sstevel@tonic-gate * Ensure that loopback is OFF and comma detection is enabled. Then
10700Sstevel@tonic-gate * disable the SerDes output (the first time through, it may/will
10710Sstevel@tonic-gate * already be disabled). If we're shutting down, leave it disabled.
10720Sstevel@tonic-gate */
10730Sstevel@tonic-gate bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TBI_LOOPBACK);
10740Sstevel@tonic-gate bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_COMMA_DETECT);
10750Sstevel@tonic-gate bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
10760Sstevel@tonic-gate if (powerdown)
10771865Sdilpreet return (DDI_SUCCESS);
10780Sstevel@tonic-gate
10790Sstevel@tonic-gate /*
10800Sstevel@tonic-gate * Otherwise, pause, (re-)enable the SerDes output, and send
10810Sstevel@tonic-gate * all-zero config words in order to force autoneg restart.
10820Sstevel@tonic-gate * Invalidate the saved "link partners received configs", as
10830Sstevel@tonic-gate * we're starting over ...
10840Sstevel@tonic-gate */
10850Sstevel@tonic-gate drv_usecwait(10000);
10860Sstevel@tonic-gate bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE);
10870Sstevel@tonic-gate bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG, 0);
10880Sstevel@tonic-gate bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
10890Sstevel@tonic-gate drv_usecwait(10);
10900Sstevel@tonic-gate bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS);
10910Sstevel@tonic-gate bgep->serdes_lpadv = AUTONEG_CODE_FAULT_ANEG_ERR;
10920Sstevel@tonic-gate bgep->serdes_status = ~0U;
10931865Sdilpreet return (DDI_SUCCESS);
10940Sstevel@tonic-gate }
10950Sstevel@tonic-gate
10960Sstevel@tonic-gate /*
10970Sstevel@tonic-gate * Synchronise the SerDes speed/duplex/autonegotiation capabilities and
10980Sstevel@tonic-gate * advertisements with the required settings as specified by the various
10990Sstevel@tonic-gate * param_* variables that can be poked via the NDD interface.
11000Sstevel@tonic-gate *
11010Sstevel@tonic-gate * We always reinitalise the SerDes; this should cause the link to go down,
11020Sstevel@tonic-gate * and then back up again once the link is stable and autonegotiation
11030Sstevel@tonic-gate * (if enabled) is complete. We should get a link state change interrupt
11040Sstevel@tonic-gate * somewhere along the way ...
11050Sstevel@tonic-gate *
11060Sstevel@tonic-gate * NOTE: SerDes only supports 1000FDX/HDX (with or without pause) so the
11070Sstevel@tonic-gate * param_* variables relating to lower speeds are ignored.
11080Sstevel@tonic-gate *
11090Sstevel@tonic-gate * NOTE: <genlock> must already be held by the caller
11100Sstevel@tonic-gate */
11111865Sdilpreet static int
bge_update_serdes(bge_t * bgep)11120Sstevel@tonic-gate bge_update_serdes(bge_t *bgep)
11130Sstevel@tonic-gate {
11140Sstevel@tonic-gate boolean_t adv_autoneg;
11150Sstevel@tonic-gate boolean_t adv_pause;
11160Sstevel@tonic-gate boolean_t adv_asym_pause;
11170Sstevel@tonic-gate boolean_t adv_1000fdx;
11180Sstevel@tonic-gate boolean_t adv_1000hdx;
11190Sstevel@tonic-gate
11200Sstevel@tonic-gate uint32_t serdes;
11210Sstevel@tonic-gate uint32_t advert;
11220Sstevel@tonic-gate
11230Sstevel@tonic-gate BGE_TRACE(("bge_update_serdes($%p)", (void *)bgep));
11240Sstevel@tonic-gate
11250Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
11260Sstevel@tonic-gate
11270Sstevel@tonic-gate BGE_DEBUG(("bge_update_serdes: autoneg %d "
11286219Sgh162552 "pause %d asym_pause %d "
11296219Sgh162552 "1000fdx %d 1000hdx %d "
11306219Sgh162552 "100fdx %d 100hdx %d "
11316219Sgh162552 "10fdx %d 10hdx %d ",
11326219Sgh162552 bgep->param_adv_autoneg,
11336219Sgh162552 bgep->param_adv_pause, bgep->param_adv_asym_pause,
11346219Sgh162552 bgep->param_adv_1000fdx, bgep->param_adv_1000hdx,
11356219Sgh162552 bgep->param_adv_100fdx, bgep->param_adv_100hdx,
11366219Sgh162552 bgep->param_adv_10fdx, bgep->param_adv_10hdx));
11370Sstevel@tonic-gate
11380Sstevel@tonic-gate serdes = advert = 0;
11390Sstevel@tonic-gate
11400Sstevel@tonic-gate /*
11410Sstevel@tonic-gate * SerDes settings are normally based on the param_* variables,
11420Sstevel@tonic-gate * but if any loopback mode is in effect, that takes precedence.
11430Sstevel@tonic-gate *
11440Sstevel@tonic-gate * BGE supports MAC-internal loopback, PHY-internal loopback,
11450Sstevel@tonic-gate * and External loopback at a variety of speeds (with a special
11460Sstevel@tonic-gate * cable). In all cases, autoneg is turned OFF, full-duplex
11470Sstevel@tonic-gate * is turned ON, and the speed/mastership is forced.
11480Sstevel@tonic-gate *
11490Sstevel@tonic-gate * Note: for the SerDes interface, "PHY" internal loopback is
11500Sstevel@tonic-gate * interpreted as SerDes internal loopback, and all external
11510Sstevel@tonic-gate * loopback modes are treated equivalently, as 1Gb/external.
11520Sstevel@tonic-gate */
11530Sstevel@tonic-gate switch (bgep->param_loop_mode) {
11540Sstevel@tonic-gate case BGE_LOOP_NONE:
11550Sstevel@tonic-gate default:
11560Sstevel@tonic-gate adv_autoneg = bgep->param_adv_autoneg;
11570Sstevel@tonic-gate adv_pause = bgep->param_adv_pause;
11580Sstevel@tonic-gate adv_asym_pause = bgep->param_adv_asym_pause;
11590Sstevel@tonic-gate adv_1000fdx = bgep->param_adv_1000fdx;
11600Sstevel@tonic-gate adv_1000hdx = bgep->param_adv_1000hdx;
11610Sstevel@tonic-gate break;
11620Sstevel@tonic-gate
11630Sstevel@tonic-gate case BGE_LOOP_INTERNAL_PHY:
11640Sstevel@tonic-gate serdes |= SERDES_CONTROL_TBI_LOOPBACK;
11650Sstevel@tonic-gate /* FALLTHRU */
11660Sstevel@tonic-gate case BGE_LOOP_INTERNAL_MAC:
11670Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_1000:
11680Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_100:
11690Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_10:
11700Sstevel@tonic-gate adv_autoneg = adv_pause = adv_asym_pause = B_FALSE;
11710Sstevel@tonic-gate adv_1000fdx = B_TRUE;
11720Sstevel@tonic-gate adv_1000hdx = B_FALSE;
11730Sstevel@tonic-gate break;
11740Sstevel@tonic-gate }
11750Sstevel@tonic-gate
11760Sstevel@tonic-gate BGE_DEBUG(("bge_update_serdes: autoneg %d "
11776219Sgh162552 "pause %d asym_pause %d "
11786219Sgh162552 "1000fdx %d 1000hdx %d ",
11796219Sgh162552 adv_autoneg,
11806219Sgh162552 adv_pause, adv_asym_pause,
11816219Sgh162552 adv_1000fdx, adv_1000hdx));
11820Sstevel@tonic-gate
11830Sstevel@tonic-gate /*
11840Sstevel@tonic-gate * We should have at least one gigabit technology capability
11850Sstevel@tonic-gate * set; if not, we select a default of 1000Mb/s full-duplex
11860Sstevel@tonic-gate */
11870Sstevel@tonic-gate if (!adv_1000fdx && !adv_1000hdx)
11880Sstevel@tonic-gate adv_1000fdx = B_TRUE;
11890Sstevel@tonic-gate
11900Sstevel@tonic-gate /*
11910Sstevel@tonic-gate * Now transform the adv_* variables into the proper settings
11920Sstevel@tonic-gate * of the SerDes registers ...
11930Sstevel@tonic-gate *
11940Sstevel@tonic-gate * If autonegotiation is (now) not enabled, pretend it's been
11950Sstevel@tonic-gate * done and failed ...
11960Sstevel@tonic-gate */
11970Sstevel@tonic-gate if (!adv_autoneg)
11980Sstevel@tonic-gate advert |= AUTONEG_CODE_FAULT_ANEG_ERR;
11990Sstevel@tonic-gate
12000Sstevel@tonic-gate if (adv_1000fdx) {
12010Sstevel@tonic-gate advert |= AUTONEG_CODE_FULL_DUPLEX;
12020Sstevel@tonic-gate bgep->param_adv_1000fdx = adv_1000fdx;
12030Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_FULL;
12040Sstevel@tonic-gate bgep->param_link_speed = 1000;
12050Sstevel@tonic-gate }
12060Sstevel@tonic-gate if (adv_1000hdx) {
12070Sstevel@tonic-gate advert |= AUTONEG_CODE_HALF_DUPLEX;
12080Sstevel@tonic-gate bgep->param_adv_1000hdx = adv_1000hdx;
12090Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_HALF;
12100Sstevel@tonic-gate bgep->param_link_speed = 1000;
12110Sstevel@tonic-gate }
12120Sstevel@tonic-gate
12130Sstevel@tonic-gate if (adv_pause)
12140Sstevel@tonic-gate advert |= AUTONEG_CODE_PAUSE;
12150Sstevel@tonic-gate if (adv_asym_pause)
12160Sstevel@tonic-gate advert |= AUTONEG_CODE_ASYM_PAUSE;
12170Sstevel@tonic-gate
12180Sstevel@tonic-gate /*
12190Sstevel@tonic-gate * Restart the SerDes and write the new values. Note the
12200Sstevel@tonic-gate * time, so that we can say whether subsequent link state
12210Sstevel@tonic-gate * changes can be attributed to our reprogramming the SerDes
12220Sstevel@tonic-gate */
12230Sstevel@tonic-gate bgep->serdes_advert = advert;
12241865Sdilpreet (void) bge_restart_serdes(bgep, B_FALSE);
12250Sstevel@tonic-gate bge_reg_set32(bgep, SERDES_CONTROL_REG, serdes);
12260Sstevel@tonic-gate
12270Sstevel@tonic-gate BGE_DEBUG(("bge_update_serdes: serdes |= 0x%x, advert 0x%x",
12286219Sgh162552 serdes, advert));
12291865Sdilpreet return (DDI_SUCCESS);
12300Sstevel@tonic-gate }
12310Sstevel@tonic-gate
12320Sstevel@tonic-gate /*
12330Sstevel@tonic-gate * Bare-minimum autoneg protocol
12340Sstevel@tonic-gate *
12350Sstevel@tonic-gate * This code is only called when the link is up and we're receiving config
12360Sstevel@tonic-gate * words, which implies that the link partner wants to autonegotiate
12370Sstevel@tonic-gate * (otherwise, we wouldn't see configs and wouldn't reach this code).
12380Sstevel@tonic-gate */
12390Sstevel@tonic-gate static void
bge_autoneg_serdes(bge_t * bgep)12400Sstevel@tonic-gate bge_autoneg_serdes(bge_t *bgep)
12410Sstevel@tonic-gate {
12420Sstevel@tonic-gate boolean_t ack;
12430Sstevel@tonic-gate
12440Sstevel@tonic-gate bgep->serdes_lpadv = bge_reg_get32(bgep, RX_1000BASEX_AUTONEG_REG);
12450Sstevel@tonic-gate ack = BIS(bgep->serdes_lpadv, AUTONEG_CODE_ACKNOWLEDGE);
12460Sstevel@tonic-gate
12470Sstevel@tonic-gate if (!ack) {
12480Sstevel@tonic-gate /*
12490Sstevel@tonic-gate * Phase 1: after SerDes reset, we send a few zero configs
12500Sstevel@tonic-gate * but then stop. Here the partner is sending configs, but
12510Sstevel@tonic-gate * not ACKing ours; we assume that's 'cos we're not sending
12520Sstevel@tonic-gate * any. So here we send ours, with ACK already set.
12530Sstevel@tonic-gate */
12540Sstevel@tonic-gate bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG,
12556219Sgh162552 bgep->serdes_advert | AUTONEG_CODE_ACKNOWLEDGE);
12560Sstevel@tonic-gate bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG,
12576219Sgh162552 ETHERNET_MODE_SEND_CFGS);
12580Sstevel@tonic-gate } else {
12590Sstevel@tonic-gate /*
12600Sstevel@tonic-gate * Phase 2: partner has ACKed our configs, so now we can
12610Sstevel@tonic-gate * stop sending; once our partner also stops sending, we
12620Sstevel@tonic-gate * can resolve the Tx/Rx configs.
12630Sstevel@tonic-gate */
12640Sstevel@tonic-gate bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG,
12656219Sgh162552 ETHERNET_MODE_SEND_CFGS);
12660Sstevel@tonic-gate }
12670Sstevel@tonic-gate
12680Sstevel@tonic-gate BGE_DEBUG(("bge_autoneg_serdes: Rx 0x%x %s Tx 0x%x",
12696219Sgh162552 bgep->serdes_lpadv,
12706219Sgh162552 ack ? "stop" : "send",
12716219Sgh162552 bgep->serdes_advert));
12720Sstevel@tonic-gate }
12730Sstevel@tonic-gate
12740Sstevel@tonic-gate static boolean_t
bge_check_serdes(bge_t * bgep,boolean_t recheck)12750Sstevel@tonic-gate bge_check_serdes(bge_t *bgep, boolean_t recheck)
12760Sstevel@tonic-gate {
12770Sstevel@tonic-gate uint32_t emac_status;
1278*13026SYong.Tan@Sun.COM uint32_t tx_status;
12790Sstevel@tonic-gate uint32_t lpadv;
12800Sstevel@tonic-gate boolean_t linkup;
12817561SCrisson.Hu@Sun.COM boolean_t linkup_old = bgep->param_link_up;
12820Sstevel@tonic-gate
12830Sstevel@tonic-gate for (;;) {
12840Sstevel@tonic-gate /*
12857561SCrisson.Hu@Sun.COM * Step 10: BCM5714S, BCM5715S only
12867561SCrisson.Hu@Sun.COM * Don't call function bge_autoneg_serdes() as
12877561SCrisson.Hu@Sun.COM * RX_1000BASEX_AUTONEG_REG (0x0448) is not applicable
12887561SCrisson.Hu@Sun.COM * to BCM5705, BCM5788, BCM5721, BCM5751, BCM5752,
12897561SCrisson.Hu@Sun.COM * BCM5714, and BCM5715 devices.
12900Sstevel@tonic-gate */
12917561SCrisson.Hu@Sun.COM if (DEVICE_5714_SERIES_CHIPSETS(bgep)) {
1292*13026SYong.Tan@Sun.COM tx_status = bge_reg_get32(bgep,
1293*13026SYong.Tan@Sun.COM TRANSMIT_MAC_STATUS_REG);
1294*13026SYong.Tan@Sun.COM linkup = BIS(tx_status, TRANSMIT_STATUS_LINK_UP);
1295*13026SYong.Tan@Sun.COM emac_status = bge_reg_get32(bgep,
1296*13026SYong.Tan@Sun.COM ETHERNET_MAC_STATUS_REG);
12977561SCrisson.Hu@Sun.COM bgep->serdes_status = emac_status;
12987561SCrisson.Hu@Sun.COM if ((linkup && linkup_old) ||
12997561SCrisson.Hu@Sun.COM (!linkup && !linkup_old)) {
13007561SCrisson.Hu@Sun.COM emac_status &= ~ETHERNET_STATUS_LINK_CHANGED;
13017561SCrisson.Hu@Sun.COM emac_status &= ~ETHERNET_STATUS_RECEIVING_CFG;
13027561SCrisson.Hu@Sun.COM break;
13037561SCrisson.Hu@Sun.COM }
13047561SCrisson.Hu@Sun.COM emac_status |= ETHERNET_STATUS_LINK_CHANGED;
13057561SCrisson.Hu@Sun.COM emac_status |= ETHERNET_STATUS_RECEIVING_CFG;
13067561SCrisson.Hu@Sun.COM if (linkup)
13077561SCrisson.Hu@Sun.COM linkup_old = B_TRUE;
13087561SCrisson.Hu@Sun.COM else
13097561SCrisson.Hu@Sun.COM linkup_old = B_FALSE;
13107561SCrisson.Hu@Sun.COM recheck = B_TRUE;
13117561SCrisson.Hu@Sun.COM } else {
13127561SCrisson.Hu@Sun.COM /*
13137561SCrisson.Hu@Sun.COM * Step 10: others
13147561SCrisson.Hu@Sun.COM * read & clear the main (Ethernet) MAC status
13157561SCrisson.Hu@Sun.COM * (the relevant bits of this are write-one-to-clear).
13167561SCrisson.Hu@Sun.COM */
13177561SCrisson.Hu@Sun.COM emac_status = bge_reg_get32(bgep,
13187561SCrisson.Hu@Sun.COM ETHERNET_MAC_STATUS_REG);
13197561SCrisson.Hu@Sun.COM bge_reg_put32(bgep,
13207561SCrisson.Hu@Sun.COM ETHERNET_MAC_STATUS_REG, emac_status);
13210Sstevel@tonic-gate
13227561SCrisson.Hu@Sun.COM BGE_DEBUG(("bge_check_serdes: link %d/%s, "
13237561SCrisson.Hu@Sun.COM "MAC status 0x%x (was 0x%x)",
13247561SCrisson.Hu@Sun.COM bgep->link_state, UPORDOWN(bgep->param_link_up),
13257561SCrisson.Hu@Sun.COM emac_status, bgep->serdes_status));
13260Sstevel@tonic-gate
13277561SCrisson.Hu@Sun.COM /*
13287561SCrisson.Hu@Sun.COM * We will only consider the link UP if all the readings
13297561SCrisson.Hu@Sun.COM * are consistent and give meaningful results ...
13307561SCrisson.Hu@Sun.COM */
13317561SCrisson.Hu@Sun.COM bgep->serdes_status = emac_status;
13327561SCrisson.Hu@Sun.COM linkup = BIS(emac_status,
13337561SCrisson.Hu@Sun.COM ETHERNET_STATUS_SIGNAL_DETECT);
13347561SCrisson.Hu@Sun.COM linkup &= BIS(emac_status, ETHERNET_STATUS_PCS_SYNCHED);
13350Sstevel@tonic-gate
13367561SCrisson.Hu@Sun.COM /*
13377561SCrisson.Hu@Sun.COM * Now some fiddling with the interpretation:
13387561SCrisson.Hu@Sun.COM * if there's been an error at the PCS level, treat
13397561SCrisson.Hu@Sun.COM * it as a link change (the h/w doesn't do this)
13407561SCrisson.Hu@Sun.COM *
13417561SCrisson.Hu@Sun.COM * if there's been a change, but it's only a PCS
13427561SCrisson.Hu@Sun.COM * sync change (not a config change), AND the link
13437561SCrisson.Hu@Sun.COM * already was & is still UP, then ignore the
13447561SCrisson.Hu@Sun.COM * change
13457561SCrisson.Hu@Sun.COM */
13467561SCrisson.Hu@Sun.COM if (BIS(emac_status, ETHERNET_STATUS_PCS_ERROR))
13477561SCrisson.Hu@Sun.COM emac_status |= ETHERNET_STATUS_LINK_CHANGED;
13487561SCrisson.Hu@Sun.COM else if (BIC(emac_status, ETHERNET_STATUS_CFG_CHANGED))
13497561SCrisson.Hu@Sun.COM if (bgep->param_link_up && linkup)
13507561SCrisson.Hu@Sun.COM emac_status &=
13517561SCrisson.Hu@Sun.COM ~ETHERNET_STATUS_LINK_CHANGED;
13520Sstevel@tonic-gate
13537561SCrisson.Hu@Sun.COM BGE_DEBUG(("bge_check_serdes: status 0x%x => 0x%x %s",
13547561SCrisson.Hu@Sun.COM bgep->serdes_status, emac_status,
13557561SCrisson.Hu@Sun.COM UPORDOWN(linkup)));
13567561SCrisson.Hu@Sun.COM
13577561SCrisson.Hu@Sun.COM /*
13587561SCrisson.Hu@Sun.COM * If we're receiving configs, run the autoneg protocol
13597561SCrisson.Hu@Sun.COM */
13607561SCrisson.Hu@Sun.COM if (linkup && BIS(emac_status,
13617561SCrisson.Hu@Sun.COM ETHERNET_STATUS_RECEIVING_CFG))
13627561SCrisson.Hu@Sun.COM bge_autoneg_serdes(bgep);
13630Sstevel@tonic-gate
13647561SCrisson.Hu@Sun.COM /*
13657561SCrisson.Hu@Sun.COM * If the SerDes status hasn't changed, we're done ...
13667561SCrisson.Hu@Sun.COM */
13677561SCrisson.Hu@Sun.COM if (BIC(emac_status, ETHERNET_STATUS_LINK_CHANGED))
13687561SCrisson.Hu@Sun.COM break;
13697561SCrisson.Hu@Sun.COM
13707561SCrisson.Hu@Sun.COM /*
13717561SCrisson.Hu@Sun.COM * Go round again until we no longer see a change ...
13727561SCrisson.Hu@Sun.COM */
13737561SCrisson.Hu@Sun.COM recheck = B_TRUE;
13747561SCrisson.Hu@Sun.COM }
13750Sstevel@tonic-gate }
13760Sstevel@tonic-gate
13770Sstevel@tonic-gate /*
13780Sstevel@tonic-gate * If we're not forcing a recheck (i.e. the link state was already
13790Sstevel@tonic-gate * known), and we didn't see the hardware flag a change, there's
13800Sstevel@tonic-gate * no more to do (and we tell the caller nothing happened).
13810Sstevel@tonic-gate */
13820Sstevel@tonic-gate if (!recheck)
13830Sstevel@tonic-gate return (B_FALSE);
13840Sstevel@tonic-gate
13850Sstevel@tonic-gate /*
13860Sstevel@tonic-gate * Don't resolve autoneg until we're no longer receiving configs
13870Sstevel@tonic-gate */
13880Sstevel@tonic-gate if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG))
13890Sstevel@tonic-gate return (B_FALSE);
13900Sstevel@tonic-gate
13910Sstevel@tonic-gate /*
13920Sstevel@tonic-gate * Assume very little ...
13930Sstevel@tonic-gate */
13940Sstevel@tonic-gate bgep->param_lp_autoneg = B_FALSE;
13950Sstevel@tonic-gate bgep->param_lp_1000fdx = B_FALSE;
13960Sstevel@tonic-gate bgep->param_lp_1000hdx = B_FALSE;
13970Sstevel@tonic-gate bgep->param_lp_100fdx = B_FALSE;
13980Sstevel@tonic-gate bgep->param_lp_100hdx = B_FALSE;
13990Sstevel@tonic-gate bgep->param_lp_10fdx = B_FALSE;
14000Sstevel@tonic-gate bgep->param_lp_10hdx = B_FALSE;
14010Sstevel@tonic-gate bgep->param_lp_pause = B_FALSE;
14020Sstevel@tonic-gate bgep->param_lp_asym_pause = B_FALSE;
14030Sstevel@tonic-gate bgep->param_link_autoneg = B_FALSE;
14040Sstevel@tonic-gate bgep->param_link_tx_pause = B_FALSE;
14050Sstevel@tonic-gate if (bgep->param_adv_autoneg)
14060Sstevel@tonic-gate bgep->param_link_rx_pause = B_FALSE;
14070Sstevel@tonic-gate else
14080Sstevel@tonic-gate bgep->param_link_rx_pause = bgep->param_adv_pause;
14090Sstevel@tonic-gate
14100Sstevel@tonic-gate /*
14110Sstevel@tonic-gate * Discover all the link partner's abilities.
14120Sstevel@tonic-gate */
14130Sstevel@tonic-gate lpadv = bgep->serdes_lpadv;
14140Sstevel@tonic-gate if (lpadv != 0 && BIC(lpadv, AUTONEG_CODE_FAULT_MASK)) {
14150Sstevel@tonic-gate /*
14160Sstevel@tonic-gate * No fault, so derive partner's capabilities
14170Sstevel@tonic-gate */
14180Sstevel@tonic-gate bgep->param_lp_autoneg = B_TRUE;
14190Sstevel@tonic-gate bgep->param_lp_1000fdx = BIS(lpadv, AUTONEG_CODE_FULL_DUPLEX);
14200Sstevel@tonic-gate bgep->param_lp_1000hdx = BIS(lpadv, AUTONEG_CODE_HALF_DUPLEX);
14210Sstevel@tonic-gate bgep->param_lp_pause = BIS(lpadv, AUTONEG_CODE_PAUSE);
14220Sstevel@tonic-gate bgep->param_lp_asym_pause = BIS(lpadv, AUTONEG_CODE_ASYM_PAUSE);
14230Sstevel@tonic-gate
14240Sstevel@tonic-gate /*
14250Sstevel@tonic-gate * Pause direction resolution
14260Sstevel@tonic-gate */
14270Sstevel@tonic-gate bgep->param_link_autoneg = B_TRUE;
14280Sstevel@tonic-gate if (bgep->param_adv_pause &&
14290Sstevel@tonic-gate bgep->param_lp_pause) {
14300Sstevel@tonic-gate bgep->param_link_tx_pause = B_TRUE;
14310Sstevel@tonic-gate bgep->param_link_rx_pause = B_TRUE;
14320Sstevel@tonic-gate }
14330Sstevel@tonic-gate if (bgep->param_adv_asym_pause &&
14340Sstevel@tonic-gate bgep->param_lp_asym_pause) {
14350Sstevel@tonic-gate if (bgep->param_adv_pause)
14360Sstevel@tonic-gate bgep->param_link_rx_pause = B_TRUE;
14370Sstevel@tonic-gate if (bgep->param_lp_pause)
14380Sstevel@tonic-gate bgep->param_link_tx_pause = B_TRUE;
14390Sstevel@tonic-gate }
14400Sstevel@tonic-gate }
14410Sstevel@tonic-gate
14420Sstevel@tonic-gate /*
14430Sstevel@tonic-gate * Step 12: update ndd-visible state parameters, BUT!
14440Sstevel@tonic-gate * we don't transfer the new state to <link_state> just yet;
14450Sstevel@tonic-gate * instead we mark the <link_state> as UNKNOWN, and our caller
14460Sstevel@tonic-gate * will resolve it once the status has stopped changing and
14470Sstevel@tonic-gate * been stable for several seconds.
14480Sstevel@tonic-gate */
14490Sstevel@tonic-gate BGE_DEBUG(("bge_check_serdes: link was %s speed %d duplex %d",
14506219Sgh162552 UPORDOWN(bgep->param_link_up),
14516219Sgh162552 bgep->param_link_speed,
14526219Sgh162552 bgep->param_link_duplex));
14530Sstevel@tonic-gate
14540Sstevel@tonic-gate if (linkup) {
14550Sstevel@tonic-gate bgep->param_link_up = B_TRUE;
14560Sstevel@tonic-gate bgep->param_link_speed = 1000;
14570Sstevel@tonic-gate if (bgep->param_adv_1000fdx)
14580Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_FULL;
14590Sstevel@tonic-gate else
14600Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_HALF;
14610Sstevel@tonic-gate if (bgep->param_lp_autoneg && !bgep->param_lp_1000fdx)
14620Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_HALF;
14630Sstevel@tonic-gate } else {
14640Sstevel@tonic-gate bgep->param_link_up = B_FALSE;
14650Sstevel@tonic-gate bgep->param_link_speed = 0;
14660Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_UNKNOWN;
14670Sstevel@tonic-gate }
14680Sstevel@tonic-gate bgep->link_state = LINK_STATE_UNKNOWN;
14690Sstevel@tonic-gate
14700Sstevel@tonic-gate BGE_DEBUG(("bge_check_serdes: link now %s speed %d duplex %d",
14716219Sgh162552 UPORDOWN(bgep->param_link_up),
14726219Sgh162552 bgep->param_link_speed,
14736219Sgh162552 bgep->param_link_duplex));
14740Sstevel@tonic-gate
14750Sstevel@tonic-gate return (B_TRUE);
14760Sstevel@tonic-gate }
14770Sstevel@tonic-gate
14780Sstevel@tonic-gate static const phys_ops_t serdes_ops = {
14790Sstevel@tonic-gate bge_restart_serdes,
14800Sstevel@tonic-gate bge_update_serdes,
14810Sstevel@tonic-gate bge_check_serdes
14820Sstevel@tonic-gate };
14830Sstevel@tonic-gate
14840Sstevel@tonic-gate /*
14850Sstevel@tonic-gate * ========== Exported physical layer control routines ==========
14860Sstevel@tonic-gate */
14870Sstevel@tonic-gate
14880Sstevel@tonic-gate #undef BGE_DBG
14890Sstevel@tonic-gate #define BGE_DBG BGE_DBG_PHYS /* debug flag for this code */
14900Sstevel@tonic-gate
14910Sstevel@tonic-gate /*
14920Sstevel@tonic-gate * Here we have to determine which media we're using (copper or serdes).
14930Sstevel@tonic-gate * Once that's done, we can initialise the physical layer appropriately.
14940Sstevel@tonic-gate */
14951865Sdilpreet int
bge_phys_init(bge_t * bgep)14960Sstevel@tonic-gate bge_phys_init(bge_t *bgep)
14970Sstevel@tonic-gate {
14980Sstevel@tonic-gate BGE_TRACE(("bge_phys_init($%p)", (void *)bgep));
14990Sstevel@tonic-gate
15000Sstevel@tonic-gate mutex_enter(bgep->genlock);
15010Sstevel@tonic-gate
15020Sstevel@tonic-gate /*
15030Sstevel@tonic-gate * Probe for the (internal) PHY. If it's not there, we'll assume
1504388Sly149593 * that this is a 5703/4S, with a SerDes interface rather than
1505388Sly149593 * a PHY. BCM5714S/BCM5715S are not supported.It are based on
1506388Sly149593 * BCM800x PHY.
15070Sstevel@tonic-gate */
15080Sstevel@tonic-gate bgep->phy_mii_addr = 1;
150911968SYong.Tan@Sun.COM if (DEVICE_5717_SERIES_CHIPSETS(bgep)) {
151011968SYong.Tan@Sun.COM int regval = bge_reg_get32(bgep, CPMU_STATUS_REG);
151111968SYong.Tan@Sun.COM if (regval & CPMU_STATUS_FUN_NUM)
151211968SYong.Tan@Sun.COM bgep->phy_mii_addr += 1;
151311968SYong.Tan@Sun.COM regval = bge_reg_get32(bgep, SGMII_STATUS_REG);
151411968SYong.Tan@Sun.COM if (regval & MEDIA_SELECTION_MODE)
151511968SYong.Tan@Sun.COM bgep->phy_mii_addr += 7;
151611968SYong.Tan@Sun.COM }
151711968SYong.Tan@Sun.COM
15180Sstevel@tonic-gate if (bge_phy_probe(bgep)) {
15190Sstevel@tonic-gate bgep->chipid.flags &= ~CHIP_FLAG_SERDES;
15200Sstevel@tonic-gate bgep->physops = &copper_ops;
15210Sstevel@tonic-gate } else {
15220Sstevel@tonic-gate bgep->chipid.flags |= CHIP_FLAG_SERDES;
15230Sstevel@tonic-gate bgep->physops = &serdes_ops;
15240Sstevel@tonic-gate }
15250Sstevel@tonic-gate
15261865Sdilpreet if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS) {
15271865Sdilpreet mutex_exit(bgep->genlock);
15281865Sdilpreet return (EIO);
15291865Sdilpreet }
15301865Sdilpreet if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK) {
15311865Sdilpreet mutex_exit(bgep->genlock);
15321865Sdilpreet return (EIO);
15331865Sdilpreet }
15340Sstevel@tonic-gate mutex_exit(bgep->genlock);
15351865Sdilpreet return (0);
15360Sstevel@tonic-gate }
15370Sstevel@tonic-gate
15380Sstevel@tonic-gate /*
15390Sstevel@tonic-gate * Reset the physical layer
15400Sstevel@tonic-gate */
15410Sstevel@tonic-gate void
bge_phys_reset(bge_t * bgep)15420Sstevel@tonic-gate bge_phys_reset(bge_t *bgep)
15430Sstevel@tonic-gate {
15440Sstevel@tonic-gate BGE_TRACE(("bge_phys_reset($%p)", (void *)bgep));
15450Sstevel@tonic-gate
15460Sstevel@tonic-gate mutex_enter(bgep->genlock);
15471865Sdilpreet if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS)
15481865Sdilpreet ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED);
15491865Sdilpreet if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK)
15501865Sdilpreet ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED);
15510Sstevel@tonic-gate mutex_exit(bgep->genlock);
15520Sstevel@tonic-gate }
15530Sstevel@tonic-gate
15540Sstevel@tonic-gate /*
15550Sstevel@tonic-gate * Reset and power off the physical layer.
15560Sstevel@tonic-gate *
15570Sstevel@tonic-gate * Another RESET should get it back to working, but it may take a few
15580Sstevel@tonic-gate * seconds it may take a few moments to return to normal operation ...
15590Sstevel@tonic-gate */
15601865Sdilpreet int
bge_phys_idle(bge_t * bgep)15610Sstevel@tonic-gate bge_phys_idle(bge_t *bgep)
15620Sstevel@tonic-gate {
15630Sstevel@tonic-gate BGE_TRACE(("bge_phys_idle($%p)", (void *)bgep));
15640Sstevel@tonic-gate
15650Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
15661865Sdilpreet return ((*bgep->physops->phys_restart)(bgep, B_TRUE));
15670Sstevel@tonic-gate }
15680Sstevel@tonic-gate
15690Sstevel@tonic-gate /*
15700Sstevel@tonic-gate * Synchronise the PHYSICAL layer's speed/duplex/autonegotiation capabilities
15710Sstevel@tonic-gate * and advertisements with the required settings as specified by the various
15720Sstevel@tonic-gate * param_* variables that can be poked via the NDD interface.
15730Sstevel@tonic-gate *
15740Sstevel@tonic-gate * We always reset the PHYSICAL layer and reprogram *all* relevant registers.
15750Sstevel@tonic-gate * This is expected to cause the link to go down, and then back up again once
15760Sstevel@tonic-gate * the link is stable and autonegotiation (if enabled) is complete. We should
15770Sstevel@tonic-gate * get a link state change interrupt somewhere along the way ...
15780Sstevel@tonic-gate *
15790Sstevel@tonic-gate * NOTE: <genlock> must already be held by the caller
15800Sstevel@tonic-gate */
15811865Sdilpreet int
bge_phys_update(bge_t * bgep)15820Sstevel@tonic-gate bge_phys_update(bge_t *bgep)
15830Sstevel@tonic-gate {
15840Sstevel@tonic-gate BGE_TRACE(("bge_phys_update($%p)", (void *)bgep));
15850Sstevel@tonic-gate
15860Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
15871865Sdilpreet return ((*bgep->physops->phys_update)(bgep));
15880Sstevel@tonic-gate }
15890Sstevel@tonic-gate
15900Sstevel@tonic-gate #undef BGE_DBG
15910Sstevel@tonic-gate #define BGE_DBG BGE_DBG_LINK /* debug flag for this code */
15920Sstevel@tonic-gate
15930Sstevel@tonic-gate /*
15940Sstevel@tonic-gate * Read the link status and determine whether anything's changed ...
15950Sstevel@tonic-gate *
15960Sstevel@tonic-gate * This routine should be called whenever the chip flags a change
15972675Szh199473 * in the hardware link state.
15980Sstevel@tonic-gate *
15990Sstevel@tonic-gate * This routine returns B_FALSE if the link state has not changed,
16000Sstevel@tonic-gate * returns B_TRUE when the change to the new state should be accepted.
16010Sstevel@tonic-gate * In such a case, the param_* variables give the new hardware state,
16020Sstevel@tonic-gate * which the caller should use to update link_state etc.
16030Sstevel@tonic-gate *
16040Sstevel@tonic-gate * The caller must already hold <genlock>
16050Sstevel@tonic-gate */
16060Sstevel@tonic-gate boolean_t
bge_phys_check(bge_t * bgep)16070Sstevel@tonic-gate bge_phys_check(bge_t *bgep)
16080Sstevel@tonic-gate {
16090Sstevel@tonic-gate int32_t orig_state;
16100Sstevel@tonic-gate boolean_t recheck;
16110Sstevel@tonic-gate
16120Sstevel@tonic-gate BGE_TRACE(("bge_phys_check($%p)", (void *)bgep));
16130Sstevel@tonic-gate
16140Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock));
16150Sstevel@tonic-gate
16160Sstevel@tonic-gate orig_state = bgep->link_state;
16170Sstevel@tonic-gate recheck = orig_state == LINK_STATE_UNKNOWN;
16180Sstevel@tonic-gate recheck = (*bgep->physops->phys_check)(bgep, recheck);
16190Sstevel@tonic-gate if (!recheck)
16200Sstevel@tonic-gate return (B_FALSE);
16210Sstevel@tonic-gate
16222675Szh199473 return (B_TRUE);
16230Sstevel@tonic-gate }
1624