1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include "sys/bge_impl.h" 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate /* 32*0Sstevel@tonic-gate * Bit test macros, returning boolean_t values 33*0Sstevel@tonic-gate */ 34*0Sstevel@tonic-gate #define BIS(w, b) (((w) & (b)) ? B_TRUE : B_FALSE) 35*0Sstevel@tonic-gate #define BIC(w, b) (((w) & (b)) ? B_FALSE : B_TRUE) 36*0Sstevel@tonic-gate #define UPORDOWN(x) ((x) ? "up" : "down") 37*0Sstevel@tonic-gate 38*0Sstevel@tonic-gate /* 39*0Sstevel@tonic-gate * ========== Copper (PHY) support ========== 40*0Sstevel@tonic-gate */ 41*0Sstevel@tonic-gate 42*0Sstevel@tonic-gate #define BGE_DBG BGE_DBG_PHY /* debug flag for this code */ 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate /* 45*0Sstevel@tonic-gate * #defines: 46*0Sstevel@tonic-gate * BGE_COPPER_WIRESPEED controls whether the Broadcom WireSpeed(tm) 47*0Sstevel@tonic-gate * feature is enabled. We need to recheck whether this can be 48*0Sstevel@tonic-gate * enabled; at one time it seemed to interact unpleasantly with the 49*0Sstevel@tonic-gate * loopback modes. 50*0Sstevel@tonic-gate * 51*0Sstevel@tonic-gate * BGE_COPPER_IDLEOFF controls whether the (copper) PHY power is 52*0Sstevel@tonic-gate * turned off when the PHY is idled i.e. during driver suspend(). 53*0Sstevel@tonic-gate * For now this is disabled because the chip doesn't seem to 54*0Sstevel@tonic-gate * resume cleanly if the PHY power is turned off. 55*0Sstevel@tonic-gate */ 56*0Sstevel@tonic-gate #define BGE_COPPER_WIRESPEED B_TRUE 57*0Sstevel@tonic-gate #define BGE_COPPER_IDLEOFF B_FALSE 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate /* 60*0Sstevel@tonic-gate * The arrays below can be indexed by the MODE bits from the Auxiliary 61*0Sstevel@tonic-gate * Status register to determine the current speed/duplex settings. 62*0Sstevel@tonic-gate */ 63*0Sstevel@tonic-gate static const int16_t bge_copper_link_speed[] = { 64*0Sstevel@tonic-gate 0, /* MII_AUX_STATUS_MODE_NONE */ 65*0Sstevel@tonic-gate 10, /* MII_AUX_STATUS_MODE_10_H */ 66*0Sstevel@tonic-gate 10, /* MII_AUX_STATUS_MODE_10_F */ 67*0Sstevel@tonic-gate 100, /* MII_AUX_STATUS_MODE_100_H */ 68*0Sstevel@tonic-gate 0, /* MII_AUX_STATUS_MODE_100_4 */ 69*0Sstevel@tonic-gate 100, /* MII_AUX_STATUS_MODE_100_F */ 70*0Sstevel@tonic-gate 1000, /* MII_AUX_STATUS_MODE_1000_H */ 71*0Sstevel@tonic-gate 1000 /* MII_AUX_STATUS_MODE_1000_F */ 72*0Sstevel@tonic-gate }; 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate static const int8_t bge_copper_link_duplex[] = { 75*0Sstevel@tonic-gate LINK_DUPLEX_UNKNOWN, /* MII_AUX_STATUS_MODE_NONE */ 76*0Sstevel@tonic-gate LINK_DUPLEX_HALF, /* MII_AUX_STATUS_MODE_10_H */ 77*0Sstevel@tonic-gate LINK_DUPLEX_FULL, /* MII_AUX_STATUS_MODE_10_F */ 78*0Sstevel@tonic-gate LINK_DUPLEX_HALF, /* MII_AUX_STATUS_MODE_100_H */ 79*0Sstevel@tonic-gate LINK_DUPLEX_UNKNOWN, /* MII_AUX_STATUS_MODE_100_4 */ 80*0Sstevel@tonic-gate LINK_DUPLEX_FULL, /* MII_AUX_STATUS_MODE_100_F */ 81*0Sstevel@tonic-gate LINK_DUPLEX_HALF, /* MII_AUX_STATUS_MODE_1000_H */ 82*0Sstevel@tonic-gate LINK_DUPLEX_FULL /* MII_AUX_STATUS_MODE_1000_F */ 83*0Sstevel@tonic-gate }; 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate static const char * const bge_copper_link_text[] = { 86*0Sstevel@tonic-gate "down", /* MII_AUX_STATUS_MODE_NONE */ 87*0Sstevel@tonic-gate "up 10Mbps Half-Duplex", /* MII_AUX_STATUS_MODE_10_H */ 88*0Sstevel@tonic-gate "up 10Mbps Full-Duplex", /* MII_AUX_STATUS_MODE_10_F */ 89*0Sstevel@tonic-gate "up 100Mbps Half-Duplex", /* MII_AUX_STATUS_MODE_100_H */ 90*0Sstevel@tonic-gate "down", /* MII_AUX_STATUS_MODE_100_4 */ 91*0Sstevel@tonic-gate "up 100Mbps Full-Duplex", /* MII_AUX_STATUS_MODE_100_F */ 92*0Sstevel@tonic-gate "up 1000Mbps Half-Duplex", /* MII_AUX_STATUS_MODE_1000_H */ 93*0Sstevel@tonic-gate "up 1000Mbps Full-Duplex" /* MII_AUX_STATUS_MODE_1000_F */ 94*0Sstevel@tonic-gate }; 95*0Sstevel@tonic-gate 96*0Sstevel@tonic-gate #if BGE_DEBUGGING 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate static void 99*0Sstevel@tonic-gate bge_phydump(bge_t *bgep, uint16_t mii_status, uint16_t aux) 100*0Sstevel@tonic-gate { 101*0Sstevel@tonic-gate uint16_t regs[32]; 102*0Sstevel@tonic-gate int i; 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 105*0Sstevel@tonic-gate 106*0Sstevel@tonic-gate for (i = 0; i < 32; ++i) 107*0Sstevel@tonic-gate switch (i) { 108*0Sstevel@tonic-gate default: 109*0Sstevel@tonic-gate regs[i] = bge_mii_get16(bgep, i); 110*0Sstevel@tonic-gate break; 111*0Sstevel@tonic-gate 112*0Sstevel@tonic-gate case MII_STATUS: 113*0Sstevel@tonic-gate regs[i] = mii_status; 114*0Sstevel@tonic-gate break; 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate case MII_AUX_STATUS: 117*0Sstevel@tonic-gate regs[i] = aux; 118*0Sstevel@tonic-gate break; 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate case 0x0b: case 0x0c: case 0x0d: case 0x0e: 121*0Sstevel@tonic-gate case 0x15: case 0x16: case 0x17: 122*0Sstevel@tonic-gate case 0x1c: 123*0Sstevel@tonic-gate case 0x1f: 124*0Sstevel@tonic-gate /* reserved registers -- don't read these */ 125*0Sstevel@tonic-gate regs[i] = 0; 126*0Sstevel@tonic-gate break; 127*0Sstevel@tonic-gate } 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate for (i = 0; i < 32; i += 8) 130*0Sstevel@tonic-gate BGE_DEBUG(("bge_phydump: " 131*0Sstevel@tonic-gate "0x%04x %04x %04x %04x %04x %04x %04x %04x", 132*0Sstevel@tonic-gate regs[i+0], regs[i+1], regs[i+2], regs[i+3], 133*0Sstevel@tonic-gate regs[i+4], regs[i+5], regs[i+6], regs[i+7])); 134*0Sstevel@tonic-gate } 135*0Sstevel@tonic-gate 136*0Sstevel@tonic-gate #endif /* BGE_DEBUGGING */ 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate /* 139*0Sstevel@tonic-gate * Basic low-level function to probe for a PHY 140*0Sstevel@tonic-gate * 141*0Sstevel@tonic-gate * Returns TRUE if the PHY responds with valid data, FALSE otherwise 142*0Sstevel@tonic-gate */ 143*0Sstevel@tonic-gate static boolean_t 144*0Sstevel@tonic-gate bge_phy_probe(bge_t *bgep) 145*0Sstevel@tonic-gate { 146*0Sstevel@tonic-gate uint16_t phy_status; 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate BGE_TRACE(("bge_phy_probe($%p)", (void *)bgep)); 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gate /* 153*0Sstevel@tonic-gate * Read the MII_STATUS register twice, in 154*0Sstevel@tonic-gate * order to clear any sticky bits (but they should 155*0Sstevel@tonic-gate * have been cleared by the RESET, I think). 156*0Sstevel@tonic-gate */ 157*0Sstevel@tonic-gate phy_status = bge_mii_get16(bgep, MII_STATUS); 158*0Sstevel@tonic-gate phy_status = bge_mii_get16(bgep, MII_STATUS); 159*0Sstevel@tonic-gate BGE_DEBUG(("bge_phy_probe: status 0x%x", phy_status)); 160*0Sstevel@tonic-gate 161*0Sstevel@tonic-gate /* 162*0Sstevel@tonic-gate * Now check the value read; it should have at least one bit set 163*0Sstevel@tonic-gate * (for the device capabilities) and at least one clear (one of 164*0Sstevel@tonic-gate * the error bits). So if we see all 0s or all 1s, there's a 165*0Sstevel@tonic-gate * problem. In particular, bge_mii_get16() returns all 1s if 166*0Sstevel@tonic-gate * communications fails ... 167*0Sstevel@tonic-gate */ 168*0Sstevel@tonic-gate switch (phy_status) { 169*0Sstevel@tonic-gate case 0x0000: 170*0Sstevel@tonic-gate case 0xffff: 171*0Sstevel@tonic-gate return (B_FALSE); 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate default : 174*0Sstevel@tonic-gate return (B_TRUE); 175*0Sstevel@tonic-gate } 176*0Sstevel@tonic-gate } 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate /* 179*0Sstevel@tonic-gate * Basic low-level function to reset the PHY. 180*0Sstevel@tonic-gate * Doesn't incorporate any special-case workarounds. 181*0Sstevel@tonic-gate * 182*0Sstevel@tonic-gate * Returns TRUE on success, FALSE if the RESET bit doesn't clear 183*0Sstevel@tonic-gate */ 184*0Sstevel@tonic-gate static boolean_t 185*0Sstevel@tonic-gate bge_phy_reset(bge_t *bgep) 186*0Sstevel@tonic-gate { 187*0Sstevel@tonic-gate uint16_t control; 188*0Sstevel@tonic-gate uint_t count; 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate BGE_TRACE(("bge_phy_reset($%p)", (void *)bgep)); 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate /* 195*0Sstevel@tonic-gate * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear 196*0Sstevel@tonic-gate */ 197*0Sstevel@tonic-gate bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_RESET); 198*0Sstevel@tonic-gate for (count = 0; ++count < 1000; ) { 199*0Sstevel@tonic-gate drv_usecwait(5); 200*0Sstevel@tonic-gate control = bge_mii_get16(bgep, MII_CONTROL); 201*0Sstevel@tonic-gate if (BIC(control, MII_CONTROL_RESET)) 202*0Sstevel@tonic-gate return (B_TRUE); 203*0Sstevel@tonic-gate } 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate BGE_DEBUG(("bge_phy_reset: FAILED, control now 0x%x", control)); 206*0Sstevel@tonic-gate 207*0Sstevel@tonic-gate return (B_FALSE); 208*0Sstevel@tonic-gate } 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate /* 211*0Sstevel@tonic-gate * Basic low-level function to powerdown the PHY, if supported 212*0Sstevel@tonic-gate * If powerdown support is compiled out, this function does nothing. 213*0Sstevel@tonic-gate */ 214*0Sstevel@tonic-gate static void 215*0Sstevel@tonic-gate bge_phy_powerdown(bge_t *bgep) 216*0Sstevel@tonic-gate { 217*0Sstevel@tonic-gate BGE_TRACE(("bge_phy_powerdown")); 218*0Sstevel@tonic-gate #if BGE_COPPER_IDLEOFF 219*0Sstevel@tonic-gate bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_PWRDN); 220*0Sstevel@tonic-gate #endif /* BGE_COPPER_IDLEOFF */ 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate /* 224*0Sstevel@tonic-gate * The following functions are based on sample code provided by 225*0Sstevel@tonic-gate * Broadcom (20-June-2003), and implement workarounds said to be 226*0Sstevel@tonic-gate * required on the early revisions of the BCM5703/4C. 227*0Sstevel@tonic-gate * 228*0Sstevel@tonic-gate * The registers and values used are mostly UNDOCUMENTED, and 229*0Sstevel@tonic-gate * therefore don't have symbolic names ;-( 230*0Sstevel@tonic-gate * 231*0Sstevel@tonic-gate * Many of the comments are straight out of the Broadcom code: 232*0Sstevel@tonic-gate * even where the code has been restructured, the original 233*0Sstevel@tonic-gate * comments have been preserved in order to explain what these 234*0Sstevel@tonic-gate * undocumented registers & values are all about ... 235*0Sstevel@tonic-gate */ 236*0Sstevel@tonic-gate 237*0Sstevel@tonic-gate static void 238*0Sstevel@tonic-gate bge_phy_macro_wait(bge_t *bgep) 239*0Sstevel@tonic-gate { 240*0Sstevel@tonic-gate uint_t count; 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate for (count = 100; --count; ) 243*0Sstevel@tonic-gate if ((bge_mii_get16(bgep, 0x16) & 0x1000) == 0) 244*0Sstevel@tonic-gate break; 245*0Sstevel@tonic-gate } 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate /* 248*0Sstevel@tonic-gate * PHY test data pattern: 249*0Sstevel@tonic-gate * 250*0Sstevel@tonic-gate * For 5703/04, each DFE TAP has 21-bits (low word 15, hi word 6) 251*0Sstevel@tonic-gate * For 5705, each DFE TAP has 19-bits (low word 15, hi word 4) 252*0Sstevel@tonic-gate * For simplicity, we check only 19-bits, so we don't have to 253*0Sstevel@tonic-gate * distinguish which chip it is. 254*0Sstevel@tonic-gate * the LO word contains 15 bits, make sure pattern data is < 0x7fff 255*0Sstevel@tonic-gate * the HI word contains 6 bits, make sure pattern data is < 0x003f 256*0Sstevel@tonic-gate */ 257*0Sstevel@tonic-gate #define N_CHANNELS 4 258*0Sstevel@tonic-gate #define N_TAPS 3 259*0Sstevel@tonic-gate 260*0Sstevel@tonic-gate static struct { 261*0Sstevel@tonic-gate uint16_t lo; 262*0Sstevel@tonic-gate uint16_t hi; 263*0Sstevel@tonic-gate } tap_data[N_CHANNELS][N_TAPS] = { 264*0Sstevel@tonic-gate { 265*0Sstevel@tonic-gate { 0x5555, 0x0005 }, /* ch0, TAP 0, LO/HI pattern */ 266*0Sstevel@tonic-gate { 0x2aaa, 0x000a }, /* ch0, TAP 1, LO/HI pattern */ 267*0Sstevel@tonic-gate { 0x3456, 0x0003 } /* ch0, TAP 2, LO/HI pattern */ 268*0Sstevel@tonic-gate }, 269*0Sstevel@tonic-gate { 270*0Sstevel@tonic-gate { 0x2aaa, 0x000a }, /* ch1, TAP 0, LO/HI pattern */ 271*0Sstevel@tonic-gate { 0x3333, 0x0003 }, /* ch1, TAP 1, LO/HI pattern */ 272*0Sstevel@tonic-gate { 0x789a, 0x0005 } /* ch1, TAP 2, LO/HI pattern */ 273*0Sstevel@tonic-gate }, 274*0Sstevel@tonic-gate { 275*0Sstevel@tonic-gate { 0x5a5a, 0x0005 }, /* ch2, TAP 0, LO/HI pattern */ 276*0Sstevel@tonic-gate { 0x2a6a, 0x000a }, /* ch2, TAP 1, LO/HI pattern */ 277*0Sstevel@tonic-gate { 0x1bcd, 0x0003 } /* ch2, TAP 2, LO/HI pattern */ 278*0Sstevel@tonic-gate }, 279*0Sstevel@tonic-gate { 280*0Sstevel@tonic-gate { 0x2a5a, 0x000a }, /* ch3, TAP 0, LO/HI pattern */ 281*0Sstevel@tonic-gate { 0x33c3, 0x0003 }, /* ch3, TAP 1, LO/HI pattern */ 282*0Sstevel@tonic-gate { 0x2ef1, 0x0005 } /* ch3, TAP 2, LO/HI pattern */ 283*0Sstevel@tonic-gate } 284*0Sstevel@tonic-gate }; 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate /* 287*0Sstevel@tonic-gate * Check whether the PHY has locked up after a RESET. 288*0Sstevel@tonic-gate * 289*0Sstevel@tonic-gate * Returns TRUE if it did, FALSE is it's OK ;-) 290*0Sstevel@tonic-gate */ 291*0Sstevel@tonic-gate static boolean_t 292*0Sstevel@tonic-gate bge_phy_locked_up(bge_t *bgep) 293*0Sstevel@tonic-gate { 294*0Sstevel@tonic-gate uint16_t dataLo; 295*0Sstevel@tonic-gate uint16_t dataHi; 296*0Sstevel@tonic-gate uint_t chan; 297*0Sstevel@tonic-gate uint_t tap; 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate /* 300*0Sstevel@tonic-gate * Check TAPs for all 4 channels, as soon as we see a lockup 301*0Sstevel@tonic-gate * we'll stop checking. 302*0Sstevel@tonic-gate */ 303*0Sstevel@tonic-gate for (chan = 0; chan < N_CHANNELS; ++chan) { 304*0Sstevel@tonic-gate /* Select channel and set TAP index to 0 */ 305*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200); 306*0Sstevel@tonic-gate /* Freeze filter again just to be safe */ 307*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0002); 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate /* 310*0Sstevel@tonic-gate * Write fixed pattern to the RAM, 3 TAPs for 311*0Sstevel@tonic-gate * each channel, each TAP have 2 WORDs (LO/HI) 312*0Sstevel@tonic-gate */ 313*0Sstevel@tonic-gate for (tap = 0; tap < N_TAPS; ++tap) { 314*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, tap_data[chan][tap].lo); 315*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, tap_data[chan][tap].hi); 316*0Sstevel@tonic-gate } 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate /* 319*0Sstevel@tonic-gate * Active PHY's Macro operation to write DFE 320*0Sstevel@tonic-gate * TAP from RAM, and wait for Macro to complete. 321*0Sstevel@tonic-gate */ 322*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0202); 323*0Sstevel@tonic-gate bge_phy_macro_wait(bgep); 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate /* 326*0Sstevel@tonic-gate * Done with write phase, now begin read phase. 327*0Sstevel@tonic-gate */ 328*0Sstevel@tonic-gate 329*0Sstevel@tonic-gate /* Select channel and set TAP index to 0 */ 330*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, (chan << 13) | 0x0200); 331*0Sstevel@tonic-gate 332*0Sstevel@tonic-gate /* 333*0Sstevel@tonic-gate * Active PHY's Macro operation to load DFE 334*0Sstevel@tonic-gate * TAP to RAM, and wait for Macro to complete 335*0Sstevel@tonic-gate */ 336*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0082); 337*0Sstevel@tonic-gate bge_phy_macro_wait(bgep); 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate /* Enable "pre-fetch" */ 340*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0802); 341*0Sstevel@tonic-gate bge_phy_macro_wait(bgep); 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate /* 344*0Sstevel@tonic-gate * Read back the TAP values. 3 TAPs for each 345*0Sstevel@tonic-gate * channel, each TAP have 2 WORDs (LO/HI) 346*0Sstevel@tonic-gate */ 347*0Sstevel@tonic-gate for (tap = 0; tap < N_TAPS; ++tap) { 348*0Sstevel@tonic-gate /* 349*0Sstevel@tonic-gate * Read Lo/Hi then wait for 'done' is faster. 350*0Sstevel@tonic-gate * For DFE TAP, the HI word contains 6 bits, 351*0Sstevel@tonic-gate * LO word contains 15 bits 352*0Sstevel@tonic-gate */ 353*0Sstevel@tonic-gate dataLo = bge_mii_get16(bgep, 0x15) & 0x7fff; 354*0Sstevel@tonic-gate dataHi = bge_mii_get16(bgep, 0x15) & 0x003f; 355*0Sstevel@tonic-gate bge_phy_macro_wait(bgep); 356*0Sstevel@tonic-gate 357*0Sstevel@tonic-gate /* 358*0Sstevel@tonic-gate * Check if what we wrote is what we read back. 359*0Sstevel@tonic-gate * If failed, then the PHY is locked up, we need 360*0Sstevel@tonic-gate * to do PHY reset again 361*0Sstevel@tonic-gate */ 362*0Sstevel@tonic-gate if (dataLo != tap_data[chan][tap].lo) 363*0Sstevel@tonic-gate return (B_TRUE); /* wedged! */ 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate if (dataHi != tap_data[chan][tap].hi) 366*0Sstevel@tonic-gate return (B_TRUE); /* wedged! */ 367*0Sstevel@tonic-gate } 368*0Sstevel@tonic-gate } 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate /* 371*0Sstevel@tonic-gate * The PHY isn't locked up ;-) 372*0Sstevel@tonic-gate */ 373*0Sstevel@tonic-gate return (B_FALSE); 374*0Sstevel@tonic-gate } 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate /* 377*0Sstevel@tonic-gate * Special-case code to reset the PHY on the 5702/5703/5704C/5705/5782. 378*0Sstevel@tonic-gate * Tries up to 5 times to recover from failure to reset or PHY lockup. 379*0Sstevel@tonic-gate * 380*0Sstevel@tonic-gate * Returns TRUE on success, FALSE if there's an unrecoverable problem 381*0Sstevel@tonic-gate */ 382*0Sstevel@tonic-gate static boolean_t 383*0Sstevel@tonic-gate bge_phy_reset_and_check(bge_t *bgep) 384*0Sstevel@tonic-gate { 385*0Sstevel@tonic-gate boolean_t reset_success; 386*0Sstevel@tonic-gate boolean_t phy_locked; 387*0Sstevel@tonic-gate uint16_t extctrl; 388*0Sstevel@tonic-gate uint_t retries; 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate for (retries = 0; retries < 5; ++retries) { 391*0Sstevel@tonic-gate /* Issue a phy reset, and wait for reset to complete */ 392*0Sstevel@tonic-gate /* Assuming reset is successful first */ 393*0Sstevel@tonic-gate reset_success = bge_phy_reset(bgep); 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate /* 396*0Sstevel@tonic-gate * Now go check the DFE TAPs to see if locked up, but 397*0Sstevel@tonic-gate * first, we need to set up PHY so we can read DFE 398*0Sstevel@tonic-gate * TAPs. 399*0Sstevel@tonic-gate */ 400*0Sstevel@tonic-gate 401*0Sstevel@tonic-gate /* 402*0Sstevel@tonic-gate * Disable Transmitter and Interrupt, while we play 403*0Sstevel@tonic-gate * with the PHY registers, so the link partner won't 404*0Sstevel@tonic-gate * see any strange data and the Driver won't see any 405*0Sstevel@tonic-gate * interrupts. 406*0Sstevel@tonic-gate */ 407*0Sstevel@tonic-gate extctrl = bge_mii_get16(bgep, 0x10); 408*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x10, extctrl | 0x3000); 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gate /* Setup Full-Duplex, 1000 mbps */ 411*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x0, 0x0140); 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate /* Set to Master mode */ 414*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x9, 0x1800); 415*0Sstevel@tonic-gate 416*0Sstevel@tonic-gate /* Enable SM_DSP_CLOCK & 6dB */ 417*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x18, 0x0c00); /* "the ADC fix" */ 418*0Sstevel@tonic-gate 419*0Sstevel@tonic-gate /* Work-arounds */ 420*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x201f); 421*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, 0x2aaa); 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate /* More workarounds */ 424*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x000a); 425*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, 0x0323); /* "the Gamma fix" */ 426*0Sstevel@tonic-gate 427*0Sstevel@tonic-gate /* Blocks the PHY control access */ 428*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x8005); 429*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, 0x0800); 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate /* Test whether PHY locked up ;-( */ 432*0Sstevel@tonic-gate phy_locked = bge_phy_locked_up(bgep); 433*0Sstevel@tonic-gate if (reset_success && !phy_locked) 434*0Sstevel@tonic-gate break; 435*0Sstevel@tonic-gate 436*0Sstevel@tonic-gate /* 437*0Sstevel@tonic-gate * Some problem here ... log it & retry 438*0Sstevel@tonic-gate */ 439*0Sstevel@tonic-gate if (!reset_success) 440*0Sstevel@tonic-gate BGE_REPORT((bgep, "PHY didn't reset!")); 441*0Sstevel@tonic-gate if (phy_locked) 442*0Sstevel@tonic-gate BGE_REPORT((bgep, "PHY locked up!")); 443*0Sstevel@tonic-gate } 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate /* Remove block phy control */ 446*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x8005); 447*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x15, 0x0000); 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate /* Unfreeze DFE TAP filter for all channels */ 450*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x17, 0x8200); 451*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x16, 0x0000); 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gate /* Restore PHY back to operating state */ 454*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x18, 0x0400); 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate /* Enable transmitter and interrupt */ 457*0Sstevel@tonic-gate extctrl = bge_mii_get16(bgep, 0x10); 458*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x10, extctrl & ~0x3000); 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate return (reset_success && !phy_locked); 461*0Sstevel@tonic-gate } 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate static void 464*0Sstevel@tonic-gate bge_phy_tweak_gmii(bge_t *bgep) 465*0Sstevel@tonic-gate { 466*0Sstevel@tonic-gate /* Tweak GMII timing */ 467*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x1c, 0x8d68); 468*0Sstevel@tonic-gate bge_mii_put16(bgep, 0x1c, 0x8d68); 469*0Sstevel@tonic-gate } 470*0Sstevel@tonic-gate 471*0Sstevel@tonic-gate /* 472*0Sstevel@tonic-gate * End of Broadcom-derived workaround code * 473*0Sstevel@tonic-gate */ 474*0Sstevel@tonic-gate 475*0Sstevel@tonic-gate static void 476*0Sstevel@tonic-gate bge_restart_copper(bge_t *bgep, boolean_t powerdown) 477*0Sstevel@tonic-gate { 478*0Sstevel@tonic-gate uint16_t phy_status; 479*0Sstevel@tonic-gate boolean_t reset_ok; 480*0Sstevel@tonic-gate 481*0Sstevel@tonic-gate BGE_TRACE(("bge_restart_copper($%p, %d)", (void *)bgep, powerdown)); 482*0Sstevel@tonic-gate 483*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate switch (MHCR_CHIP_ASIC_REV(bgep->chipid.asic_rev)) { 486*0Sstevel@tonic-gate default: 487*0Sstevel@tonic-gate /* 488*0Sstevel@tonic-gate * Shouldn't happen; it means we don't recognise this chip. 489*0Sstevel@tonic-gate * It's probably a new one, so we'll try our best anyway ... 490*0Sstevel@tonic-gate */ 491*0Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5703: 492*0Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5704: 493*0Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5705: 494*0Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5721_5751: 495*0Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5714: 496*0Sstevel@tonic-gate reset_ok = bge_phy_reset_and_check(bgep); 497*0Sstevel@tonic-gate break; 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5700: 500*0Sstevel@tonic-gate case MHCR_CHIP_ASIC_REV_5701: 501*0Sstevel@tonic-gate /* 502*0Sstevel@tonic-gate * Just a plain reset; the "check" code breaks these chips 503*0Sstevel@tonic-gate */ 504*0Sstevel@tonic-gate reset_ok = bge_phy_reset(bgep); 505*0Sstevel@tonic-gate break; 506*0Sstevel@tonic-gate } 507*0Sstevel@tonic-gate if (!reset_ok) 508*0Sstevel@tonic-gate bge_problem(bgep, "PHY failed to reset correctly"); 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gate /* 511*0Sstevel@tonic-gate * Step 5: disable WOL (not required after RESET) 512*0Sstevel@tonic-gate * 513*0Sstevel@tonic-gate * Step 6: refer to errata 514*0Sstevel@tonic-gate */ 515*0Sstevel@tonic-gate switch (bgep->chipid.asic_rev) { 516*0Sstevel@tonic-gate default: 517*0Sstevel@tonic-gate break; 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate case MHCR_CHIP_REV_5704_A0: 520*0Sstevel@tonic-gate bge_phy_tweak_gmii(bgep); 521*0Sstevel@tonic-gate break; 522*0Sstevel@tonic-gate } 523*0Sstevel@tonic-gate 524*0Sstevel@tonic-gate /* 525*0Sstevel@tonic-gate * Step 7: read the MII_INTR_STATUS register twice, 526*0Sstevel@tonic-gate * in order to clear any sticky bits (but they should 527*0Sstevel@tonic-gate * have been cleared by the RESET, I think), and we're 528*0Sstevel@tonic-gate * not using PHY interrupts anyway. 529*0Sstevel@tonic-gate * 530*0Sstevel@tonic-gate * Step 8: enable the PHY to interrupt on link status 531*0Sstevel@tonic-gate * change (not required) 532*0Sstevel@tonic-gate * 533*0Sstevel@tonic-gate * Step 9: configure PHY LED Mode - not applicable? 534*0Sstevel@tonic-gate * 535*0Sstevel@tonic-gate * Step 10: read the MII_STATUS register twice, in 536*0Sstevel@tonic-gate * order to clear any sticky bits (but they should 537*0Sstevel@tonic-gate * have been cleared by the RESET, I think). 538*0Sstevel@tonic-gate */ 539*0Sstevel@tonic-gate phy_status = bge_mii_get16(bgep, MII_STATUS); 540*0Sstevel@tonic-gate phy_status = bge_mii_get16(bgep, MII_STATUS); 541*0Sstevel@tonic-gate BGE_DEBUG(("bge_restart_copper: status 0x%x", phy_status)); 542*0Sstevel@tonic-gate 543*0Sstevel@tonic-gate /* 544*0Sstevel@tonic-gate * Finally, shut down the PHY, if required 545*0Sstevel@tonic-gate */ 546*0Sstevel@tonic-gate if (powerdown) 547*0Sstevel@tonic-gate bge_phy_powerdown(bgep); 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate /* 551*0Sstevel@tonic-gate * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities 552*0Sstevel@tonic-gate * and advertisements with the required settings as specified by the various 553*0Sstevel@tonic-gate * param_* variables that can be poked via the NDD interface. 554*0Sstevel@tonic-gate * 555*0Sstevel@tonic-gate * We always reset the PHY and reprogram *all* the relevant registers, 556*0Sstevel@tonic-gate * not just those changed. This should cause the link to go down, and then 557*0Sstevel@tonic-gate * back up again once the link is stable and autonegotiation (if enabled) 558*0Sstevel@tonic-gate * is complete. We should get a link state change interrupt somewhere along 559*0Sstevel@tonic-gate * the way ... 560*0Sstevel@tonic-gate * 561*0Sstevel@tonic-gate * NOTE: <genlock> must already be held by the caller 562*0Sstevel@tonic-gate */ 563*0Sstevel@tonic-gate static void 564*0Sstevel@tonic-gate bge_update_copper(bge_t *bgep) 565*0Sstevel@tonic-gate { 566*0Sstevel@tonic-gate boolean_t adv_autoneg; 567*0Sstevel@tonic-gate boolean_t adv_pause; 568*0Sstevel@tonic-gate boolean_t adv_asym_pause; 569*0Sstevel@tonic-gate boolean_t adv_1000fdx; 570*0Sstevel@tonic-gate boolean_t adv_1000hdx; 571*0Sstevel@tonic-gate boolean_t adv_100fdx; 572*0Sstevel@tonic-gate boolean_t adv_100hdx; 573*0Sstevel@tonic-gate boolean_t adv_10fdx; 574*0Sstevel@tonic-gate boolean_t adv_10hdx; 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate uint16_t control; 577*0Sstevel@tonic-gate uint16_t gigctrl; 578*0Sstevel@tonic-gate uint16_t auxctrl; 579*0Sstevel@tonic-gate uint16_t anar; 580*0Sstevel@tonic-gate 581*0Sstevel@tonic-gate BGE_TRACE(("bge_update_copper($%p)", (void *)bgep)); 582*0Sstevel@tonic-gate 583*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: autoneg %d " 586*0Sstevel@tonic-gate "pause %d asym_pause %d " 587*0Sstevel@tonic-gate "1000fdx %d 1000hdx %d " 588*0Sstevel@tonic-gate "100fdx %d 100hdx %d " 589*0Sstevel@tonic-gate "10fdx %d 10hdx %d ", 590*0Sstevel@tonic-gate bgep->param_adv_autoneg, 591*0Sstevel@tonic-gate bgep->param_adv_pause, bgep->param_adv_asym_pause, 592*0Sstevel@tonic-gate bgep->param_adv_1000fdx, bgep->param_adv_1000hdx, 593*0Sstevel@tonic-gate bgep->param_adv_100fdx, bgep->param_adv_100hdx, 594*0Sstevel@tonic-gate bgep->param_adv_10fdx, bgep->param_adv_10hdx)); 595*0Sstevel@tonic-gate 596*0Sstevel@tonic-gate control = gigctrl = auxctrl = anar = 0; 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate /* 599*0Sstevel@tonic-gate * PHY settings are normally based on the param_* variables, 600*0Sstevel@tonic-gate * but if any loopback mode is in effect, that takes precedence. 601*0Sstevel@tonic-gate * 602*0Sstevel@tonic-gate * BGE supports MAC-internal loopback, PHY-internal loopback, 603*0Sstevel@tonic-gate * and External loopback at a variety of speeds (with a special 604*0Sstevel@tonic-gate * cable). In all cases, autoneg is turned OFF, full-duplex 605*0Sstevel@tonic-gate * is turned ON, and the speed/mastership is forced. 606*0Sstevel@tonic-gate */ 607*0Sstevel@tonic-gate switch (bgep->param_loop_mode) { 608*0Sstevel@tonic-gate case BGE_LOOP_NONE: 609*0Sstevel@tonic-gate default: 610*0Sstevel@tonic-gate adv_autoneg = bgep->param_adv_autoneg; 611*0Sstevel@tonic-gate adv_pause = bgep->param_adv_pause; 612*0Sstevel@tonic-gate adv_asym_pause = bgep->param_adv_asym_pause; 613*0Sstevel@tonic-gate adv_1000fdx = bgep->param_adv_1000fdx; 614*0Sstevel@tonic-gate adv_1000hdx = bgep->param_adv_1000hdx; 615*0Sstevel@tonic-gate adv_100fdx = bgep->param_adv_100fdx; 616*0Sstevel@tonic-gate adv_100hdx = bgep->param_adv_100hdx; 617*0Sstevel@tonic-gate adv_10fdx = bgep->param_adv_10fdx; 618*0Sstevel@tonic-gate adv_10hdx = bgep->param_adv_10hdx; 619*0Sstevel@tonic-gate break; 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_1000: 622*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_100: 623*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_10: 624*0Sstevel@tonic-gate case BGE_LOOP_INTERNAL_PHY: 625*0Sstevel@tonic-gate case BGE_LOOP_INTERNAL_MAC: 626*0Sstevel@tonic-gate adv_autoneg = adv_pause = adv_asym_pause = B_FALSE; 627*0Sstevel@tonic-gate adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE; 628*0Sstevel@tonic-gate adv_1000hdx = adv_100hdx = adv_10hdx = B_FALSE; 629*0Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_FULL; 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate switch (bgep->param_loop_mode) { 632*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_1000: 633*0Sstevel@tonic-gate bgep->param_link_speed = 1000; 634*0Sstevel@tonic-gate adv_1000fdx = B_TRUE; 635*0Sstevel@tonic-gate auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK; 636*0Sstevel@tonic-gate gigctrl |= MII_1000BT_CTL_MASTER_CFG; 637*0Sstevel@tonic-gate gigctrl |= MII_1000BT_CTL_MASTER_SEL; 638*0Sstevel@tonic-gate break; 639*0Sstevel@tonic-gate 640*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_100: 641*0Sstevel@tonic-gate bgep->param_link_speed = 100; 642*0Sstevel@tonic-gate adv_100fdx = B_TRUE; 643*0Sstevel@tonic-gate auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK; 644*0Sstevel@tonic-gate break; 645*0Sstevel@tonic-gate 646*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_10: 647*0Sstevel@tonic-gate bgep->param_link_speed = 10; 648*0Sstevel@tonic-gate adv_10fdx = B_TRUE; 649*0Sstevel@tonic-gate auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK; 650*0Sstevel@tonic-gate break; 651*0Sstevel@tonic-gate 652*0Sstevel@tonic-gate case BGE_LOOP_INTERNAL_PHY: 653*0Sstevel@tonic-gate bgep->param_link_speed = 1000; 654*0Sstevel@tonic-gate adv_1000fdx = B_TRUE; 655*0Sstevel@tonic-gate control = MII_CONTROL_LOOPBACK; 656*0Sstevel@tonic-gate break; 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate case BGE_LOOP_INTERNAL_MAC: 659*0Sstevel@tonic-gate bgep->param_link_speed = 1000; 660*0Sstevel@tonic-gate adv_1000fdx = B_TRUE; 661*0Sstevel@tonic-gate break; 662*0Sstevel@tonic-gate } 663*0Sstevel@tonic-gate } 664*0Sstevel@tonic-gate 665*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: autoneg %d " 666*0Sstevel@tonic-gate "pause %d asym_pause %d " 667*0Sstevel@tonic-gate "1000fdx %d 1000hdx %d " 668*0Sstevel@tonic-gate "100fdx %d 100hdx %d " 669*0Sstevel@tonic-gate "10fdx %d 10hdx %d ", 670*0Sstevel@tonic-gate adv_autoneg, 671*0Sstevel@tonic-gate adv_pause, adv_asym_pause, 672*0Sstevel@tonic-gate adv_1000fdx, adv_1000hdx, 673*0Sstevel@tonic-gate adv_100fdx, adv_100hdx, 674*0Sstevel@tonic-gate adv_10fdx, adv_10hdx)); 675*0Sstevel@tonic-gate 676*0Sstevel@tonic-gate /* 677*0Sstevel@tonic-gate * We should have at least one technology capability set; 678*0Sstevel@tonic-gate * if not, we select a default of 1000Mb/s full-duplex 679*0Sstevel@tonic-gate */ 680*0Sstevel@tonic-gate if (!adv_1000fdx && !adv_100fdx && !adv_10fdx && 681*0Sstevel@tonic-gate !adv_1000hdx && !adv_100hdx && !adv_10hdx) 682*0Sstevel@tonic-gate adv_1000fdx = B_TRUE; 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate /* 685*0Sstevel@tonic-gate * Now transform the adv_* variables into the proper settings 686*0Sstevel@tonic-gate * of the PHY registers ... 687*0Sstevel@tonic-gate * 688*0Sstevel@tonic-gate * If autonegotiation is (now) enabled, we want to trigger 689*0Sstevel@tonic-gate * a new autonegotiation cycle once the PHY has been 690*0Sstevel@tonic-gate * programmed with the capabilities to be advertised. 691*0Sstevel@tonic-gate */ 692*0Sstevel@tonic-gate if (adv_autoneg) 693*0Sstevel@tonic-gate control |= MII_CONTROL_ANE|MII_CONTROL_RSAN; 694*0Sstevel@tonic-gate 695*0Sstevel@tonic-gate if (adv_1000fdx) 696*0Sstevel@tonic-gate control |= MII_CONTROL_1000MB|MII_CONTROL_FDUPLEX; 697*0Sstevel@tonic-gate else if (adv_1000hdx) 698*0Sstevel@tonic-gate control |= MII_CONTROL_1000MB; 699*0Sstevel@tonic-gate else if (adv_100fdx) 700*0Sstevel@tonic-gate control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX; 701*0Sstevel@tonic-gate else if (adv_100hdx) 702*0Sstevel@tonic-gate control |= MII_CONTROL_100MB; 703*0Sstevel@tonic-gate else if (adv_10fdx) 704*0Sstevel@tonic-gate control |= MII_CONTROL_FDUPLEX; 705*0Sstevel@tonic-gate else if (adv_10hdx) 706*0Sstevel@tonic-gate control |= 0; 707*0Sstevel@tonic-gate else 708*0Sstevel@tonic-gate { _NOTE(EMPTY); } /* Can't get here anyway ... */ 709*0Sstevel@tonic-gate 710*0Sstevel@tonic-gate if (adv_1000fdx) 711*0Sstevel@tonic-gate gigctrl |= MII_1000BT_CTL_ADV_FDX; 712*0Sstevel@tonic-gate if (adv_1000hdx) 713*0Sstevel@tonic-gate gigctrl |= MII_1000BT_CTL_ADV_HDX; 714*0Sstevel@tonic-gate 715*0Sstevel@tonic-gate if (adv_100fdx) 716*0Sstevel@tonic-gate anar |= MII_ABILITY_100BASE_TX_FD; 717*0Sstevel@tonic-gate if (adv_100hdx) 718*0Sstevel@tonic-gate anar |= MII_ABILITY_100BASE_TX; 719*0Sstevel@tonic-gate if (adv_10fdx) 720*0Sstevel@tonic-gate anar |= MII_ABILITY_10BASE_T_FD; 721*0Sstevel@tonic-gate if (adv_10hdx) 722*0Sstevel@tonic-gate anar |= MII_ABILITY_10BASE_T; 723*0Sstevel@tonic-gate 724*0Sstevel@tonic-gate if (adv_pause) 725*0Sstevel@tonic-gate anar |= MII_ABILITY_PAUSE; 726*0Sstevel@tonic-gate if (adv_asym_pause) 727*0Sstevel@tonic-gate anar |= MII_ABILITY_ASYM_PAUSE; 728*0Sstevel@tonic-gate 729*0Sstevel@tonic-gate /* 730*0Sstevel@tonic-gate * Munge in any other fixed bits we require ... 731*0Sstevel@tonic-gate */ 732*0Sstevel@tonic-gate anar |= MII_AN_SELECTOR_8023; 733*0Sstevel@tonic-gate auxctrl |= MII_AUX_CTRL_NORM_TX_MODE; 734*0Sstevel@tonic-gate auxctrl |= MII_AUX_CTRL_NORMAL; 735*0Sstevel@tonic-gate 736*0Sstevel@tonic-gate /* 737*0Sstevel@tonic-gate * Restart the PHY and write the new values. Note the 738*0Sstevel@tonic-gate * time, so that we can say whether subsequent link state 739*0Sstevel@tonic-gate * changes can be attributed to our reprogramming the PHY 740*0Sstevel@tonic-gate */ 741*0Sstevel@tonic-gate bgep->phys_write_time = gethrtime(); 742*0Sstevel@tonic-gate (*bgep->physops->phys_restart)(bgep, B_FALSE); 743*0Sstevel@tonic-gate bge_mii_put16(bgep, MII_AN_ADVERT, anar); 744*0Sstevel@tonic-gate bge_mii_put16(bgep, MII_CONTROL, control); 745*0Sstevel@tonic-gate bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl); 746*0Sstevel@tonic-gate bge_mii_put16(bgep, MII_1000BASE_T_CONTROL, gigctrl); 747*0Sstevel@tonic-gate 748*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: anar <- 0x%x", anar)); 749*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: control <- 0x%x", control)); 750*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: auxctrl <- 0x%x", auxctrl)); 751*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_copper: gigctrl <- 0x%x", gigctrl)); 752*0Sstevel@tonic-gate 753*0Sstevel@tonic-gate #if BGE_COPPER_WIRESPEED 754*0Sstevel@tonic-gate /* 755*0Sstevel@tonic-gate * Enable the 'wire-speed' feature, if the chip supports it 756*0Sstevel@tonic-gate * and we haven't got (any) loopback mode selected. 757*0Sstevel@tonic-gate */ 758*0Sstevel@tonic-gate switch (bgep->chipid.device) { 759*0Sstevel@tonic-gate case DEVICE_ID_5700: 760*0Sstevel@tonic-gate case DEVICE_ID_5700x: 761*0Sstevel@tonic-gate case DEVICE_ID_5705: 762*0Sstevel@tonic-gate case DEVICE_ID_5705C: 763*0Sstevel@tonic-gate case DEVICE_ID_5782: 764*0Sstevel@tonic-gate /* 765*0Sstevel@tonic-gate * These chips are known or assumed not to support it 766*0Sstevel@tonic-gate */ 767*0Sstevel@tonic-gate break; 768*0Sstevel@tonic-gate 769*0Sstevel@tonic-gate default: 770*0Sstevel@tonic-gate /* 771*0Sstevel@tonic-gate * All other Broadcom chips are expected to support it. 772*0Sstevel@tonic-gate */ 773*0Sstevel@tonic-gate if (bgep->param_loop_mode == BGE_LOOP_NONE) 774*0Sstevel@tonic-gate bge_mii_put16(bgep, MII_AUX_CONTROL, 775*0Sstevel@tonic-gate MII_AUX_CTRL_MISC_WRITE_ENABLE | 776*0Sstevel@tonic-gate MII_AUX_CTRL_MISC_WIRE_SPEED | 777*0Sstevel@tonic-gate MII_AUX_CTRL_MISC); 778*0Sstevel@tonic-gate break; 779*0Sstevel@tonic-gate } 780*0Sstevel@tonic-gate #endif /* BGE_COPPER_WIRESPEED */ 781*0Sstevel@tonic-gate } 782*0Sstevel@tonic-gate 783*0Sstevel@tonic-gate static boolean_t 784*0Sstevel@tonic-gate bge_check_copper(bge_t *bgep, boolean_t recheck) 785*0Sstevel@tonic-gate { 786*0Sstevel@tonic-gate uint32_t emac_status; 787*0Sstevel@tonic-gate uint16_t mii_status; 788*0Sstevel@tonic-gate uint16_t aux; 789*0Sstevel@tonic-gate uint_t mode; 790*0Sstevel@tonic-gate boolean_t linkup; 791*0Sstevel@tonic-gate 792*0Sstevel@tonic-gate /* 793*0Sstevel@tonic-gate * Step 10: read the status from the PHY (which is self-clearing 794*0Sstevel@tonic-gate * on read!); also read & clear the main (Ethernet) MAC status 795*0Sstevel@tonic-gate * (the relevant bits of this are write-one-to-clear). 796*0Sstevel@tonic-gate */ 797*0Sstevel@tonic-gate mii_status = bge_mii_get16(bgep, MII_STATUS); 798*0Sstevel@tonic-gate emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG); 799*0Sstevel@tonic-gate bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status); 800*0Sstevel@tonic-gate 801*0Sstevel@tonic-gate BGE_DEBUG(("bge_check_copper: link %d/%s, MII status 0x%x " 802*0Sstevel@tonic-gate "(was 0x%x), Ethernet MAC status 0x%x", 803*0Sstevel@tonic-gate bgep->link_state, UPORDOWN(bgep->param_link_up), mii_status, 804*0Sstevel@tonic-gate bgep->phy_gen_status, emac_status)); 805*0Sstevel@tonic-gate 806*0Sstevel@tonic-gate /* 807*0Sstevel@tonic-gate * If the PHY status hasn't changed since last we looked, and 808*0Sstevel@tonic-gate * we not forcing a recheck (i.e. the link state was already 809*0Sstevel@tonic-gate * known), there's nothing to do. 810*0Sstevel@tonic-gate */ 811*0Sstevel@tonic-gate if (mii_status == bgep->phy_gen_status && !recheck) 812*0Sstevel@tonic-gate return (B_FALSE); 813*0Sstevel@tonic-gate 814*0Sstevel@tonic-gate do { 815*0Sstevel@tonic-gate /* 816*0Sstevel@tonic-gate * If the PHY status changed, record the time 817*0Sstevel@tonic-gate */ 818*0Sstevel@tonic-gate if (mii_status != bgep->phy_gen_status) 819*0Sstevel@tonic-gate bgep->phys_event_time = gethrtime(); 820*0Sstevel@tonic-gate 821*0Sstevel@tonic-gate /* 822*0Sstevel@tonic-gate * Step 11: read AUX STATUS register to find speed/duplex 823*0Sstevel@tonic-gate */ 824*0Sstevel@tonic-gate aux = bge_mii_get16(bgep, MII_AUX_STATUS); 825*0Sstevel@tonic-gate BGE_CDB(bge_phydump, (bgep, mii_status, aux)); 826*0Sstevel@tonic-gate 827*0Sstevel@tonic-gate /* 828*0Sstevel@tonic-gate * We will only consider the link UP if all the readings 829*0Sstevel@tonic-gate * are consistent and give meaningful results ... 830*0Sstevel@tonic-gate */ 831*0Sstevel@tonic-gate mode = aux & MII_AUX_STATUS_MODE_MASK; 832*0Sstevel@tonic-gate mode >>= MII_AUX_STATUS_MODE_SHIFT; 833*0Sstevel@tonic-gate linkup = bge_copper_link_speed[mode] > 0; 834*0Sstevel@tonic-gate linkup &= bge_copper_link_duplex[mode] != LINK_DUPLEX_UNKNOWN; 835*0Sstevel@tonic-gate linkup &= BIS(aux, MII_AUX_STATUS_LINKUP); 836*0Sstevel@tonic-gate linkup &= BIS(mii_status, MII_STATUS_LINKUP); 837*0Sstevel@tonic-gate 838*0Sstevel@tonic-gate BGE_DEBUG(("bge_check_copper: MII status 0x%x aux 0x%x " 839*0Sstevel@tonic-gate "=> mode %d (%s)", 840*0Sstevel@tonic-gate mii_status, aux, 841*0Sstevel@tonic-gate mode, UPORDOWN(linkup))); 842*0Sstevel@tonic-gate 843*0Sstevel@tonic-gate /* 844*0Sstevel@tonic-gate * Record current register values, then reread status 845*0Sstevel@tonic-gate * register & loop until it stabilises ... 846*0Sstevel@tonic-gate */ 847*0Sstevel@tonic-gate bgep->phy_aux_status = aux; 848*0Sstevel@tonic-gate bgep->phy_gen_status = mii_status; 849*0Sstevel@tonic-gate mii_status = bge_mii_get16(bgep, MII_STATUS); 850*0Sstevel@tonic-gate } while (mii_status != bgep->phy_gen_status); 851*0Sstevel@tonic-gate 852*0Sstevel@tonic-gate /* 853*0Sstevel@tonic-gate * Assume very little ... 854*0Sstevel@tonic-gate */ 855*0Sstevel@tonic-gate bgep->param_lp_autoneg = B_FALSE; 856*0Sstevel@tonic-gate bgep->param_lp_1000fdx = B_FALSE; 857*0Sstevel@tonic-gate bgep->param_lp_1000hdx = B_FALSE; 858*0Sstevel@tonic-gate bgep->param_lp_100fdx = B_FALSE; 859*0Sstevel@tonic-gate bgep->param_lp_100hdx = B_FALSE; 860*0Sstevel@tonic-gate bgep->param_lp_10fdx = B_FALSE; 861*0Sstevel@tonic-gate bgep->param_lp_10hdx = B_FALSE; 862*0Sstevel@tonic-gate bgep->param_lp_pause = B_FALSE; 863*0Sstevel@tonic-gate bgep->param_lp_asym_pause = B_FALSE; 864*0Sstevel@tonic-gate bgep->param_link_autoneg = B_FALSE; 865*0Sstevel@tonic-gate bgep->param_link_tx_pause = B_FALSE; 866*0Sstevel@tonic-gate if (bgep->param_adv_autoneg) 867*0Sstevel@tonic-gate bgep->param_link_rx_pause = B_FALSE; 868*0Sstevel@tonic-gate else 869*0Sstevel@tonic-gate bgep->param_link_rx_pause = bgep->param_adv_pause; 870*0Sstevel@tonic-gate 871*0Sstevel@tonic-gate /* 872*0Sstevel@tonic-gate * Discover all the link partner's abilities. 873*0Sstevel@tonic-gate * These are scattered through various registters ... 874*0Sstevel@tonic-gate */ 875*0Sstevel@tonic-gate if (BIS(aux, MII_AUX_STATUS_LP_ANEG_ABLE)) { 876*0Sstevel@tonic-gate bgep->param_lp_autoneg = B_TRUE; 877*0Sstevel@tonic-gate bgep->param_link_autoneg = B_TRUE; 878*0Sstevel@tonic-gate bgep->param_link_tx_pause = BIS(aux, MII_AUX_STATUS_TX_PAUSE); 879*0Sstevel@tonic-gate bgep->param_link_rx_pause = BIS(aux, MII_AUX_STATUS_RX_PAUSE); 880*0Sstevel@tonic-gate 881*0Sstevel@tonic-gate aux = bge_mii_get16(bgep, MII_1000BASE_T_STATUS); 882*0Sstevel@tonic-gate bgep->param_lp_1000fdx = BIS(aux, MII_1000BT_STAT_LP_FDX_CAP); 883*0Sstevel@tonic-gate bgep->param_lp_1000hdx = BIS(aux, MII_1000BT_STAT_LP_HDX_CAP); 884*0Sstevel@tonic-gate 885*0Sstevel@tonic-gate aux = bge_mii_get16(bgep, MII_AN_LPABLE); 886*0Sstevel@tonic-gate bgep->param_lp_100fdx = BIS(aux, MII_ABILITY_100BASE_TX_FD); 887*0Sstevel@tonic-gate bgep->param_lp_100hdx = BIS(aux, MII_ABILITY_100BASE_TX); 888*0Sstevel@tonic-gate bgep->param_lp_10fdx = BIS(aux, MII_ABILITY_10BASE_T_FD); 889*0Sstevel@tonic-gate bgep->param_lp_10hdx = BIS(aux, MII_ABILITY_10BASE_T); 890*0Sstevel@tonic-gate bgep->param_lp_pause = BIS(aux, MII_ABILITY_PAUSE); 891*0Sstevel@tonic-gate bgep->param_lp_asym_pause = BIS(aux, MII_ABILITY_ASYM_PAUSE); 892*0Sstevel@tonic-gate } 893*0Sstevel@tonic-gate 894*0Sstevel@tonic-gate /* 895*0Sstevel@tonic-gate * Step 12: update ndd-visible state parameters, BUT! 896*0Sstevel@tonic-gate * we don't transfer the new state to <link_state> just yet; 897*0Sstevel@tonic-gate * instead we mark the <link_state> as UNKNOWN, and our caller 898*0Sstevel@tonic-gate * will resolve it once the status has stopped changing and 899*0Sstevel@tonic-gate * been stable for several seconds. 900*0Sstevel@tonic-gate */ 901*0Sstevel@tonic-gate BGE_DEBUG(("bge_check_copper: link was %s speed %d duplex %d", 902*0Sstevel@tonic-gate UPORDOWN(bgep->param_link_up), 903*0Sstevel@tonic-gate bgep->param_link_speed, 904*0Sstevel@tonic-gate bgep->param_link_duplex)); 905*0Sstevel@tonic-gate 906*0Sstevel@tonic-gate if (!linkup) 907*0Sstevel@tonic-gate mode = MII_AUX_STATUS_MODE_NONE; 908*0Sstevel@tonic-gate bgep->param_link_up = linkup; 909*0Sstevel@tonic-gate bgep->param_link_speed = bge_copper_link_speed[mode]; 910*0Sstevel@tonic-gate bgep->param_link_duplex = bge_copper_link_duplex[mode]; 911*0Sstevel@tonic-gate bgep->link_mode_msg = bge_copper_link_text[mode]; 912*0Sstevel@tonic-gate bgep->link_state = LINK_STATE_UNKNOWN; 913*0Sstevel@tonic-gate 914*0Sstevel@tonic-gate BGE_DEBUG(("bge_check_copper: link now %s speed %d duplex %d", 915*0Sstevel@tonic-gate UPORDOWN(bgep->param_link_up), 916*0Sstevel@tonic-gate bgep->param_link_speed, 917*0Sstevel@tonic-gate bgep->param_link_duplex)); 918*0Sstevel@tonic-gate 919*0Sstevel@tonic-gate return (B_TRUE); 920*0Sstevel@tonic-gate } 921*0Sstevel@tonic-gate 922*0Sstevel@tonic-gate static const phys_ops_t copper_ops = { 923*0Sstevel@tonic-gate bge_restart_copper, 924*0Sstevel@tonic-gate bge_update_copper, 925*0Sstevel@tonic-gate bge_check_copper 926*0Sstevel@tonic-gate }; 927*0Sstevel@tonic-gate 928*0Sstevel@tonic-gate 929*0Sstevel@tonic-gate /* 930*0Sstevel@tonic-gate * ========== SerDes support ========== 931*0Sstevel@tonic-gate */ 932*0Sstevel@tonic-gate 933*0Sstevel@tonic-gate #undef BGE_DBG 934*0Sstevel@tonic-gate #define BGE_DBG BGE_DBG_SERDES /* debug flag for this code */ 935*0Sstevel@tonic-gate 936*0Sstevel@tonic-gate /* 937*0Sstevel@tonic-gate * Reinitialise the SerDes interface. Note that it normally powers 938*0Sstevel@tonic-gate * up in the disabled state, so we need to explicitly activate it. 939*0Sstevel@tonic-gate */ 940*0Sstevel@tonic-gate static void 941*0Sstevel@tonic-gate bge_restart_serdes(bge_t *bgep, boolean_t powerdown) 942*0Sstevel@tonic-gate { 943*0Sstevel@tonic-gate uint32_t macmode; 944*0Sstevel@tonic-gate 945*0Sstevel@tonic-gate BGE_TRACE(("bge_restart_serdes($%p, %d)", (void *)bgep, powerdown)); 946*0Sstevel@tonic-gate 947*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 948*0Sstevel@tonic-gate 949*0Sstevel@tonic-gate /* 950*0Sstevel@tonic-gate * Ensure that the main Ethernet MAC mode register is programmed 951*0Sstevel@tonic-gate * appropriately for the SerDes interface ... 952*0Sstevel@tonic-gate */ 953*0Sstevel@tonic-gate macmode = bge_reg_get32(bgep, ETHERNET_MAC_MODE_REG); 954*0Sstevel@tonic-gate macmode &= ~ETHERNET_MODE_LINK_POLARITY; 955*0Sstevel@tonic-gate macmode &= ~ETHERNET_MODE_PORTMODE_MASK; 956*0Sstevel@tonic-gate macmode |= ETHERNET_MODE_PORTMODE_TBI; 957*0Sstevel@tonic-gate bge_reg_put32(bgep, ETHERNET_MAC_MODE_REG, macmode); 958*0Sstevel@tonic-gate 959*0Sstevel@tonic-gate /* 960*0Sstevel@tonic-gate * Ensure that loopback is OFF and comma detection is enabled. Then 961*0Sstevel@tonic-gate * disable the SerDes output (the first time through, it may/will 962*0Sstevel@tonic-gate * already be disabled). If we're shutting down, leave it disabled. 963*0Sstevel@tonic-gate */ 964*0Sstevel@tonic-gate bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TBI_LOOPBACK); 965*0Sstevel@tonic-gate bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_COMMA_DETECT); 966*0Sstevel@tonic-gate bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE); 967*0Sstevel@tonic-gate if (powerdown) 968*0Sstevel@tonic-gate return; 969*0Sstevel@tonic-gate 970*0Sstevel@tonic-gate /* 971*0Sstevel@tonic-gate * Otherwise, pause, (re-)enable the SerDes output, and send 972*0Sstevel@tonic-gate * all-zero config words in order to force autoneg restart. 973*0Sstevel@tonic-gate * Invalidate the saved "link partners received configs", as 974*0Sstevel@tonic-gate * we're starting over ... 975*0Sstevel@tonic-gate */ 976*0Sstevel@tonic-gate drv_usecwait(10000); 977*0Sstevel@tonic-gate bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE); 978*0Sstevel@tonic-gate bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG, 0); 979*0Sstevel@tonic-gate bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS); 980*0Sstevel@tonic-gate drv_usecwait(10); 981*0Sstevel@tonic-gate bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS); 982*0Sstevel@tonic-gate bgep->serdes_lpadv = AUTONEG_CODE_FAULT_ANEG_ERR; 983*0Sstevel@tonic-gate bgep->serdes_status = ~0U; 984*0Sstevel@tonic-gate } 985*0Sstevel@tonic-gate 986*0Sstevel@tonic-gate /* 987*0Sstevel@tonic-gate * Synchronise the SerDes speed/duplex/autonegotiation capabilities and 988*0Sstevel@tonic-gate * advertisements with the required settings as specified by the various 989*0Sstevel@tonic-gate * param_* variables that can be poked via the NDD interface. 990*0Sstevel@tonic-gate * 991*0Sstevel@tonic-gate * We always reinitalise the SerDes; this should cause the link to go down, 992*0Sstevel@tonic-gate * and then back up again once the link is stable and autonegotiation 993*0Sstevel@tonic-gate * (if enabled) is complete. We should get a link state change interrupt 994*0Sstevel@tonic-gate * somewhere along the way ... 995*0Sstevel@tonic-gate * 996*0Sstevel@tonic-gate * NOTE: SerDes only supports 1000FDX/HDX (with or without pause) so the 997*0Sstevel@tonic-gate * param_* variables relating to lower speeds are ignored. 998*0Sstevel@tonic-gate * 999*0Sstevel@tonic-gate * NOTE: <genlock> must already be held by the caller 1000*0Sstevel@tonic-gate */ 1001*0Sstevel@tonic-gate static void 1002*0Sstevel@tonic-gate bge_update_serdes(bge_t *bgep) 1003*0Sstevel@tonic-gate { 1004*0Sstevel@tonic-gate boolean_t adv_autoneg; 1005*0Sstevel@tonic-gate boolean_t adv_pause; 1006*0Sstevel@tonic-gate boolean_t adv_asym_pause; 1007*0Sstevel@tonic-gate boolean_t adv_1000fdx; 1008*0Sstevel@tonic-gate boolean_t adv_1000hdx; 1009*0Sstevel@tonic-gate 1010*0Sstevel@tonic-gate uint32_t serdes; 1011*0Sstevel@tonic-gate uint32_t advert; 1012*0Sstevel@tonic-gate 1013*0Sstevel@tonic-gate BGE_TRACE(("bge_update_serdes($%p)", (void *)bgep)); 1014*0Sstevel@tonic-gate 1015*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 1016*0Sstevel@tonic-gate 1017*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_serdes: autoneg %d " 1018*0Sstevel@tonic-gate "pause %d asym_pause %d " 1019*0Sstevel@tonic-gate "1000fdx %d 1000hdx %d " 1020*0Sstevel@tonic-gate "100fdx %d 100hdx %d " 1021*0Sstevel@tonic-gate "10fdx %d 10hdx %d ", 1022*0Sstevel@tonic-gate bgep->param_adv_autoneg, 1023*0Sstevel@tonic-gate bgep->param_adv_pause, bgep->param_adv_asym_pause, 1024*0Sstevel@tonic-gate bgep->param_adv_1000fdx, bgep->param_adv_1000hdx, 1025*0Sstevel@tonic-gate bgep->param_adv_100fdx, bgep->param_adv_100hdx, 1026*0Sstevel@tonic-gate bgep->param_adv_10fdx, bgep->param_adv_10hdx)); 1027*0Sstevel@tonic-gate 1028*0Sstevel@tonic-gate serdes = advert = 0; 1029*0Sstevel@tonic-gate 1030*0Sstevel@tonic-gate /* 1031*0Sstevel@tonic-gate * SerDes settings are normally based on the param_* variables, 1032*0Sstevel@tonic-gate * but if any loopback mode is in effect, that takes precedence. 1033*0Sstevel@tonic-gate * 1034*0Sstevel@tonic-gate * BGE supports MAC-internal loopback, PHY-internal loopback, 1035*0Sstevel@tonic-gate * and External loopback at a variety of speeds (with a special 1036*0Sstevel@tonic-gate * cable). In all cases, autoneg is turned OFF, full-duplex 1037*0Sstevel@tonic-gate * is turned ON, and the speed/mastership is forced. 1038*0Sstevel@tonic-gate * 1039*0Sstevel@tonic-gate * Note: for the SerDes interface, "PHY" internal loopback is 1040*0Sstevel@tonic-gate * interpreted as SerDes internal loopback, and all external 1041*0Sstevel@tonic-gate * loopback modes are treated equivalently, as 1Gb/external. 1042*0Sstevel@tonic-gate */ 1043*0Sstevel@tonic-gate switch (bgep->param_loop_mode) { 1044*0Sstevel@tonic-gate case BGE_LOOP_NONE: 1045*0Sstevel@tonic-gate default: 1046*0Sstevel@tonic-gate adv_autoneg = bgep->param_adv_autoneg; 1047*0Sstevel@tonic-gate adv_pause = bgep->param_adv_pause; 1048*0Sstevel@tonic-gate adv_asym_pause = bgep->param_adv_asym_pause; 1049*0Sstevel@tonic-gate adv_1000fdx = bgep->param_adv_1000fdx; 1050*0Sstevel@tonic-gate adv_1000hdx = bgep->param_adv_1000hdx; 1051*0Sstevel@tonic-gate break; 1052*0Sstevel@tonic-gate 1053*0Sstevel@tonic-gate case BGE_LOOP_INTERNAL_PHY: 1054*0Sstevel@tonic-gate serdes |= SERDES_CONTROL_TBI_LOOPBACK; 1055*0Sstevel@tonic-gate /* FALLTHRU */ 1056*0Sstevel@tonic-gate case BGE_LOOP_INTERNAL_MAC: 1057*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_1000: 1058*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_100: 1059*0Sstevel@tonic-gate case BGE_LOOP_EXTERNAL_10: 1060*0Sstevel@tonic-gate adv_autoneg = adv_pause = adv_asym_pause = B_FALSE; 1061*0Sstevel@tonic-gate adv_1000fdx = B_TRUE; 1062*0Sstevel@tonic-gate adv_1000hdx = B_FALSE; 1063*0Sstevel@tonic-gate break; 1064*0Sstevel@tonic-gate } 1065*0Sstevel@tonic-gate 1066*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_serdes: autoneg %d " 1067*0Sstevel@tonic-gate "pause %d asym_pause %d " 1068*0Sstevel@tonic-gate "1000fdx %d 1000hdx %d ", 1069*0Sstevel@tonic-gate adv_autoneg, 1070*0Sstevel@tonic-gate adv_pause, adv_asym_pause, 1071*0Sstevel@tonic-gate adv_1000fdx, adv_1000hdx)); 1072*0Sstevel@tonic-gate 1073*0Sstevel@tonic-gate /* 1074*0Sstevel@tonic-gate * We should have at least one gigabit technology capability 1075*0Sstevel@tonic-gate * set; if not, we select a default of 1000Mb/s full-duplex 1076*0Sstevel@tonic-gate */ 1077*0Sstevel@tonic-gate if (!adv_1000fdx && !adv_1000hdx) 1078*0Sstevel@tonic-gate adv_1000fdx = B_TRUE; 1079*0Sstevel@tonic-gate 1080*0Sstevel@tonic-gate /* 1081*0Sstevel@tonic-gate * Now transform the adv_* variables into the proper settings 1082*0Sstevel@tonic-gate * of the SerDes registers ... 1083*0Sstevel@tonic-gate * 1084*0Sstevel@tonic-gate * If autonegotiation is (now) not enabled, pretend it's been 1085*0Sstevel@tonic-gate * done and failed ... 1086*0Sstevel@tonic-gate */ 1087*0Sstevel@tonic-gate if (!adv_autoneg) 1088*0Sstevel@tonic-gate advert |= AUTONEG_CODE_FAULT_ANEG_ERR; 1089*0Sstevel@tonic-gate 1090*0Sstevel@tonic-gate if (adv_1000fdx) { 1091*0Sstevel@tonic-gate advert |= AUTONEG_CODE_FULL_DUPLEX; 1092*0Sstevel@tonic-gate bgep->param_adv_1000fdx = adv_1000fdx; 1093*0Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_FULL; 1094*0Sstevel@tonic-gate bgep->param_link_speed = 1000; 1095*0Sstevel@tonic-gate } 1096*0Sstevel@tonic-gate if (adv_1000hdx) { 1097*0Sstevel@tonic-gate advert |= AUTONEG_CODE_HALF_DUPLEX; 1098*0Sstevel@tonic-gate bgep->param_adv_1000hdx = adv_1000hdx; 1099*0Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_HALF; 1100*0Sstevel@tonic-gate bgep->param_link_speed = 1000; 1101*0Sstevel@tonic-gate } 1102*0Sstevel@tonic-gate 1103*0Sstevel@tonic-gate if (adv_pause) 1104*0Sstevel@tonic-gate advert |= AUTONEG_CODE_PAUSE; 1105*0Sstevel@tonic-gate if (adv_asym_pause) 1106*0Sstevel@tonic-gate advert |= AUTONEG_CODE_ASYM_PAUSE; 1107*0Sstevel@tonic-gate 1108*0Sstevel@tonic-gate /* 1109*0Sstevel@tonic-gate * Restart the SerDes and write the new values. Note the 1110*0Sstevel@tonic-gate * time, so that we can say whether subsequent link state 1111*0Sstevel@tonic-gate * changes can be attributed to our reprogramming the SerDes 1112*0Sstevel@tonic-gate */ 1113*0Sstevel@tonic-gate bgep->serdes_advert = advert; 1114*0Sstevel@tonic-gate bgep->phys_write_time = gethrtime(); 1115*0Sstevel@tonic-gate bge_restart_serdes(bgep, B_FALSE); 1116*0Sstevel@tonic-gate bge_reg_set32(bgep, SERDES_CONTROL_REG, serdes); 1117*0Sstevel@tonic-gate 1118*0Sstevel@tonic-gate BGE_DEBUG(("bge_update_serdes: serdes |= 0x%x, advert 0x%x", 1119*0Sstevel@tonic-gate serdes, advert)); 1120*0Sstevel@tonic-gate } 1121*0Sstevel@tonic-gate 1122*0Sstevel@tonic-gate /* 1123*0Sstevel@tonic-gate * Bare-minimum autoneg protocol 1124*0Sstevel@tonic-gate * 1125*0Sstevel@tonic-gate * This code is only called when the link is up and we're receiving config 1126*0Sstevel@tonic-gate * words, which implies that the link partner wants to autonegotiate 1127*0Sstevel@tonic-gate * (otherwise, we wouldn't see configs and wouldn't reach this code). 1128*0Sstevel@tonic-gate */ 1129*0Sstevel@tonic-gate static void 1130*0Sstevel@tonic-gate bge_autoneg_serdes(bge_t *bgep) 1131*0Sstevel@tonic-gate { 1132*0Sstevel@tonic-gate boolean_t ack; 1133*0Sstevel@tonic-gate 1134*0Sstevel@tonic-gate bgep->serdes_lpadv = bge_reg_get32(bgep, RX_1000BASEX_AUTONEG_REG); 1135*0Sstevel@tonic-gate ack = BIS(bgep->serdes_lpadv, AUTONEG_CODE_ACKNOWLEDGE); 1136*0Sstevel@tonic-gate 1137*0Sstevel@tonic-gate if (!ack) { 1138*0Sstevel@tonic-gate /* 1139*0Sstevel@tonic-gate * Phase 1: after SerDes reset, we send a few zero configs 1140*0Sstevel@tonic-gate * but then stop. Here the partner is sending configs, but 1141*0Sstevel@tonic-gate * not ACKing ours; we assume that's 'cos we're not sending 1142*0Sstevel@tonic-gate * any. So here we send ours, with ACK already set. 1143*0Sstevel@tonic-gate */ 1144*0Sstevel@tonic-gate bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG, 1145*0Sstevel@tonic-gate bgep->serdes_advert | AUTONEG_CODE_ACKNOWLEDGE); 1146*0Sstevel@tonic-gate bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG, 1147*0Sstevel@tonic-gate ETHERNET_MODE_SEND_CFGS); 1148*0Sstevel@tonic-gate } else { 1149*0Sstevel@tonic-gate /* 1150*0Sstevel@tonic-gate * Phase 2: partner has ACKed our configs, so now we can 1151*0Sstevel@tonic-gate * stop sending; once our partner also stops sending, we 1152*0Sstevel@tonic-gate * can resolve the Tx/Rx configs. 1153*0Sstevel@tonic-gate */ 1154*0Sstevel@tonic-gate bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG, 1155*0Sstevel@tonic-gate ETHERNET_MODE_SEND_CFGS); 1156*0Sstevel@tonic-gate } 1157*0Sstevel@tonic-gate 1158*0Sstevel@tonic-gate BGE_DEBUG(("bge_autoneg_serdes: Rx 0x%x %s Tx 0x%x", 1159*0Sstevel@tonic-gate bgep->serdes_lpadv, 1160*0Sstevel@tonic-gate ack ? "stop" : "send", 1161*0Sstevel@tonic-gate bgep->serdes_advert)); 1162*0Sstevel@tonic-gate } 1163*0Sstevel@tonic-gate 1164*0Sstevel@tonic-gate static boolean_t 1165*0Sstevel@tonic-gate bge_check_serdes(bge_t *bgep, boolean_t recheck) 1166*0Sstevel@tonic-gate { 1167*0Sstevel@tonic-gate uint32_t emac_status; 1168*0Sstevel@tonic-gate uint32_t lpadv; 1169*0Sstevel@tonic-gate boolean_t linkup; 1170*0Sstevel@tonic-gate 1171*0Sstevel@tonic-gate for (;;) { 1172*0Sstevel@tonic-gate /* 1173*0Sstevel@tonic-gate * Step 10: read & clear the main (Ethernet) MAC status 1174*0Sstevel@tonic-gate * (the relevant bits of this are write-one-to-clear). 1175*0Sstevel@tonic-gate */ 1176*0Sstevel@tonic-gate emac_status = bge_reg_get32(bgep, ETHERNET_MAC_STATUS_REG); 1177*0Sstevel@tonic-gate bge_reg_put32(bgep, ETHERNET_MAC_STATUS_REG, emac_status); 1178*0Sstevel@tonic-gate 1179*0Sstevel@tonic-gate BGE_DEBUG(("bge_check_serdes: link %d/%s, " 1180*0Sstevel@tonic-gate "MAC status 0x%x (was 0x%x)", 1181*0Sstevel@tonic-gate bgep->link_state, UPORDOWN(bgep->param_link_up), 1182*0Sstevel@tonic-gate emac_status, bgep->serdes_status)); 1183*0Sstevel@tonic-gate 1184*0Sstevel@tonic-gate /* 1185*0Sstevel@tonic-gate * We will only consider the link UP if all the readings 1186*0Sstevel@tonic-gate * are consistent and give meaningful results ... 1187*0Sstevel@tonic-gate */ 1188*0Sstevel@tonic-gate bgep->serdes_status = emac_status; 1189*0Sstevel@tonic-gate linkup = BIS(emac_status, ETHERNET_STATUS_SIGNAL_DETECT); 1190*0Sstevel@tonic-gate linkup &= BIS(emac_status, ETHERNET_STATUS_PCS_SYNCHED); 1191*0Sstevel@tonic-gate 1192*0Sstevel@tonic-gate /* 1193*0Sstevel@tonic-gate * Now some fiddling with the interpretation: 1194*0Sstevel@tonic-gate * if there's been an error at the PCS level, treat 1195*0Sstevel@tonic-gate * it as a link change (the h/w doesn't do this) 1196*0Sstevel@tonic-gate * 1197*0Sstevel@tonic-gate * if there's been a change, but it's only a PCS sync 1198*0Sstevel@tonic-gate * change (not a config change), AND the link already 1199*0Sstevel@tonic-gate * was & is still UP, then ignore the change 1200*0Sstevel@tonic-gate */ 1201*0Sstevel@tonic-gate if (BIS(emac_status, ETHERNET_STATUS_PCS_ERROR)) 1202*0Sstevel@tonic-gate emac_status |= ETHERNET_STATUS_LINK_CHANGED; 1203*0Sstevel@tonic-gate else if (BIC(emac_status, ETHERNET_STATUS_CFG_CHANGED)) 1204*0Sstevel@tonic-gate if (bgep->param_link_up && linkup) 1205*0Sstevel@tonic-gate emac_status &= ~ETHERNET_STATUS_LINK_CHANGED; 1206*0Sstevel@tonic-gate 1207*0Sstevel@tonic-gate BGE_DEBUG(("bge_check_serdes: status 0x%x => 0x%x %s", 1208*0Sstevel@tonic-gate bgep->serdes_status, emac_status, UPORDOWN(linkup))); 1209*0Sstevel@tonic-gate 1210*0Sstevel@tonic-gate /* 1211*0Sstevel@tonic-gate * If we're receiving configs, run the autoneg protocol 1212*0Sstevel@tonic-gate */ 1213*0Sstevel@tonic-gate if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG)) 1214*0Sstevel@tonic-gate bge_autoneg_serdes(bgep); 1215*0Sstevel@tonic-gate 1216*0Sstevel@tonic-gate /* 1217*0Sstevel@tonic-gate * If the SerDes status hasn't changed, we're done ... 1218*0Sstevel@tonic-gate */ 1219*0Sstevel@tonic-gate if (BIC(emac_status, ETHERNET_STATUS_LINK_CHANGED)) 1220*0Sstevel@tonic-gate break; 1221*0Sstevel@tonic-gate 1222*0Sstevel@tonic-gate /* 1223*0Sstevel@tonic-gate * Record when the SerDes status changed, then go 1224*0Sstevel@tonic-gate * round again until we no longer see a change ... 1225*0Sstevel@tonic-gate */ 1226*0Sstevel@tonic-gate bgep->phys_event_time = gethrtime(); 1227*0Sstevel@tonic-gate recheck = B_TRUE; 1228*0Sstevel@tonic-gate } 1229*0Sstevel@tonic-gate 1230*0Sstevel@tonic-gate /* 1231*0Sstevel@tonic-gate * If we're not forcing a recheck (i.e. the link state was already 1232*0Sstevel@tonic-gate * known), and we didn't see the hardware flag a change, there's 1233*0Sstevel@tonic-gate * no more to do (and we tell the caller nothing happened). 1234*0Sstevel@tonic-gate */ 1235*0Sstevel@tonic-gate if (!recheck) 1236*0Sstevel@tonic-gate return (B_FALSE); 1237*0Sstevel@tonic-gate 1238*0Sstevel@tonic-gate /* 1239*0Sstevel@tonic-gate * Don't resolve autoneg until we're no longer receiving configs 1240*0Sstevel@tonic-gate */ 1241*0Sstevel@tonic-gate if (linkup && BIS(emac_status, ETHERNET_STATUS_RECEIVING_CFG)) 1242*0Sstevel@tonic-gate return (B_FALSE); 1243*0Sstevel@tonic-gate 1244*0Sstevel@tonic-gate /* 1245*0Sstevel@tonic-gate * Assume very little ... 1246*0Sstevel@tonic-gate */ 1247*0Sstevel@tonic-gate bgep->param_lp_autoneg = B_FALSE; 1248*0Sstevel@tonic-gate bgep->param_lp_1000fdx = B_FALSE; 1249*0Sstevel@tonic-gate bgep->param_lp_1000hdx = B_FALSE; 1250*0Sstevel@tonic-gate bgep->param_lp_100fdx = B_FALSE; 1251*0Sstevel@tonic-gate bgep->param_lp_100hdx = B_FALSE; 1252*0Sstevel@tonic-gate bgep->param_lp_10fdx = B_FALSE; 1253*0Sstevel@tonic-gate bgep->param_lp_10hdx = B_FALSE; 1254*0Sstevel@tonic-gate bgep->param_lp_pause = B_FALSE; 1255*0Sstevel@tonic-gate bgep->param_lp_asym_pause = B_FALSE; 1256*0Sstevel@tonic-gate bgep->param_link_autoneg = B_FALSE; 1257*0Sstevel@tonic-gate bgep->param_link_tx_pause = B_FALSE; 1258*0Sstevel@tonic-gate if (bgep->param_adv_autoneg) 1259*0Sstevel@tonic-gate bgep->param_link_rx_pause = B_FALSE; 1260*0Sstevel@tonic-gate else 1261*0Sstevel@tonic-gate bgep->param_link_rx_pause = bgep->param_adv_pause; 1262*0Sstevel@tonic-gate 1263*0Sstevel@tonic-gate /* 1264*0Sstevel@tonic-gate * Discover all the link partner's abilities. 1265*0Sstevel@tonic-gate */ 1266*0Sstevel@tonic-gate lpadv = bgep->serdes_lpadv; 1267*0Sstevel@tonic-gate if (lpadv != 0 && BIC(lpadv, AUTONEG_CODE_FAULT_MASK)) { 1268*0Sstevel@tonic-gate /* 1269*0Sstevel@tonic-gate * No fault, so derive partner's capabilities 1270*0Sstevel@tonic-gate */ 1271*0Sstevel@tonic-gate bgep->param_lp_autoneg = B_TRUE; 1272*0Sstevel@tonic-gate bgep->param_lp_1000fdx = BIS(lpadv, AUTONEG_CODE_FULL_DUPLEX); 1273*0Sstevel@tonic-gate bgep->param_lp_1000hdx = BIS(lpadv, AUTONEG_CODE_HALF_DUPLEX); 1274*0Sstevel@tonic-gate bgep->param_lp_pause = BIS(lpadv, AUTONEG_CODE_PAUSE); 1275*0Sstevel@tonic-gate bgep->param_lp_asym_pause = BIS(lpadv, AUTONEG_CODE_ASYM_PAUSE); 1276*0Sstevel@tonic-gate 1277*0Sstevel@tonic-gate /* 1278*0Sstevel@tonic-gate * Pause direction resolution 1279*0Sstevel@tonic-gate */ 1280*0Sstevel@tonic-gate bgep->param_link_autoneg = B_TRUE; 1281*0Sstevel@tonic-gate if (bgep->param_adv_pause && 1282*0Sstevel@tonic-gate bgep->param_lp_pause) { 1283*0Sstevel@tonic-gate bgep->param_link_tx_pause = B_TRUE; 1284*0Sstevel@tonic-gate bgep->param_link_rx_pause = B_TRUE; 1285*0Sstevel@tonic-gate } 1286*0Sstevel@tonic-gate if (bgep->param_adv_asym_pause && 1287*0Sstevel@tonic-gate bgep->param_lp_asym_pause) { 1288*0Sstevel@tonic-gate if (bgep->param_adv_pause) 1289*0Sstevel@tonic-gate bgep->param_link_rx_pause = B_TRUE; 1290*0Sstevel@tonic-gate if (bgep->param_lp_pause) 1291*0Sstevel@tonic-gate bgep->param_link_tx_pause = B_TRUE; 1292*0Sstevel@tonic-gate } 1293*0Sstevel@tonic-gate } 1294*0Sstevel@tonic-gate 1295*0Sstevel@tonic-gate /* 1296*0Sstevel@tonic-gate * Step 12: update ndd-visible state parameters, BUT! 1297*0Sstevel@tonic-gate * we don't transfer the new state to <link_state> just yet; 1298*0Sstevel@tonic-gate * instead we mark the <link_state> as UNKNOWN, and our caller 1299*0Sstevel@tonic-gate * will resolve it once the status has stopped changing and 1300*0Sstevel@tonic-gate * been stable for several seconds. 1301*0Sstevel@tonic-gate */ 1302*0Sstevel@tonic-gate BGE_DEBUG(("bge_check_serdes: link was %s speed %d duplex %d", 1303*0Sstevel@tonic-gate UPORDOWN(bgep->param_link_up), 1304*0Sstevel@tonic-gate bgep->param_link_speed, 1305*0Sstevel@tonic-gate bgep->param_link_duplex)); 1306*0Sstevel@tonic-gate 1307*0Sstevel@tonic-gate if (linkup) { 1308*0Sstevel@tonic-gate bgep->param_link_up = B_TRUE; 1309*0Sstevel@tonic-gate bgep->param_link_speed = 1000; 1310*0Sstevel@tonic-gate if (bgep->param_adv_1000fdx) 1311*0Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_FULL; 1312*0Sstevel@tonic-gate else 1313*0Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_HALF; 1314*0Sstevel@tonic-gate if (bgep->param_lp_autoneg && !bgep->param_lp_1000fdx) 1315*0Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_HALF; 1316*0Sstevel@tonic-gate } else { 1317*0Sstevel@tonic-gate bgep->param_link_up = B_FALSE; 1318*0Sstevel@tonic-gate bgep->param_link_speed = 0; 1319*0Sstevel@tonic-gate bgep->param_link_duplex = LINK_DUPLEX_UNKNOWN; 1320*0Sstevel@tonic-gate } 1321*0Sstevel@tonic-gate switch (bgep->param_link_duplex) { 1322*0Sstevel@tonic-gate default: 1323*0Sstevel@tonic-gate case LINK_DUPLEX_UNKNOWN: 1324*0Sstevel@tonic-gate bgep->link_mode_msg = "down"; 1325*0Sstevel@tonic-gate break; 1326*0Sstevel@tonic-gate 1327*0Sstevel@tonic-gate case LINK_DUPLEX_HALF: 1328*0Sstevel@tonic-gate bgep->link_mode_msg = "up 1000Mbps Half-Duplex"; 1329*0Sstevel@tonic-gate break; 1330*0Sstevel@tonic-gate 1331*0Sstevel@tonic-gate case LINK_DUPLEX_FULL: 1332*0Sstevel@tonic-gate bgep->link_mode_msg = "up 1000Mbps Full-Duplex"; 1333*0Sstevel@tonic-gate break; 1334*0Sstevel@tonic-gate } 1335*0Sstevel@tonic-gate bgep->link_state = LINK_STATE_UNKNOWN; 1336*0Sstevel@tonic-gate 1337*0Sstevel@tonic-gate BGE_DEBUG(("bge_check_serdes: link now %s speed %d duplex %d", 1338*0Sstevel@tonic-gate UPORDOWN(bgep->param_link_up), 1339*0Sstevel@tonic-gate bgep->param_link_speed, 1340*0Sstevel@tonic-gate bgep->param_link_duplex)); 1341*0Sstevel@tonic-gate 1342*0Sstevel@tonic-gate return (B_TRUE); 1343*0Sstevel@tonic-gate } 1344*0Sstevel@tonic-gate 1345*0Sstevel@tonic-gate static const phys_ops_t serdes_ops = { 1346*0Sstevel@tonic-gate bge_restart_serdes, 1347*0Sstevel@tonic-gate bge_update_serdes, 1348*0Sstevel@tonic-gate bge_check_serdes 1349*0Sstevel@tonic-gate }; 1350*0Sstevel@tonic-gate 1351*0Sstevel@tonic-gate /* 1352*0Sstevel@tonic-gate * ========== Exported physical layer control routines ========== 1353*0Sstevel@tonic-gate */ 1354*0Sstevel@tonic-gate 1355*0Sstevel@tonic-gate #undef BGE_DBG 1356*0Sstevel@tonic-gate #define BGE_DBG BGE_DBG_PHYS /* debug flag for this code */ 1357*0Sstevel@tonic-gate 1358*0Sstevel@tonic-gate /* 1359*0Sstevel@tonic-gate * Here we have to determine which media we're using (copper or serdes). 1360*0Sstevel@tonic-gate * Once that's done, we can initialise the physical layer appropriately. 1361*0Sstevel@tonic-gate */ 1362*0Sstevel@tonic-gate void 1363*0Sstevel@tonic-gate bge_phys_init(bge_t *bgep) 1364*0Sstevel@tonic-gate { 1365*0Sstevel@tonic-gate BGE_TRACE(("bge_phys_init($%p)", (void *)bgep)); 1366*0Sstevel@tonic-gate 1367*0Sstevel@tonic-gate mutex_enter(bgep->genlock); 1368*0Sstevel@tonic-gate 1369*0Sstevel@tonic-gate /* 1370*0Sstevel@tonic-gate * Probe for the (internal) PHY. If it's not there, we'll assume 1371*0Sstevel@tonic-gate * that this is a 5703/4S, with a SerDes interface rather than a PHY. 1372*0Sstevel@tonic-gate */ 1373*0Sstevel@tonic-gate bgep->phy_mii_addr = 1; 1374*0Sstevel@tonic-gate if (bge_phy_probe(bgep)) { 1375*0Sstevel@tonic-gate bgep->chipid.flags &= ~CHIP_FLAG_SERDES; 1376*0Sstevel@tonic-gate bgep->phys_delta_time = BGE_PHY_STABLE_TIME; 1377*0Sstevel@tonic-gate bgep->physops = &copper_ops; 1378*0Sstevel@tonic-gate } else { 1379*0Sstevel@tonic-gate bgep->chipid.flags |= CHIP_FLAG_SERDES; 1380*0Sstevel@tonic-gate bgep->phys_delta_time = BGE_SERDES_STABLE_TIME; 1381*0Sstevel@tonic-gate bgep->physops = &serdes_ops; 1382*0Sstevel@tonic-gate } 1383*0Sstevel@tonic-gate 1384*0Sstevel@tonic-gate (*bgep->physops->phys_restart)(bgep, B_FALSE); 1385*0Sstevel@tonic-gate mutex_exit(bgep->genlock); 1386*0Sstevel@tonic-gate } 1387*0Sstevel@tonic-gate 1388*0Sstevel@tonic-gate /* 1389*0Sstevel@tonic-gate * Reset the physical layer 1390*0Sstevel@tonic-gate */ 1391*0Sstevel@tonic-gate void 1392*0Sstevel@tonic-gate bge_phys_reset(bge_t *bgep) 1393*0Sstevel@tonic-gate { 1394*0Sstevel@tonic-gate BGE_TRACE(("bge_phys_reset($%p)", (void *)bgep)); 1395*0Sstevel@tonic-gate 1396*0Sstevel@tonic-gate mutex_enter(bgep->genlock); 1397*0Sstevel@tonic-gate (*bgep->physops->phys_restart)(bgep, B_FALSE); 1398*0Sstevel@tonic-gate mutex_exit(bgep->genlock); 1399*0Sstevel@tonic-gate } 1400*0Sstevel@tonic-gate 1401*0Sstevel@tonic-gate /* 1402*0Sstevel@tonic-gate * Reset and power off the physical layer. 1403*0Sstevel@tonic-gate * 1404*0Sstevel@tonic-gate * Another RESET should get it back to working, but it may take a few 1405*0Sstevel@tonic-gate * seconds it may take a few moments to return to normal operation ... 1406*0Sstevel@tonic-gate */ 1407*0Sstevel@tonic-gate void 1408*0Sstevel@tonic-gate bge_phys_idle(bge_t *bgep) 1409*0Sstevel@tonic-gate { 1410*0Sstevel@tonic-gate BGE_TRACE(("bge_phys_idle($%p)", (void *)bgep)); 1411*0Sstevel@tonic-gate 1412*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 1413*0Sstevel@tonic-gate (*bgep->physops->phys_restart)(bgep, B_TRUE); 1414*0Sstevel@tonic-gate } 1415*0Sstevel@tonic-gate 1416*0Sstevel@tonic-gate /* 1417*0Sstevel@tonic-gate * Synchronise the PHYSICAL layer's speed/duplex/autonegotiation capabilities 1418*0Sstevel@tonic-gate * and advertisements with the required settings as specified by the various 1419*0Sstevel@tonic-gate * param_* variables that can be poked via the NDD interface. 1420*0Sstevel@tonic-gate * 1421*0Sstevel@tonic-gate * We always reset the PHYSICAL layer and reprogram *all* relevant registers. 1422*0Sstevel@tonic-gate * This is expected to cause the link to go down, and then back up again once 1423*0Sstevel@tonic-gate * the link is stable and autonegotiation (if enabled) is complete. We should 1424*0Sstevel@tonic-gate * get a link state change interrupt somewhere along the way ... 1425*0Sstevel@tonic-gate * 1426*0Sstevel@tonic-gate * NOTE: <genlock> must already be held by the caller 1427*0Sstevel@tonic-gate */ 1428*0Sstevel@tonic-gate void 1429*0Sstevel@tonic-gate bge_phys_update(bge_t *bgep) 1430*0Sstevel@tonic-gate { 1431*0Sstevel@tonic-gate BGE_TRACE(("bge_phys_update($%p)", (void *)bgep)); 1432*0Sstevel@tonic-gate 1433*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 1434*0Sstevel@tonic-gate (*bgep->physops->phys_update)(bgep); 1435*0Sstevel@tonic-gate } 1436*0Sstevel@tonic-gate 1437*0Sstevel@tonic-gate #undef BGE_DBG 1438*0Sstevel@tonic-gate #define BGE_DBG BGE_DBG_LINK /* debug flag for this code */ 1439*0Sstevel@tonic-gate 1440*0Sstevel@tonic-gate /* 1441*0Sstevel@tonic-gate * Read the link status and determine whether anything's changed ... 1442*0Sstevel@tonic-gate * 1443*0Sstevel@tonic-gate * This routine should be called whenever the chip flags a change 1444*0Sstevel@tonic-gate * in the hardware link state, and repeatedly for several seconds 1445*0Sstevel@tonic-gate * afterwards, until we're sure the state has stabilised (sometimes 1446*0Sstevel@tonic-gate * it goes up and down several times during autonegotiation before 1447*0Sstevel@tonic-gate * settling on the proper configuration). This routine applies 1448*0Sstevel@tonic-gate * timing-based heuristics to determine when the state is stable. 1449*0Sstevel@tonic-gate * 1450*0Sstevel@tonic-gate * This routine returns B_FALSE if the link state has not changed, 1451*0Sstevel@tonic-gate * or if it has changed, but hasn't settled for long enough yet. It 1452*0Sstevel@tonic-gate * returns B_TRUE when the change to the new state should be accepted. 1453*0Sstevel@tonic-gate * In such a case, the param_* variables give the new hardware state, 1454*0Sstevel@tonic-gate * which the caller should use to update link_state etc. 1455*0Sstevel@tonic-gate * 1456*0Sstevel@tonic-gate * The caller must already hold <genlock> 1457*0Sstevel@tonic-gate */ 1458*0Sstevel@tonic-gate boolean_t 1459*0Sstevel@tonic-gate bge_phys_check(bge_t *bgep) 1460*0Sstevel@tonic-gate { 1461*0Sstevel@tonic-gate int32_t orig_state; 1462*0Sstevel@tonic-gate boolean_t recheck; 1463*0Sstevel@tonic-gate boolean_t linkup; 1464*0Sstevel@tonic-gate hrtime_t deltat; 1465*0Sstevel@tonic-gate hrtime_t now; 1466*0Sstevel@tonic-gate 1467*0Sstevel@tonic-gate BGE_TRACE(("bge_phys_check($%p)", (void *)bgep)); 1468*0Sstevel@tonic-gate 1469*0Sstevel@tonic-gate ASSERT(mutex_owned(bgep->genlock)); 1470*0Sstevel@tonic-gate 1471*0Sstevel@tonic-gate linkup = bgep->param_link_up; 1472*0Sstevel@tonic-gate orig_state = bgep->link_state; 1473*0Sstevel@tonic-gate recheck = orig_state == LINK_STATE_UNKNOWN; 1474*0Sstevel@tonic-gate recheck = (*bgep->physops->phys_check)(bgep, recheck); 1475*0Sstevel@tonic-gate if (!recheck) 1476*0Sstevel@tonic-gate return (B_FALSE); 1477*0Sstevel@tonic-gate 1478*0Sstevel@tonic-gate /* 1479*0Sstevel@tonic-gate * At this point, the check_*_link() function above has detected 1480*0Sstevel@tonic-gate * a change and updated the param_* variables to show what the 1481*0Sstevel@tonic-gate * latest hardware state seems to be -- but it might still be 1482*0Sstevel@tonic-gate * changing. 1483*0Sstevel@tonic-gate * 1484*0Sstevel@tonic-gate * The link_state must now be UNKNOWN, but if it was previously 1485*0Sstevel@tonic-gate * UP, we want to recognise this immediately, whereas in any other 1486*0Sstevel@tonic-gate * case (e.g. DOWN->UP) we don't accept it until a few seconds have 1487*0Sstevel@tonic-gate * elapsed, to give the hardware time to settle. 1488*0Sstevel@tonic-gate */ 1489*0Sstevel@tonic-gate now = gethrtime(); 1490*0Sstevel@tonic-gate deltat = now - bgep->phys_event_time; 1491*0Sstevel@tonic-gate 1492*0Sstevel@tonic-gate BGE_DEBUG(("bge_phys_check: link was %d/%s now %d/%s", 1493*0Sstevel@tonic-gate orig_state, UPORDOWN(linkup), 1494*0Sstevel@tonic-gate bgep->link_state, UPORDOWN(bgep->param_link_up))); 1495*0Sstevel@tonic-gate BGE_DEBUG(("bge_phys_check: update %lld change %lld " 1496*0Sstevel@tonic-gate "now %lld delta %lld", 1497*0Sstevel@tonic-gate bgep->phys_write_time, bgep->phys_event_time, now, deltat)); 1498*0Sstevel@tonic-gate 1499*0Sstevel@tonic-gate if (orig_state == LINK_STATE_UP) 1500*0Sstevel@tonic-gate return (B_TRUE); 1501*0Sstevel@tonic-gate else 1502*0Sstevel@tonic-gate return (deltat > bgep->phys_delta_time); 1503*0Sstevel@tonic-gate } 1504