1*bdb9230aSGarrett D'Amore /* 2*bdb9230aSGarrett D'Amore * CDDL HEADER START 3*bdb9230aSGarrett D'Amore * 4*bdb9230aSGarrett D'Amore * The contents of this file are subject to the terms of the 5*bdb9230aSGarrett D'Amore * Common Development and Distribution License (the "License"). 6*bdb9230aSGarrett D'Amore * You may not use this file except in compliance with the License. 7*bdb9230aSGarrett D'Amore * 8*bdb9230aSGarrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*bdb9230aSGarrett D'Amore * or http://www.opensolaris.org/os/licensing. 10*bdb9230aSGarrett D'Amore * See the License for the specific language governing permissions 11*bdb9230aSGarrett D'Amore * and limitations under the License. 12*bdb9230aSGarrett D'Amore * 13*bdb9230aSGarrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each 14*bdb9230aSGarrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*bdb9230aSGarrett D'Amore * If applicable, add the following below this CDDL HEADER, with the 16*bdb9230aSGarrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying 17*bdb9230aSGarrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner] 18*bdb9230aSGarrett D'Amore * 19*bdb9230aSGarrett D'Amore * CDDL HEADER END 20*bdb9230aSGarrett D'Amore */ 21*bdb9230aSGarrett D'Amore /* 22*bdb9230aSGarrett D'Amore * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23*bdb9230aSGarrett D'Amore * Use is subject to license terms. 24*bdb9230aSGarrett D'Amore */ 25*bdb9230aSGarrett D'Amore 26*bdb9230aSGarrett D'Amore /* 27*bdb9230aSGarrett D'Amore * mii - MII/PHY support for MAC drivers 28*bdb9230aSGarrett D'Amore * 29*bdb9230aSGarrett D'Amore * Utility module to provide a consistent interface to a MAC driver accross 30*bdb9230aSGarrett D'Amore * different implementations of PHY devices 31*bdb9230aSGarrett D'Amore */ 32*bdb9230aSGarrett D'Amore 33*bdb9230aSGarrett D'Amore #include <sys/types.h> 34*bdb9230aSGarrett D'Amore #include <sys/debug.h> 35*bdb9230aSGarrett D'Amore #include <sys/errno.h> 36*bdb9230aSGarrett D'Amore #include <sys/param.h> 37*bdb9230aSGarrett D'Amore #include <sys/sysmacros.h> 38*bdb9230aSGarrett D'Amore #include <sys/stropts.h> 39*bdb9230aSGarrett D'Amore #include <sys/stream.h> 40*bdb9230aSGarrett D'Amore #include <sys/kmem.h> 41*bdb9230aSGarrett D'Amore #include <sys/conf.h> 42*bdb9230aSGarrett D'Amore #include <sys/ddi.h> 43*bdb9230aSGarrett D'Amore #include <sys/sunddi.h> 44*bdb9230aSGarrett D'Amore #include <sys/devops.h> 45*bdb9230aSGarrett D'Amore #include <sys/modctl.h> 46*bdb9230aSGarrett D'Amore #include <sys/cmn_err.h> 47*bdb9230aSGarrett D'Amore #include <sys/miiregs.h> 48*bdb9230aSGarrett D'Amore #include "dnet_mii.h" 49*bdb9230aSGarrett D'Amore 50*bdb9230aSGarrett D'Amore 51*bdb9230aSGarrett D'Amore #ifdef DEBUG 52*bdb9230aSGarrett D'Amore #define MIIDEBUG 53*bdb9230aSGarrett D'Amore int miidebug = 0; 54*bdb9230aSGarrett D'Amore #define MIITRACE 1 55*bdb9230aSGarrett D'Amore #define MIIDUMP 2 56*bdb9230aSGarrett D'Amore #define MIIPROBE 4 57*bdb9230aSGarrett D'Amore #define MIICOMPAT 8 58*bdb9230aSGarrett D'Amore #endif 59*bdb9230aSGarrett D'Amore 60*bdb9230aSGarrett D'Amore /* Local functions */ 61*bdb9230aSGarrett D'Amore static struct phydata *mii_get_valid_phydata(mii_handle_t mac, int phy); 62*bdb9230aSGarrett D'Amore static void mii_portmon(mii_handle_t mac); 63*bdb9230aSGarrett D'Amore 64*bdb9230aSGarrett D'Amore /* Vendor specific callback function prototypes */ 65*bdb9230aSGarrett D'Amore static void dump_NS83840(mii_handle_t, int); 66*bdb9230aSGarrett D'Amore static void dump_ICS1890(struct mii_info *, int); 67*bdb9230aSGarrett D'Amore static int getspeed_NS83840(mii_handle_t, int, int *, int *); 68*bdb9230aSGarrett D'Amore static int getspeed_82553(mii_handle_t, int, int *, int *); 69*bdb9230aSGarrett D'Amore static int getspeed_ICS1890(mii_handle_t, int, int *, int *); 70*bdb9230aSGarrett D'Amore static int getspeed_generic(mii_handle_t, int, int *, int *); 71*bdb9230aSGarrett D'Amore static void postreset_ICS1890(mii_handle_t mac, int phy); 72*bdb9230aSGarrett D'Amore static void postreset_NS83840(mii_handle_t mac, int phy); 73*bdb9230aSGarrett D'Amore 74*bdb9230aSGarrett D'Amore /* 75*bdb9230aSGarrett D'Amore * MII Interface functions 76*bdb9230aSGarrett D'Amore */ 77*bdb9230aSGarrett D'Amore 78*bdb9230aSGarrett D'Amore /* 79*bdb9230aSGarrett D'Amore * Register an instance of an MII interface user 80*bdb9230aSGarrett D'Amore */ 81*bdb9230aSGarrett D'Amore 82*bdb9230aSGarrett D'Amore int 83*bdb9230aSGarrett D'Amore mii_create(dev_info_t *dip, /* Passed to read/write functions */ 84*bdb9230aSGarrett D'Amore mii_writefunc_t writefunc, /* How to write to a MII register */ 85*bdb9230aSGarrett D'Amore mii_readfunc_t readfunc, /* How to read from a MII regster */ 86*bdb9230aSGarrett D'Amore mii_handle_t *macp) 87*bdb9230aSGarrett D'Amore { 88*bdb9230aSGarrett D'Amore mii_handle_t mac; 89*bdb9230aSGarrett D'Amore 90*bdb9230aSGarrett D'Amore /* Allocate space for the mii structure */ 91*bdb9230aSGarrett D'Amore if ((mac = (mii_handle_t) 92*bdb9230aSGarrett D'Amore kmem_zalloc(sizeof (struct mii_info), KM_NOSLEEP)) == NULL) 93*bdb9230aSGarrett D'Amore return (MII_NOMEM); 94*bdb9230aSGarrett D'Amore 95*bdb9230aSGarrett D'Amore mac->mii_write = writefunc; 96*bdb9230aSGarrett D'Amore mac->mii_read = readfunc; 97*bdb9230aSGarrett D'Amore mac->mii_dip = dip; 98*bdb9230aSGarrett D'Amore *macp = mac; 99*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 100*bdb9230aSGarrett D'Amore } 101*bdb9230aSGarrett D'Amore 102*bdb9230aSGarrett D'Amore /* 103*bdb9230aSGarrett D'Amore * Returns true if PHY at address phy is accessible. This should be 104*bdb9230aSGarrett D'Amore * considered the only function that takes a PHY address that can be called 105*bdb9230aSGarrett D'Amore * before mii_init_phy. There should be at least one bit set in the status 106*bdb9230aSGarrett D'Amore * register, and at least one clear 107*bdb9230aSGarrett D'Amore */ 108*bdb9230aSGarrett D'Amore int 109*bdb9230aSGarrett D'Amore mii_probe_phy(mii_handle_t mac, int phy) 110*bdb9230aSGarrett D'Amore { 111*bdb9230aSGarrett D'Amore ushort_t status; 112*bdb9230aSGarrett D'Amore dev_info_t *dip; 113*bdb9230aSGarrett D'Amore 114*bdb9230aSGarrett D'Amore if (!mac || phy < 0 || phy > 31) 115*bdb9230aSGarrett D'Amore return (MII_PARAM); 116*bdb9230aSGarrett D'Amore 117*bdb9230aSGarrett D'Amore dip = mac->mii_dip; 118*bdb9230aSGarrett D'Amore 119*bdb9230aSGarrett D'Amore /* Clear any latched bits by reading twice */ 120*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, MII_STATUS); 121*bdb9230aSGarrett D'Amore status = mac->mii_read(dip, phy, MII_STATUS); 122*bdb9230aSGarrett D'Amore 123*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG 124*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, MII_CONTROL); 125*bdb9230aSGarrett D'Amore if (miidebug & MIIPROBE) 126*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "PHY Probe: Control=%x, Status=%x", 127*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, MII_CONTROL), status); 128*bdb9230aSGarrett D'Amore #endif 129*bdb9230aSGarrett D'Amore /* 130*bdb9230aSGarrett D'Amore * At least one bit in status should be clear (one of the error 131*bdb9230aSGarrett D'Amore * bits), and there must be at least one bit set for the device 132*bdb9230aSGarrett D'Amore * capabilities. Unconnected devices tend to show 0xffff, but 0x0000 133*bdb9230aSGarrett D'Amore * has been seen. 134*bdb9230aSGarrett D'Amore */ 135*bdb9230aSGarrett D'Amore 136*bdb9230aSGarrett D'Amore if (status == 0xffff || status == 0x0000) 137*bdb9230aSGarrett D'Amore return (MII_PHYNOTPRESENT); 138*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 139*bdb9230aSGarrett D'Amore } 140*bdb9230aSGarrett D'Amore 141*bdb9230aSGarrett D'Amore /* 142*bdb9230aSGarrett D'Amore * Initialise PHY, and store info about it in the handle for future 143*bdb9230aSGarrett D'Amore * reference when the MAC calls us. PHY Vendor-specific code here isolates 144*bdb9230aSGarrett D'Amore * the LAN driver from worrying about different PHY implementations 145*bdb9230aSGarrett D'Amore */ 146*bdb9230aSGarrett D'Amore 147*bdb9230aSGarrett D'Amore int 148*bdb9230aSGarrett D'Amore mii_init_phy(mii_handle_t mac, int phy) 149*bdb9230aSGarrett D'Amore { 150*bdb9230aSGarrett D'Amore ushort_t status; 151*bdb9230aSGarrett D'Amore void *dip; 152*bdb9230aSGarrett D'Amore struct phydata *phydata; 153*bdb9230aSGarrett D'Amore 154*bdb9230aSGarrett D'Amore if ((mac == (mii_handle_t)NULL) || phy < 0 || phy > 31) 155*bdb9230aSGarrett D'Amore return (MII_PARAM); 156*bdb9230aSGarrett D'Amore 157*bdb9230aSGarrett D'Amore dip = mac->mii_dip; 158*bdb9230aSGarrett D'Amore 159*bdb9230aSGarrett D'Amore /* Create a phydata structure for this new phy */ 160*bdb9230aSGarrett D'Amore if (mac->phys[phy]) 161*bdb9230aSGarrett D'Amore return (MII_PHYPRESENT); 162*bdb9230aSGarrett D'Amore 163*bdb9230aSGarrett D'Amore mac->phys[phy] = phydata = (struct phydata *) 164*bdb9230aSGarrett D'Amore kmem_zalloc(sizeof (struct phydata), KM_NOSLEEP); 165*bdb9230aSGarrett D'Amore 166*bdb9230aSGarrett D'Amore if (!phydata) 167*bdb9230aSGarrett D'Amore return (MII_NOMEM); 168*bdb9230aSGarrett D'Amore 169*bdb9230aSGarrett D'Amore phydata->id = (ulong_t)mac->mii_read(dip, phy, MII_PHYIDH) << 16; 170*bdb9230aSGarrett D'Amore phydata->id |= (ulong_t)mac->mii_read(dip, phy, MII_PHYIDL); 171*bdb9230aSGarrett D'Amore phydata->state = phy_state_unknown; 172*bdb9230aSGarrett D'Amore 173*bdb9230aSGarrett D'Amore /* Override speed and duplex mode from conf-file if present */ 174*bdb9230aSGarrett D'Amore phydata->fix_duplex = 175*bdb9230aSGarrett D'Amore ddi_getprop(DDI_DEV_T_NONE, 176*bdb9230aSGarrett D'Amore mac->mii_dip, DDI_PROP_DONTPASS, "full-duplex", 0); 177*bdb9230aSGarrett D'Amore 178*bdb9230aSGarrett D'Amore phydata->fix_speed = 179*bdb9230aSGarrett D'Amore ddi_getprop(DDI_DEV_T_NONE, 180*bdb9230aSGarrett D'Amore mac->mii_dip, DDI_PROP_DONTPASS, "speed", 0); 181*bdb9230aSGarrett D'Amore 182*bdb9230aSGarrett D'Amore status = mac->mii_read(dip, phy, MII_STATUS); 183*bdb9230aSGarrett D'Amore 184*bdb9230aSGarrett D'Amore /* 185*bdb9230aSGarrett D'Amore * when explicitly setting speed or duplex, we must 186*bdb9230aSGarrett D'Amore * disable autonegotiation 187*bdb9230aSGarrett D'Amore */ 188*bdb9230aSGarrett D'Amore if (!(status & MII_STATUS_CANAUTONEG) || 189*bdb9230aSGarrett D'Amore phydata->fix_speed || phydata->fix_duplex) { 190*bdb9230aSGarrett D'Amore /* 191*bdb9230aSGarrett D'Amore * If local side cannot autonegotiate, we can't try to enable 192*bdb9230aSGarrett D'Amore * full duplex without the user's consent, because we cannot 193*bdb9230aSGarrett D'Amore * tell without AN if the partner can support it 194*bdb9230aSGarrett D'Amore */ 195*bdb9230aSGarrett D'Amore if ((status & (MII_STATUS_100_BASEX | MII_STATUS_100_BASEX_FD | 196*bdb9230aSGarrett D'Amore MII_STATUS_100_BASE_T4)) && phydata->fix_speed == 0) { 197*bdb9230aSGarrett D'Amore phydata->fix_speed = 100; 198*bdb9230aSGarrett D'Amore } else if ((status & (MII_STATUS_10 | MII_STATUS_10_FD)) && 199*bdb9230aSGarrett D'Amore phydata->fix_speed == 0) { 200*bdb9230aSGarrett D'Amore phydata->fix_speed = 10; 201*bdb9230aSGarrett D'Amore } else if (phydata->fix_speed == 0) { 202*bdb9230aSGarrett D'Amore /* A very stupid PHY would not be supported */ 203*bdb9230aSGarrett D'Amore kmem_free(mac->phys[phy], sizeof (struct phydata)); 204*bdb9230aSGarrett D'Amore mac->phys[phy] = NULL; 205*bdb9230aSGarrett D'Amore return (MII_NOTSUPPORTED); 206*bdb9230aSGarrett D'Amore } 207*bdb9230aSGarrett D'Amore /* mii_sync will sort out the speed selection on the PHY */ 208*bdb9230aSGarrett D'Amore } else 209*bdb9230aSGarrett D'Amore phydata->control = MII_CONTROL_ANE; 210*bdb9230aSGarrett D'Amore 211*bdb9230aSGarrett D'Amore switch (MII_PHY_MFG(phydata->id)) { 212*bdb9230aSGarrett D'Amore case OUI_NATIONAL_SEMICONDUCTOR: 213*bdb9230aSGarrett D'Amore switch (MII_PHY_MODEL(phydata->id)) { 214*bdb9230aSGarrett D'Amore case NS_DP83840: 215*bdb9230aSGarrett D'Amore phydata->phy_postreset = postreset_NS83840; 216*bdb9230aSGarrett D'Amore phydata->phy_dump = dump_NS83840; 217*bdb9230aSGarrett D'Amore phydata->description = 218*bdb9230aSGarrett D'Amore "National Semiconductor DP-83840"; 219*bdb9230aSGarrett D'Amore phydata->phy_getspeed = getspeed_NS83840; 220*bdb9230aSGarrett D'Amore break; 221*bdb9230aSGarrett D'Amore default: 222*bdb9230aSGarrett D'Amore phydata->description = "Unknown NS"; 223*bdb9230aSGarrett D'Amore break; 224*bdb9230aSGarrett D'Amore } 225*bdb9230aSGarrett D'Amore break; 226*bdb9230aSGarrett D'Amore 227*bdb9230aSGarrett D'Amore case OUI_INTEL: 228*bdb9230aSGarrett D'Amore switch (MII_PHY_MODEL(phydata->id)) { 229*bdb9230aSGarrett D'Amore case INTEL_82553_CSTEP: 230*bdb9230aSGarrett D'Amore phydata->description = "Intel 82553 C-step"; 231*bdb9230aSGarrett D'Amore phydata->phy_getspeed = getspeed_82553; 232*bdb9230aSGarrett D'Amore break; 233*bdb9230aSGarrett D'Amore case INTEL_82555: 234*bdb9230aSGarrett D'Amore phydata->description = "Intel 82555"; 235*bdb9230aSGarrett D'Amore phydata->phy_getspeed = getspeed_82553; 236*bdb9230aSGarrett D'Amore break; 237*bdb9230aSGarrett D'Amore case INTEL_82562_EH: 238*bdb9230aSGarrett D'Amore phydata->description = "Intel 82562 EH"; 239*bdb9230aSGarrett D'Amore phydata->phy_getspeed = getspeed_82553; 240*bdb9230aSGarrett D'Amore break; 241*bdb9230aSGarrett D'Amore case INTEL_82562_ET: 242*bdb9230aSGarrett D'Amore phydata->description = "Intel 82562 ET"; 243*bdb9230aSGarrett D'Amore phydata->phy_getspeed = getspeed_82553; 244*bdb9230aSGarrett D'Amore break; 245*bdb9230aSGarrett D'Amore case INTEL_82562_EM: 246*bdb9230aSGarrett D'Amore phydata->description = "Intel 82562 EM"; 247*bdb9230aSGarrett D'Amore phydata->phy_getspeed = getspeed_82553; 248*bdb9230aSGarrett D'Amore break; 249*bdb9230aSGarrett D'Amore default: 250*bdb9230aSGarrett D'Amore phydata->description = "Unknown INTEL"; 251*bdb9230aSGarrett D'Amore break; 252*bdb9230aSGarrett D'Amore } 253*bdb9230aSGarrett D'Amore break; 254*bdb9230aSGarrett D'Amore 255*bdb9230aSGarrett D'Amore case OUI_ICS: 256*bdb9230aSGarrett D'Amore switch (MII_PHY_MODEL(phydata->id)) { 257*bdb9230aSGarrett D'Amore case ICS_1890: 258*bdb9230aSGarrett D'Amore case ICS_1889: 259*bdb9230aSGarrett D'Amore phydata->phy_postreset = postreset_ICS1890; 260*bdb9230aSGarrett D'Amore phydata->description = "ICS 1890/1889 PHY"; 261*bdb9230aSGarrett D'Amore phydata->phy_getspeed = getspeed_ICS1890; 262*bdb9230aSGarrett D'Amore phydata->phy_dump = dump_ICS1890; 263*bdb9230aSGarrett D'Amore break; 264*bdb9230aSGarrett D'Amore default: 265*bdb9230aSGarrett D'Amore phydata->description = "ICS Unknown PHY"; 266*bdb9230aSGarrett D'Amore break; 267*bdb9230aSGarrett D'Amore } 268*bdb9230aSGarrett D'Amore break; 269*bdb9230aSGarrett D'Amore 270*bdb9230aSGarrett D'Amore default: /* Non-standard PHYs, that encode weird IDs */ 271*bdb9230aSGarrett D'Amore phydata->description = "Unknown PHY"; 272*bdb9230aSGarrett D'Amore phydata->phy_dump = NULL; 273*bdb9230aSGarrett D'Amore phydata->phy_getspeed = getspeed_generic; 274*bdb9230aSGarrett D'Amore break; 275*bdb9230aSGarrett D'Amore } 276*bdb9230aSGarrett D'Amore 277*bdb9230aSGarrett D'Amore /* Do all post-reset hacks and user settings */ 278*bdb9230aSGarrett D'Amore (void) mii_sync(mac, phy); 279*bdb9230aSGarrett D'Amore 280*bdb9230aSGarrett D'Amore if (ddi_getprop(DDI_DEV_T_NONE, mac->mii_dip, DDI_PROP_DONTPASS, 281*bdb9230aSGarrett D'Amore "dump-phy", 0)) 282*bdb9230aSGarrett D'Amore (void) mii_dump_phy(mac, phy); 283*bdb9230aSGarrett D'Amore 284*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 285*bdb9230aSGarrett D'Amore } 286*bdb9230aSGarrett D'Amore 287*bdb9230aSGarrett D'Amore /* 288*bdb9230aSGarrett D'Amore * Cause a reset on a PHY 289*bdb9230aSGarrett D'Amore */ 290*bdb9230aSGarrett D'Amore 291*bdb9230aSGarrett D'Amore int 292*bdb9230aSGarrett D'Amore mii_reset_phy(mii_handle_t mac, int phy, enum mii_wait_type wait) 293*bdb9230aSGarrett D'Amore { 294*bdb9230aSGarrett D'Amore int i; 295*bdb9230aSGarrett D'Amore struct phydata *phyd; 296*bdb9230aSGarrett D'Amore ushort_t control; 297*bdb9230aSGarrett D'Amore if (!(phyd = mii_get_valid_phydata(mac, phy))) 298*bdb9230aSGarrett D'Amore return (MII_PARAM); 299*bdb9230aSGarrett D'Amore 300*bdb9230aSGarrett D'Amore /* Strobe the reset bit in the control register */ 301*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, MII_CONTROL, 302*bdb9230aSGarrett D'Amore phyd->control | MII_CONTROL_RESET); 303*bdb9230aSGarrett D'Amore 304*bdb9230aSGarrett D'Amore phyd->state = phy_state_unknown; 305*bdb9230aSGarrett D'Amore 306*bdb9230aSGarrett D'Amore /* 307*bdb9230aSGarrett D'Amore * This is likely to be very fast (ie, by the time we read the 308*bdb9230aSGarrett D'Amore * control register once, the devices we have seen can have already 309*bdb9230aSGarrett D'Amore * reset), but according to 802.3u 22.2.4.1.1, it could be up to .5 sec. 310*bdb9230aSGarrett D'Amore */ 311*bdb9230aSGarrett D'Amore if (wait == mii_wait_interrupt || wait == mii_wait_user) { 312*bdb9230aSGarrett D'Amore for (i = 100; i--; ) { 313*bdb9230aSGarrett D'Amore control = mac->mii_read(mac->mii_dip, phy, MII_CONTROL); 314*bdb9230aSGarrett D'Amore if (!(control & MII_CONTROL_RESET)) 315*bdb9230aSGarrett D'Amore break; 316*bdb9230aSGarrett D'Amore drv_usecwait(10); 317*bdb9230aSGarrett D'Amore } 318*bdb9230aSGarrett D'Amore if (i) 319*bdb9230aSGarrett D'Amore goto reset_completed; 320*bdb9230aSGarrett D'Amore } 321*bdb9230aSGarrett D'Amore 322*bdb9230aSGarrett D'Amore if (wait == mii_wait_user) { 323*bdb9230aSGarrett D'Amore for (i = 50; i--; ) { 324*bdb9230aSGarrett D'Amore control = mac->mii_read(mac->mii_dip, phy, MII_CONTROL); 325*bdb9230aSGarrett D'Amore if (!(control & MII_CONTROL_RESET)) 326*bdb9230aSGarrett D'Amore break; 327*bdb9230aSGarrett D'Amore delay(drv_usectohz(10000)); 328*bdb9230aSGarrett D'Amore } 329*bdb9230aSGarrett D'Amore if (i) 330*bdb9230aSGarrett D'Amore goto reset_completed; 331*bdb9230aSGarrett D'Amore return (MII_HARDFAIL); /* It MUST reset within this time */ 332*bdb9230aSGarrett D'Amore 333*bdb9230aSGarrett D'Amore } 334*bdb9230aSGarrett D'Amore return (MII_TIMEOUT); 335*bdb9230aSGarrett D'Amore 336*bdb9230aSGarrett D'Amore reset_completed: 337*bdb9230aSGarrett D'Amore (void) mii_sync(mac, phy); 338*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 339*bdb9230aSGarrett D'Amore } 340*bdb9230aSGarrett D'Amore 341*bdb9230aSGarrett D'Amore /* 342*bdb9230aSGarrett D'Amore * This routine is called to synchronise the software and the PHY. It should 343*bdb9230aSGarrett D'Amore * be called after the PHY is reset, and after initialising the PHY. This 344*bdb9230aSGarrett D'Amore * routine is external because devices (DNET) can reset the PHY in ways beyond 345*bdb9230aSGarrett D'Amore * the control of the mii interface. Should this happen, the driver is 346*bdb9230aSGarrett D'Amore * required to call mii_sync(). 347*bdb9230aSGarrett D'Amore * If the PHY is resetting still when this is called, it will do nothing, 348*bdb9230aSGarrett D'Amore * but, it will be retriggered when the portmon timer expires. 349*bdb9230aSGarrett D'Amore */ 350*bdb9230aSGarrett D'Amore 351*bdb9230aSGarrett D'Amore int 352*bdb9230aSGarrett D'Amore mii_sync(mii_handle_t mac, int phy) 353*bdb9230aSGarrett D'Amore { 354*bdb9230aSGarrett D'Amore struct phydata *phyd = mac->phys[phy]; 355*bdb9230aSGarrett D'Amore int len, i, numprop; 356*bdb9230aSGarrett D'Amore struct regprop { 357*bdb9230aSGarrett D'Amore int reg; 358*bdb9230aSGarrett D'Amore int value; 359*bdb9230aSGarrett D'Amore } *regprop; 360*bdb9230aSGarrett D'Amore 361*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG 362*bdb9230aSGarrett D'Amore if (miidebug & MIITRACE) 363*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "mii_sync (phy addr %d)", phy); 364*bdb9230aSGarrett D'Amore #endif 365*bdb9230aSGarrett D'Amore 366*bdb9230aSGarrett D'Amore len = 0; 367*bdb9230aSGarrett D'Amore /* 368*bdb9230aSGarrett D'Amore * Conf file can specify a sequence of values to write to 369*bdb9230aSGarrett D'Amore * the PHY registers if required 370*bdb9230aSGarrett D'Amore */ 371*bdb9230aSGarrett D'Amore if (ddi_getlongprop(DDI_DEV_T_ANY, mac->mii_dip, 372*bdb9230aSGarrett D'Amore DDI_PROP_DONTPASS, "phy-registers", (caddr_t)®prop, 373*bdb9230aSGarrett D'Amore &len) == DDI_PROP_SUCCESS) { 374*bdb9230aSGarrett D'Amore numprop = len / sizeof (struct regprop); 375*bdb9230aSGarrett D'Amore for (i = 0; i < numprop; i++) { 376*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, 377*bdb9230aSGarrett D'Amore regprop[i].reg, regprop[i].value); 378*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG 379*bdb9230aSGarrett D'Amore if (miidebug & MIITRACE) 380*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "PHY Write reg %d=%x", 381*bdb9230aSGarrett D'Amore regprop[i].reg, regprop[i].value); 382*bdb9230aSGarrett D'Amore #endif 383*bdb9230aSGarrett D'Amore } 384*bdb9230aSGarrett D'Amore kmem_free(regprop, len); 385*bdb9230aSGarrett D'Amore } else { 386*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control); 387*bdb9230aSGarrett D'Amore if (phyd->phy_postreset) 388*bdb9230aSGarrett D'Amore phyd->phy_postreset(mac, phy); 389*bdb9230aSGarrett D'Amore if (phyd->fix_speed || phyd->fix_duplex) { 390*bdb9230aSGarrett D'Amore /* XXX function return value ignored */ 391*bdb9230aSGarrett D'Amore (void) mii_fixspeed(mac, phy, phyd->fix_speed, 392*bdb9230aSGarrett D'Amore phyd->fix_duplex); 393*bdb9230aSGarrett D'Amore } 394*bdb9230aSGarrett D'Amore } 395*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 396*bdb9230aSGarrett D'Amore } 397*bdb9230aSGarrett D'Amore 398*bdb9230aSGarrett D'Amore /* 399*bdb9230aSGarrett D'Amore * Disable full-duplex negotiation on the PHY. This is useful if the 400*bdb9230aSGarrett D'Amore * driver or link-partner is advertising full duplex, but does not support 401*bdb9230aSGarrett D'Amore * it properly (as some previous solaris drivers didn't) 402*bdb9230aSGarrett D'Amore */ 403*bdb9230aSGarrett D'Amore 404*bdb9230aSGarrett D'Amore int 405*bdb9230aSGarrett D'Amore mii_disable_fullduplex(mii_handle_t mac, int phy) 406*bdb9230aSGarrett D'Amore { 407*bdb9230aSGarrett D'Amore void *dip = mac->mii_dip; 408*bdb9230aSGarrett D'Amore ushort_t expansion, miiadvert; 409*bdb9230aSGarrett D'Amore /* dont advertise full duplex capabilites */ 410*bdb9230aSGarrett D'Amore const int fullduplex = MII_ABILITY_10BASE_T_FD 411*bdb9230aSGarrett D'Amore | MII_ABILITY_100BASE_TX_FD; 412*bdb9230aSGarrett D'Amore 413*bdb9230aSGarrett D'Amore if (!(mac->mii_read(dip, phy, MII_STATUS) & MII_STATUS_CANAUTONEG)) { 414*bdb9230aSGarrett D'Amore /* 415*bdb9230aSGarrett D'Amore * Local side cannot autonegotiate, so full duplex should 416*bdb9230aSGarrett D'Amore * never be negotiated. Consider it as a success 417*bdb9230aSGarrett D'Amore */ 418*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 419*bdb9230aSGarrett D'Amore } 420*bdb9230aSGarrett D'Amore 421*bdb9230aSGarrett D'Amore /* Change what we advertise if it includes full duplex */ 422*bdb9230aSGarrett D'Amore 423*bdb9230aSGarrett D'Amore miiadvert = mac->mii_read(dip, phy, MII_AN_ADVERT); 424*bdb9230aSGarrett D'Amore if (miiadvert & fullduplex) 425*bdb9230aSGarrett D'Amore mac->mii_write(dip, phy, MII_AN_ADVERT, 426*bdb9230aSGarrett D'Amore miiadvert & ~fullduplex); 427*bdb9230aSGarrett D'Amore 428*bdb9230aSGarrett D'Amore /* See what other end is able to do. */ 429*bdb9230aSGarrett D'Amore 430*bdb9230aSGarrett D'Amore expansion = mac->mii_read(dip, phy, MII_AN_EXPANSION); 431*bdb9230aSGarrett D'Amore 432*bdb9230aSGarrett D'Amore /* 433*bdb9230aSGarrett D'Amore * Renegotiate if the link partner supports autonegotiation 434*bdb9230aSGarrett D'Amore * If it doesn't, we will never have auto-negotiated full duplex 435*bdb9230aSGarrett D'Amore * anyway 436*bdb9230aSGarrett D'Amore */ 437*bdb9230aSGarrett D'Amore 438*bdb9230aSGarrett D'Amore if (expansion & MII_AN_EXP_LPCANAN) 439*bdb9230aSGarrett D'Amore return (mii_rsan(mac, phy, mii_wait_none)); 440*bdb9230aSGarrett D'Amore else 441*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 442*bdb9230aSGarrett D'Amore } 443*bdb9230aSGarrett D'Amore 444*bdb9230aSGarrett D'Amore /* 445*bdb9230aSGarrett D'Amore * (re)enable autonegotiation on a PHY. 446*bdb9230aSGarrett D'Amore */ 447*bdb9230aSGarrett D'Amore 448*bdb9230aSGarrett D'Amore int 449*bdb9230aSGarrett D'Amore mii_autoneg_enab(mii_handle_t mac, int phy) 450*bdb9230aSGarrett D'Amore { 451*bdb9230aSGarrett D'Amore struct phydata *phyd; 452*bdb9230aSGarrett D'Amore if (!(phyd = mii_get_valid_phydata(mac, phy))) 453*bdb9230aSGarrett D'Amore return (MII_PARAM); 454*bdb9230aSGarrett D'Amore phyd->control |= MII_CONTROL_ANE; 455*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control); 456*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 457*bdb9230aSGarrett D'Amore } 458*bdb9230aSGarrett D'Amore 459*bdb9230aSGarrett D'Amore /* 460*bdb9230aSGarrett D'Amore * Check the link status of a PHY connection 461*bdb9230aSGarrett D'Amore */ 462*bdb9230aSGarrett D'Amore int 463*bdb9230aSGarrett D'Amore mii_linkup(mii_handle_t mac, int phy) 464*bdb9230aSGarrett D'Amore { 465*bdb9230aSGarrett D'Amore ushort_t status; 466*bdb9230aSGarrett D'Amore 467*bdb9230aSGarrett D'Amore /* 468*bdb9230aSGarrett D'Amore * Link status latches, so we need to read it twice, to make sure we 469*bdb9230aSGarrett D'Amore * get its current status 470*bdb9230aSGarrett D'Amore */ 471*bdb9230aSGarrett D'Amore mac->mii_read(mac->mii_dip, phy, MII_STATUS); 472*bdb9230aSGarrett D'Amore status = mac->mii_read(mac->mii_dip, phy, MII_STATUS); 473*bdb9230aSGarrett D'Amore 474*bdb9230aSGarrett D'Amore if (status != 0xffff && (status & MII_STATUS_LINKUP)) 475*bdb9230aSGarrett D'Amore return (1); 476*bdb9230aSGarrett D'Amore else 477*bdb9230aSGarrett D'Amore return (0); 478*bdb9230aSGarrett D'Amore } 479*bdb9230aSGarrett D'Amore 480*bdb9230aSGarrett D'Amore /* 481*bdb9230aSGarrett D'Amore * Discover what speed the PHY is running at, irrespective of wheather it 482*bdb9230aSGarrett D'Amore * autonegotiated this, or was fixed at that rate. 483*bdb9230aSGarrett D'Amore */ 484*bdb9230aSGarrett D'Amore 485*bdb9230aSGarrett D'Amore int 486*bdb9230aSGarrett D'Amore mii_getspeed(mii_handle_t mac, int phy, int *speed, int *fulld) 487*bdb9230aSGarrett D'Amore { 488*bdb9230aSGarrett D'Amore struct phydata *phyd; 489*bdb9230aSGarrett D'Amore 490*bdb9230aSGarrett D'Amore if (!(phyd = mii_get_valid_phydata(mac, phy))) 491*bdb9230aSGarrett D'Amore return (MII_PARAM); 492*bdb9230aSGarrett D'Amore if (!(phyd->control & MII_CONTROL_ANE)) { 493*bdb9230aSGarrett D'Amore /* 494*bdb9230aSGarrett D'Amore * user has requested fixed speed operation, return what we 495*bdb9230aSGarrett D'Amore * wrote to the control registerfrom control register 496*bdb9230aSGarrett D'Amore */ 497*bdb9230aSGarrett D'Amore 498*bdb9230aSGarrett D'Amore *speed = phyd->control & MII_CONTROL_100MB ? 100:10; 499*bdb9230aSGarrett D'Amore *fulld = phyd->control & MII_CONTROL_FDUPLEX ? 1:0; 500*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 501*bdb9230aSGarrett D'Amore } 502*bdb9230aSGarrett D'Amore 503*bdb9230aSGarrett D'Amore if (!phyd->phy_getspeed) /* No standard way to do this(!) */ 504*bdb9230aSGarrett D'Amore return (MII_NOTSUPPORTED); 505*bdb9230aSGarrett D'Amore 506*bdb9230aSGarrett D'Amore return (phyd->phy_getspeed(mac, phy, speed, fulld)); 507*bdb9230aSGarrett D'Amore } 508*bdb9230aSGarrett D'Amore 509*bdb9230aSGarrett D'Amore /* 510*bdb9230aSGarrett D'Amore * Fix the speed and duplex mode of a PHY 511*bdb9230aSGarrett D'Amore */ 512*bdb9230aSGarrett D'Amore 513*bdb9230aSGarrett D'Amore int 514*bdb9230aSGarrett D'Amore mii_fixspeed(mii_handle_t mac, int phy, int speed, int fullduplex) 515*bdb9230aSGarrett D'Amore { 516*bdb9230aSGarrett D'Amore struct phydata *phyd; 517*bdb9230aSGarrett D'Amore 518*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG 519*bdb9230aSGarrett D'Amore cmn_err(CE_CONT, "!%s: setting speed to %d, %s duplex", 520*bdb9230aSGarrett D'Amore ddi_get_name(mac->mii_dip), speed, 521*bdb9230aSGarrett D'Amore fullduplex ? "full" : "half"); 522*bdb9230aSGarrett D'Amore #endif 523*bdb9230aSGarrett D'Amore 524*bdb9230aSGarrett D'Amore if (!(phyd = mii_get_valid_phydata(mac, phy))) 525*bdb9230aSGarrett D'Amore return (MII_PARAM); 526*bdb9230aSGarrett D'Amore phyd->control &= ~MII_CONTROL_ANE; 527*bdb9230aSGarrett D'Amore 528*bdb9230aSGarrett D'Amore if (speed == 100) 529*bdb9230aSGarrett D'Amore phyd->control |= MII_CONTROL_100MB; 530*bdb9230aSGarrett D'Amore else if (speed == 10) 531*bdb9230aSGarrett D'Amore phyd->control &= ~MII_CONTROL_100MB; 532*bdb9230aSGarrett D'Amore else 533*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "%s: mii does not support %d Mb/s speed", 534*bdb9230aSGarrett D'Amore ddi_get_name(mac->mii_dip), speed); 535*bdb9230aSGarrett D'Amore 536*bdb9230aSGarrett D'Amore if (fullduplex) 537*bdb9230aSGarrett D'Amore phyd->control |= MII_CONTROL_FDUPLEX; 538*bdb9230aSGarrett D'Amore else 539*bdb9230aSGarrett D'Amore phyd->control &= ~MII_CONTROL_FDUPLEX; 540*bdb9230aSGarrett D'Amore 541*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control); 542*bdb9230aSGarrett D'Amore phyd->fix_speed = speed; 543*bdb9230aSGarrett D'Amore phyd->fix_duplex = fullduplex; 544*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 545*bdb9230aSGarrett D'Amore } 546*bdb9230aSGarrett D'Amore /* 547*bdb9230aSGarrett D'Amore * Electrically isolate/unisolate the PHY 548*bdb9230aSGarrett D'Amore */ 549*bdb9230aSGarrett D'Amore 550*bdb9230aSGarrett D'Amore int 551*bdb9230aSGarrett D'Amore mii_isolate(mii_handle_t mac, int phy) 552*bdb9230aSGarrett D'Amore { 553*bdb9230aSGarrett D'Amore struct phydata *phyd; 554*bdb9230aSGarrett D'Amore 555*bdb9230aSGarrett D'Amore if (!(phyd = mii_get_valid_phydata(mac, phy))) 556*bdb9230aSGarrett D'Amore return (MII_PARAM); 557*bdb9230aSGarrett D'Amore 558*bdb9230aSGarrett D'Amore phyd->control |= MII_CONTROL_ISOLATE; 559*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control); 560*bdb9230aSGarrett D'Amore 561*bdb9230aSGarrett D'Amore /* Wait for device to settle */ 562*bdb9230aSGarrett D'Amore drv_usecwait(50); 563*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 564*bdb9230aSGarrett D'Amore } 565*bdb9230aSGarrett D'Amore 566*bdb9230aSGarrett D'Amore int 567*bdb9230aSGarrett D'Amore mii_unisolate(mii_handle_t mac, int phy) 568*bdb9230aSGarrett D'Amore { 569*bdb9230aSGarrett D'Amore struct phydata *phyd; 570*bdb9230aSGarrett D'Amore 571*bdb9230aSGarrett D'Amore if (!(phyd = mii_get_valid_phydata(mac, phy))) 572*bdb9230aSGarrett D'Amore return (MII_PARAM); 573*bdb9230aSGarrett D'Amore 574*bdb9230aSGarrett D'Amore phyd->control &= ~MII_CONTROL_ISOLATE; 575*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, MII_CONTROL, phyd->control); 576*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 577*bdb9230aSGarrett D'Amore } 578*bdb9230aSGarrett D'Amore 579*bdb9230aSGarrett D'Amore /* 580*bdb9230aSGarrett D'Amore * Restart autonegotiation on a PHY 581*bdb9230aSGarrett D'Amore */ 582*bdb9230aSGarrett D'Amore 583*bdb9230aSGarrett D'Amore int 584*bdb9230aSGarrett D'Amore mii_rsan(mii_handle_t mac, int phy, enum mii_wait_type wait) 585*bdb9230aSGarrett D'Amore { 586*bdb9230aSGarrett D'Amore int i; 587*bdb9230aSGarrett D'Amore void *dip; 588*bdb9230aSGarrett D'Amore struct phydata *phyd; 589*bdb9230aSGarrett D'Amore 590*bdb9230aSGarrett D'Amore if (wait == mii_wait_interrupt || 591*bdb9230aSGarrett D'Amore !(phyd = mii_get_valid_phydata(mac, phy))) 592*bdb9230aSGarrett D'Amore return (MII_PARAM); 593*bdb9230aSGarrett D'Amore 594*bdb9230aSGarrett D'Amore if (phyd->fix_speed) 595*bdb9230aSGarrett D'Amore return (MII_STATE); 596*bdb9230aSGarrett D'Amore 597*bdb9230aSGarrett D'Amore dip = mac->mii_dip; 598*bdb9230aSGarrett D'Amore 599*bdb9230aSGarrett D'Amore phyd->control |= MII_CONTROL_ANE; 600*bdb9230aSGarrett D'Amore mac->mii_write(dip, phy, MII_CONTROL, phyd->control|MII_CONTROL_RSAN); 601*bdb9230aSGarrett D'Amore 602*bdb9230aSGarrett D'Amore /* 603*bdb9230aSGarrett D'Amore * This can take ages (a second or so). It makes more sense to use 604*bdb9230aSGarrett D'Amore * the port monitor rather than waiting for completion of this on the 605*bdb9230aSGarrett D'Amore * PHY. It is pointless doing a busy wait here 606*bdb9230aSGarrett D'Amore */ 607*bdb9230aSGarrett D'Amore 608*bdb9230aSGarrett D'Amore if (wait == mii_wait_user) { 609*bdb9230aSGarrett D'Amore for (i = 200; i--; ) { 610*bdb9230aSGarrett D'Amore delay(drv_usectohz(10000)); 611*bdb9230aSGarrett D'Amore if (mac->mii_read(dip, phy, MII_STATUS) & 612*bdb9230aSGarrett D'Amore MII_STATUS_ANDONE) 613*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 614*bdb9230aSGarrett D'Amore } 615*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, 616*bdb9230aSGarrett D'Amore "!%s:Timed out waiting for autonegotiation", 617*bdb9230aSGarrett D'Amore ddi_get_name(mac->mii_dip)); 618*bdb9230aSGarrett D'Amore return (MII_TIMEOUT); 619*bdb9230aSGarrett D'Amore } 620*bdb9230aSGarrett D'Amore return (MII_TIMEOUT); 621*bdb9230aSGarrett D'Amore } 622*bdb9230aSGarrett D'Amore 623*bdb9230aSGarrett D'Amore /* 624*bdb9230aSGarrett D'Amore * Debuging function to dump contents of PHY registers 625*bdb9230aSGarrett D'Amore */ 626*bdb9230aSGarrett D'Amore int 627*bdb9230aSGarrett D'Amore mii_dump_phy(mii_handle_t mac, int phy) 628*bdb9230aSGarrett D'Amore { 629*bdb9230aSGarrett D'Amore struct phydata *phydat; 630*bdb9230aSGarrett D'Amore 631*bdb9230aSGarrett D'Amore char *miiregs[] = { 632*bdb9230aSGarrett D'Amore "Control ", 633*bdb9230aSGarrett D'Amore "Status ", 634*bdb9230aSGarrett D'Amore "PHY Id(H) ", 635*bdb9230aSGarrett D'Amore "PHY Id(L) ", 636*bdb9230aSGarrett D'Amore "Advertisement ", 637*bdb9230aSGarrett D'Amore "Link Partner Ability", 638*bdb9230aSGarrett D'Amore "Expansion ", 639*bdb9230aSGarrett D'Amore "Next Page Transmit ", 640*bdb9230aSGarrett D'Amore 0 641*bdb9230aSGarrett D'Amore }; 642*bdb9230aSGarrett D'Amore int i; 643*bdb9230aSGarrett D'Amore 644*bdb9230aSGarrett D'Amore if (!(phydat = mii_get_valid_phydata(mac, phy))) 645*bdb9230aSGarrett D'Amore return (MII_PARAM); 646*bdb9230aSGarrett D'Amore 647*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "%s: PHY %d, type %s", ddi_get_name(mac->mii_dip), phy, 648*bdb9230aSGarrett D'Amore phydat->description ? phydat->description: "Unknown"); 649*bdb9230aSGarrett D'Amore 650*bdb9230aSGarrett D'Amore for (i = 0; miiregs[i]; i++) 651*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "%s:\t%x", 652*bdb9230aSGarrett D'Amore miiregs[i], mac->mii_read(mac->mii_dip, phy, i)); 653*bdb9230aSGarrett D'Amore 654*bdb9230aSGarrett D'Amore if (phydat->phy_dump) 655*bdb9230aSGarrett D'Amore phydat->phy_dump((struct mii_info *)mac, phy); 656*bdb9230aSGarrett D'Amore 657*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 658*bdb9230aSGarrett D'Amore } 659*bdb9230aSGarrett D'Amore 660*bdb9230aSGarrett D'Amore /* 661*bdb9230aSGarrett D'Amore * Start a periodic check to monitor the MII devices attached, and callback 662*bdb9230aSGarrett D'Amore * to the MAC driver when the state on a device changes 663*bdb9230aSGarrett D'Amore */ 664*bdb9230aSGarrett D'Amore 665*bdb9230aSGarrett D'Amore int 666*bdb9230aSGarrett D'Amore mii_start_portmon(mii_handle_t mac, mii_linkfunc_t notify, kmutex_t *lock) 667*bdb9230aSGarrett D'Amore { 668*bdb9230aSGarrett D'Amore if (mac->mii_linknotify || mac->portmon_timer) 669*bdb9230aSGarrett D'Amore return (MII_STATE); 670*bdb9230aSGarrett D'Amore mac->mii_linknotify = notify; 671*bdb9230aSGarrett D'Amore /* 672*bdb9230aSGarrett D'Amore * NOTE: Portmon is normally called through a timeout. In the case 673*bdb9230aSGarrett D'Amore * of starting off, we assume that the lock is already held 674*bdb9230aSGarrett D'Amore */ 675*bdb9230aSGarrett D'Amore mac->lock = NULL; /* portmon wont try to aquire any lock this time */ 676*bdb9230aSGarrett D'Amore mii_portmon(mac); 677*bdb9230aSGarrett D'Amore mac->lock = lock; 678*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 679*bdb9230aSGarrett D'Amore } 680*bdb9230aSGarrett D'Amore 681*bdb9230aSGarrett D'Amore int 682*bdb9230aSGarrett D'Amore mii_stop_portmon(mii_handle_t mac) 683*bdb9230aSGarrett D'Amore { 684*bdb9230aSGarrett D'Amore if (!mac->mii_linknotify || !mac->portmon_timer) 685*bdb9230aSGarrett D'Amore return (MII_STATE); 686*bdb9230aSGarrett D'Amore 687*bdb9230aSGarrett D'Amore mac->mii_linknotify = NULL; 688*bdb9230aSGarrett D'Amore mac->lock = NULL; 689*bdb9230aSGarrett D'Amore (void) untimeout(mac->portmon_timer); 690*bdb9230aSGarrett D'Amore mac->portmon_timer = 0; 691*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 692*bdb9230aSGarrett D'Amore } 693*bdb9230aSGarrett D'Amore 694*bdb9230aSGarrett D'Amore static void 695*bdb9230aSGarrett D'Amore mii_portmon(mii_handle_t mac) 696*bdb9230aSGarrett D'Amore { 697*bdb9230aSGarrett D'Amore int i; 698*bdb9230aSGarrett D'Amore enum mii_phy_state state; 699*bdb9230aSGarrett D'Amore struct phydata *phydata; 700*bdb9230aSGarrett D'Amore 701*bdb9230aSGarrett D'Amore /* 702*bdb9230aSGarrett D'Amore * There is a potential deadlock between this test and the 703*bdb9230aSGarrett D'Amore * mutex_enter 704*bdb9230aSGarrett D'Amore */ 705*bdb9230aSGarrett D'Amore if (!mac->mii_linknotify) /* Exiting */ 706*bdb9230aSGarrett D'Amore return; 707*bdb9230aSGarrett D'Amore 708*bdb9230aSGarrett D'Amore if (mac->lock) 709*bdb9230aSGarrett D'Amore mutex_enter(mac->lock); 710*bdb9230aSGarrett D'Amore 711*bdb9230aSGarrett D'Amore /* 712*bdb9230aSGarrett D'Amore * For each initialised phy, see if the link state has changed, and 713*bdb9230aSGarrett D'Amore * callback to the mac driver if it has 714*bdb9230aSGarrett D'Amore */ 715*bdb9230aSGarrett D'Amore for (i = 0; i < 32; i++) { 716*bdb9230aSGarrett D'Amore if ((phydata = mac->phys[i]) != 0) { 717*bdb9230aSGarrett D'Amore state = mii_linkup(mac, i) ? 718*bdb9230aSGarrett D'Amore phy_state_linkup : phy_state_linkdown; 719*bdb9230aSGarrett D'Amore if (state != phydata->state) { 720*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG 721*bdb9230aSGarrett D'Amore if (miidebug) 722*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "%s: PHY %d link %s", 723*bdb9230aSGarrett D'Amore ddi_get_name(mac->mii_dip), i, 724*bdb9230aSGarrett D'Amore state == phy_state_linkup ? 725*bdb9230aSGarrett D'Amore "up" : "down"); 726*bdb9230aSGarrett D'Amore #endif 727*bdb9230aSGarrett D'Amore phydata->state = state; 728*bdb9230aSGarrett D'Amore mac->mii_linknotify(mac->mii_dip, i, state); 729*bdb9230aSGarrett D'Amore } 730*bdb9230aSGarrett D'Amore } 731*bdb9230aSGarrett D'Amore } 732*bdb9230aSGarrett D'Amore /* Check the ports every 5 seconds */ 733*bdb9230aSGarrett D'Amore mac->portmon_timer = timeout((void (*)(void*))mii_portmon, (void *)mac, 734*bdb9230aSGarrett D'Amore (clock_t)(5 * drv_usectohz(1000000))); 735*bdb9230aSGarrett D'Amore if (mac->lock) 736*bdb9230aSGarrett D'Amore mutex_exit(mac->lock); 737*bdb9230aSGarrett D'Amore } 738*bdb9230aSGarrett D'Amore 739*bdb9230aSGarrett D'Amore /* 740*bdb9230aSGarrett D'Amore * Close a handle to the MII interface from a registered user 741*bdb9230aSGarrett D'Amore */ 742*bdb9230aSGarrett D'Amore 743*bdb9230aSGarrett D'Amore void 744*bdb9230aSGarrett D'Amore mii_destroy(mii_handle_t mac) 745*bdb9230aSGarrett D'Amore { 746*bdb9230aSGarrett D'Amore /* Free per-PHY information */ 747*bdb9230aSGarrett D'Amore int i; 748*bdb9230aSGarrett D'Amore 749*bdb9230aSGarrett D'Amore (void) mii_stop_portmon(mac); 750*bdb9230aSGarrett D'Amore 751*bdb9230aSGarrett D'Amore for (i = 0; i < 32; i++) 752*bdb9230aSGarrett D'Amore if (mac->phys[i]) 753*bdb9230aSGarrett D'Amore kmem_free(mac->phys[i], sizeof (struct phydata)); 754*bdb9230aSGarrett D'Amore 755*bdb9230aSGarrett D'Amore kmem_free(mac, sizeof (*mac)); 756*bdb9230aSGarrett D'Amore } 757*bdb9230aSGarrett D'Amore 758*bdb9230aSGarrett D'Amore /* 759*bdb9230aSGarrett D'Amore * Get a PHY data structure from an MII handle, and validate the common 760*bdb9230aSGarrett D'Amore * parameters to the MII functions. Used to verify parameters in most MII 761*bdb9230aSGarrett D'Amore * functions 762*bdb9230aSGarrett D'Amore */ 763*bdb9230aSGarrett D'Amore static struct phydata * 764*bdb9230aSGarrett D'Amore mii_get_valid_phydata(mii_handle_t mac, int phy) 765*bdb9230aSGarrett D'Amore { 766*bdb9230aSGarrett D'Amore if (!mac || phy > 31 || phy < 0 || !mac->phys[phy]) { 767*bdb9230aSGarrett D'Amore ASSERT(!"MII: Bad invocation"); 768*bdb9230aSGarrett D'Amore return (NULL); 769*bdb9230aSGarrett D'Amore } 770*bdb9230aSGarrett D'Amore return (mac->phys[phy]); 771*bdb9230aSGarrett D'Amore } 772*bdb9230aSGarrett D'Amore /* 773*bdb9230aSGarrett D'Amore * Device-specific routines - National Semiconductor 774*bdb9230aSGarrett D'Amore */ 775*bdb9230aSGarrett D'Amore 776*bdb9230aSGarrett D'Amore #define BIT(bit, value) ((value) & (1<<(bit))) 777*bdb9230aSGarrett D'Amore static void 778*bdb9230aSGarrett D'Amore dump_NS83840(mii_handle_t mac, int phy) 779*bdb9230aSGarrett D'Amore { 780*bdb9230aSGarrett D'Amore ushort_t reg; 781*bdb9230aSGarrett D'Amore void *dip; 782*bdb9230aSGarrett D'Amore 783*bdb9230aSGarrett D'Amore dip = mac->mii_dip; 784*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "Disconnect count: %x", 785*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, 0x12)); 786*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "False Carrier detect count: %x", 787*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, 0x13)); 788*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "Receive error count: %x", 789*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, 0x15)); 790*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "Silicon revision: %x", 791*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, 0x16)); 792*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "PCS Configuration : %x", 793*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, 0x17)); 794*bdb9230aSGarrett D'Amore 795*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "Loopback, Bypass and Receiver error mask: %x", 796*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, 0x18)); 797*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "Wired phy address: %x", 798*bdb9230aSGarrett D'Amore mac->mii_read(dip, phy, 0x19)&0xf); 799*bdb9230aSGarrett D'Amore 800*bdb9230aSGarrett D'Amore reg = mac->mii_read(dip, phy, 0x1b); 801*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "10 Base T in %s mode", 802*bdb9230aSGarrett D'Amore BIT(9, reg) ? "serial":"nibble"); 803*bdb9230aSGarrett D'Amore 804*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "%slink pulses, %sheartbeat, %s,%s squelch,jabber %s", 805*bdb9230aSGarrett D'Amore BIT(reg, 5) ? "" : "no ", 806*bdb9230aSGarrett D'Amore BIT(reg, 4) ? "" : "no ", 807*bdb9230aSGarrett D'Amore BIT(reg, 3) ? "UTP" : "STP", 808*bdb9230aSGarrett D'Amore BIT(reg, 2) ? "low" : "normal", 809*bdb9230aSGarrett D'Amore BIT(reg, 0) ? "enabled" : "disabled"); 810*bdb9230aSGarrett D'Amore } 811*bdb9230aSGarrett D'Amore 812*bdb9230aSGarrett D'Amore static int 813*bdb9230aSGarrett D'Amore getspeed_NS83840(mii_handle_t mac, int phy, int *speed, int *fulld) 814*bdb9230aSGarrett D'Amore { 815*bdb9230aSGarrett D'Amore int exten = mac->mii_read(mac->mii_dip, phy, MII_AN_EXPANSION); 816*bdb9230aSGarrett D'Amore if (exten & MII_AN_EXP_LPCANAN) { 817*bdb9230aSGarrett D'Amore /* 818*bdb9230aSGarrett D'Amore * Link partner can auto-neg, take speed from LP Ability 819*bdb9230aSGarrett D'Amore * register 820*bdb9230aSGarrett D'Amore */ 821*bdb9230aSGarrett D'Amore int lpable, anadv, mask; 822*bdb9230aSGarrett D'Amore 823*bdb9230aSGarrett D'Amore lpable = mac->mii_read(mac->mii_dip, phy, MII_AN_LPABLE); 824*bdb9230aSGarrett D'Amore anadv = mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT); 825*bdb9230aSGarrett D'Amore mask = anadv & lpable; 826*bdb9230aSGarrett D'Amore 827*bdb9230aSGarrett D'Amore if (mask & MII_ABILITY_100BASE_TX_FD) { 828*bdb9230aSGarrett D'Amore *speed = 100; 829*bdb9230aSGarrett D'Amore *fulld = 1; 830*bdb9230aSGarrett D'Amore } else if (mask & MII_ABILITY_100BASE_T4) { 831*bdb9230aSGarrett D'Amore *speed = 100; 832*bdb9230aSGarrett D'Amore *fulld = 0; 833*bdb9230aSGarrett D'Amore } else if (mask & MII_ABILITY_100BASE_TX) { 834*bdb9230aSGarrett D'Amore *speed = 100; 835*bdb9230aSGarrett D'Amore *fulld = 0; 836*bdb9230aSGarrett D'Amore } else if (mask & MII_ABILITY_10BASE_T_FD) { 837*bdb9230aSGarrett D'Amore *speed = 10; 838*bdb9230aSGarrett D'Amore *fulld = 1; 839*bdb9230aSGarrett D'Amore } else if (mask & MII_ABILITY_10BASE_T) { 840*bdb9230aSGarrett D'Amore *speed = 10; 841*bdb9230aSGarrett D'Amore *fulld = 0; 842*bdb9230aSGarrett D'Amore } 843*bdb9230aSGarrett D'Amore } else { 844*bdb9230aSGarrett D'Amore int addr = mac->mii_read(mac->mii_dip, phy, MII_83840_ADDR); 845*bdb9230aSGarrett D'Amore *speed = (addr & NS83840_ADDR_SPEED10) ? 10:100; 846*bdb9230aSGarrett D'Amore /* No fullduplex without autonegotiation on link partner */ 847*bdb9230aSGarrett D'Amore *fulld = 0; 848*bdb9230aSGarrett D'Amore } 849*bdb9230aSGarrett D'Amore return (0); 850*bdb9230aSGarrett D'Amore } 851*bdb9230aSGarrett D'Amore 852*bdb9230aSGarrett D'Amore /* 853*bdb9230aSGarrett D'Amore * Device-specific routines - INTEL 854*bdb9230aSGarrett D'Amore */ 855*bdb9230aSGarrett D'Amore 856*bdb9230aSGarrett D'Amore static int 857*bdb9230aSGarrett D'Amore getspeed_82553(mii_handle_t mac, int phy, int *speed, int *fulld) 858*bdb9230aSGarrett D'Amore { 859*bdb9230aSGarrett D'Amore int ex0 = mac->mii_read(mac->mii_dip, phy, MII_82553_EX0); 860*bdb9230aSGarrett D'Amore *fulld = (ex0 & I82553_EX0_FDUPLEX) ? 1:0; 861*bdb9230aSGarrett D'Amore *speed = (ex0 & I82553_EX0_100MB) ? 100:10; 862*bdb9230aSGarrett D'Amore return (0); 863*bdb9230aSGarrett D'Amore } 864*bdb9230aSGarrett D'Amore 865*bdb9230aSGarrett D'Amore /* 866*bdb9230aSGarrett D'Amore * Device-specific routines - ICS 867*bdb9230aSGarrett D'Amore */ 868*bdb9230aSGarrett D'Amore 869*bdb9230aSGarrett D'Amore static int 870*bdb9230aSGarrett D'Amore getspeed_ICS1890(mii_handle_t mac, int phy, int *speed, int *fulld) 871*bdb9230aSGarrett D'Amore { 872*bdb9230aSGarrett D'Amore ushort_t quickpoll = mac->mii_read(mac->mii_dip, phy, ICS_QUICKPOLL); 873*bdb9230aSGarrett D'Amore *speed = (quickpoll & ICS_QUICKPOLL_100MB) ? 100 : 10; 874*bdb9230aSGarrett D'Amore *fulld = (quickpoll & ICS_QUICKPOLL_FDUPLEX) ? 1 : 0; 875*bdb9230aSGarrett D'Amore return (0); 876*bdb9230aSGarrett D'Amore } 877*bdb9230aSGarrett D'Amore 878*bdb9230aSGarrett D'Amore static void 879*bdb9230aSGarrett D'Amore dump_ICS1890(mii_handle_t mac, int phy) 880*bdb9230aSGarrett D'Amore { 881*bdb9230aSGarrett D'Amore ushort_t quickpoll = mac->mii_read(mac->mii_dip, phy, ICS_QUICKPOLL); 882*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "QuickPoll:%x (Speed:%d FullDuplex:%c) ", 883*bdb9230aSGarrett D'Amore quickpoll, 884*bdb9230aSGarrett D'Amore quickpoll & ICS_QUICKPOLL_100MB ? 100:10, 885*bdb9230aSGarrett D'Amore quickpoll & ICS_QUICKPOLL_FDUPLEX ? 'Y' : 'N'); 886*bdb9230aSGarrett D'Amore } 887*bdb9230aSGarrett D'Amore 888*bdb9230aSGarrett D'Amore static void 889*bdb9230aSGarrett D'Amore postreset_NS83840(mii_handle_t mac, int phy) 890*bdb9230aSGarrett D'Amore { 891*bdb9230aSGarrett D'Amore ushort_t reg; 892*bdb9230aSGarrett D'Amore struct phydata *phyd = mac->phys[phy]; 893*bdb9230aSGarrett D'Amore /* 894*bdb9230aSGarrett D'Amore * As per INTEL "PRO/100B Adapter Software Technical 895*bdb9230aSGarrett D'Amore * Reference Manual", set bit 10 of MII register 23. 896*bdb9230aSGarrett D'Amore * National Semiconductor documentation shows this as 897*bdb9230aSGarrett D'Amore * "reserved, write to as zero". We also set the 898*bdb9230aSGarrett D'Amore * "f_connect" bit, also as requested by the PRO/100B 899*bdb9230aSGarrett D'Amore * doc 900*bdb9230aSGarrett D'Amore */ 901*bdb9230aSGarrett D'Amore 902*bdb9230aSGarrett D'Amore reg = mac->mii_read(mac->mii_dip, phy, 23) | (1<<10) | (1<<5); 903*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, 23, reg); 904*bdb9230aSGarrett D'Amore 905*bdb9230aSGarrett D'Amore /* 906*bdb9230aSGarrett D'Amore * Some of thses PHYs seem to reset with the wrong value in the 907*bdb9230aSGarrett D'Amore * AN advertisment register. It should containt 1e1, indicating that 908*bdb9230aSGarrett D'Amore * the device can do 802.3 10BASE-T, 10BASE-T Full duplex, 100BASE-TX, 909*bdb9230aSGarrett D'Amore * and 100 BASE-TX full duplex. Instead it seems to advertise only 910*bdb9230aSGarrett D'Amore * 100BASE-TX Full duplex. The result of this is that the device will 911*bdb9230aSGarrett D'Amore * NOT autonegotiate at all against a 10MB only or 100MB/Half duplex 912*bdb9230aSGarrett D'Amore * autonegotiating hub 913*bdb9230aSGarrett D'Amore * NEEDSWORK: 914*bdb9230aSGarrett D'Amore * There is possibly a time-dependancy here. 915*bdb9230aSGarrett D'Amore * If the autonegotiation has completed BEFORE we get to here 916*bdb9230aSGarrett D'Amore * (after the reset) then this could possibly have not effect 917*bdb9230aSGarrett D'Amore */ 918*bdb9230aSGarrett D'Amore if (!phyd->fix_speed) { 919*bdb9230aSGarrett D'Amore #ifdef MIIDEBUG 920*bdb9230aSGarrett D'Amore if (miidebug & MIICOMPAT) 921*bdb9230aSGarrett D'Amore cmn_err(CE_NOTE, "Reset value of AN_ADV reg:%x", 922*bdb9230aSGarrett D'Amore mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT)); 923*bdb9230aSGarrett D'Amore #endif 924*bdb9230aSGarrett D'Amore mac->mii_write(mac->mii_dip, phy, MII_AN_ADVERT, 0x1e1); 925*bdb9230aSGarrett D'Amore } 926*bdb9230aSGarrett D'Amore } 927*bdb9230aSGarrett D'Amore 928*bdb9230aSGarrett D'Amore void 929*bdb9230aSGarrett D'Amore postreset_ICS1890(mii_handle_t mac, int phy) 930*bdb9230aSGarrett D'Amore { 931*bdb9230aSGarrett D'Amore /* This device comes up isolated if no link is found */ 932*bdb9230aSGarrett D'Amore (void) mii_unisolate(mac, phy); 933*bdb9230aSGarrett D'Amore } 934*bdb9230aSGarrett D'Amore 935*bdb9230aSGarrett D'Amore /* 936*bdb9230aSGarrett D'Amore * generic getspeed routine 937*bdb9230aSGarrett D'Amore */ 938*bdb9230aSGarrett D'Amore static int 939*bdb9230aSGarrett D'Amore getspeed_generic(mii_handle_t mac, int phy, int *speed, int *fulld) 940*bdb9230aSGarrett D'Amore { 941*bdb9230aSGarrett D'Amore int exten = mac->mii_read(mac->mii_dip, phy, MII_AN_EXPANSION); 942*bdb9230aSGarrett D'Amore if (exten & MII_AN_EXP_LPCANAN) { 943*bdb9230aSGarrett D'Amore /* 944*bdb9230aSGarrett D'Amore * Link partner can auto-neg, take speed from LP Ability 945*bdb9230aSGarrett D'Amore * register 946*bdb9230aSGarrett D'Amore */ 947*bdb9230aSGarrett D'Amore int lpable, anadv, mask; 948*bdb9230aSGarrett D'Amore 949*bdb9230aSGarrett D'Amore lpable = mac->mii_read(mac->mii_dip, phy, MII_AN_LPABLE); 950*bdb9230aSGarrett D'Amore anadv = mac->mii_read(mac->mii_dip, phy, MII_AN_ADVERT); 951*bdb9230aSGarrett D'Amore mask = anadv & lpable; 952*bdb9230aSGarrett D'Amore 953*bdb9230aSGarrett D'Amore if (mask & MII_ABILITY_100BASE_TX_FD) { 954*bdb9230aSGarrett D'Amore *speed = 100; 955*bdb9230aSGarrett D'Amore *fulld = 1; 956*bdb9230aSGarrett D'Amore } else if (mask & MII_ABILITY_100BASE_T4) { 957*bdb9230aSGarrett D'Amore *speed = 100; 958*bdb9230aSGarrett D'Amore *fulld = 0; 959*bdb9230aSGarrett D'Amore } else if (mask & MII_ABILITY_100BASE_TX) { 960*bdb9230aSGarrett D'Amore *speed = 100; 961*bdb9230aSGarrett D'Amore *fulld = 0; 962*bdb9230aSGarrett D'Amore } else if (mask & MII_ABILITY_10BASE_T_FD) { 963*bdb9230aSGarrett D'Amore *speed = 10; 964*bdb9230aSGarrett D'Amore *fulld = 1; 965*bdb9230aSGarrett D'Amore } else if (mask & MII_ABILITY_10BASE_T) { 966*bdb9230aSGarrett D'Amore *speed = 10; 967*bdb9230aSGarrett D'Amore *fulld = 0; 968*bdb9230aSGarrett D'Amore } 969*bdb9230aSGarrett D'Amore } else { 970*bdb9230aSGarrett D'Amore /* 971*bdb9230aSGarrett D'Amore * Link partner cannot auto-neg, it would be nice if we 972*bdb9230aSGarrett D'Amore * could figure out what the device selected. (NWay?) 973*bdb9230aSGarrett D'Amore */ 974*bdb9230aSGarrett D'Amore *speed = 0; 975*bdb9230aSGarrett D'Amore *fulld = 0; 976*bdb9230aSGarrett D'Amore } 977*bdb9230aSGarrett D'Amore return (MII_SUCCESS); 978*bdb9230aSGarrett D'Amore } 979