dcphy.c revision 1.13
1/* $OpenBSD: dcphy.c,v 1.13 2005/01/28 18:27:55 brad Exp $ */ 2 3/* 4 * Copyright (c) 1997, 1998, 1999 5 * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Bill Paul. 18 * 4. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32 * THE POSSIBILITY OF SUCH DAMAGE. 33 * 34 * $FreeBSD: src/sys/dev/mii/dcphy.c,v 1.6 2000/10/05 17:36:14 wpaul Exp $ 35 */ 36 37/* 38 * Pseudo-driver for internal NWAY support on DEC 21143 and workalike 39 * controllers. Technically we're abusing the miibus code to handle 40 * media selection and NWAY support here since there is no MII 41 * interface. However the logical operations are roughly the same, 42 * and the alternative is to create a fake MII interface in the driver, 43 * which is harder to do. 44 */ 45 46#include <sys/param.h> 47#include <sys/device.h> 48#include <sys/systm.h> 49#include <sys/kernel.h> 50#include <sys/socket.h> 51#include <sys/errno.h> 52#include <sys/socket.h> 53 54#include <net/if.h> 55#include <net/if_dl.h> 56#include <net/if_types.h> 57#include <net/if_media.h> 58#include <netinet/in.h> 59#include <netinet/if_ether.h> 60 61#include <dev/mii/mii.h> 62#include <dev/mii/miivar.h> 63#include <dev/mii/miidevs.h> 64 65#include <machine/bus.h> 66 67#include <dev/pci/pcivar.h> 68 69#include <dev/ic/dcreg.h> 70 71#define DC_SETBIT(sc, reg, x) \ 72 CSR_WRITE_4(sc, reg, \ 73 CSR_READ_4(sc, reg) | x) 74 75#define DC_CLRBIT(sc, reg, x) \ 76 CSR_WRITE_4(sc, reg, \ 77 CSR_READ_4(sc, reg) & ~x) 78 79#define MIIF_AUTOTIMEOUT 0x0004 80 81/* 82 * This is the subsystem ID for the built-in 21143 ethernet 83 * in several Compaq Presario systems. Apparently these are 84 * 10Mbps only, so we need to treat them specially. 85 */ 86#define COMPAQ_PRESARIO_ID 0xb0bb0e11 87 88int dcphy_match(struct device *, void *, void *); 89void dcphy_attach(struct device *, struct device *, void *); 90 91struct cfattach dcphy_ca = { 92 sizeof(struct mii_softc), dcphy_match, dcphy_attach, mii_phy_detach, 93 mii_phy_activate 94}; 95 96struct cfdriver dcphy_cd = { 97 NULL, "dcphy", DV_DULL 98}; 99 100int dcphy_service(struct mii_softc *, struct mii_data *, int); 101void dcphy_status(struct mii_softc *); 102int dcphy_auto(struct mii_softc *, int); 103void dcphy_reset(struct mii_softc *); 104 105const struct mii_phy_funcs dcphy_funcs = { 106 dcphy_service, dcphy_status, dcphy_reset, 107}; 108 109int 110dcphy_match(struct device *parent, void *match, void *aux) 111{ 112 struct mii_attach_args *ma = aux; 113 114 /* 115 * The dc driver will report the 21143 vendor and device 116 * ID to let us know that it wants us to attach. 117 */ 118 if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC && 119 MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC) 120 return (10); 121 122 return (0); 123} 124 125void 126dcphy_attach(struct device *parent, struct device *self, void *aux) 127{ 128 struct mii_softc *sc = (struct mii_softc *)self; 129 struct mii_attach_args *ma = aux; 130 struct mii_data *mii = ma->mii_data; 131 struct dc_softc *dc_sc; 132 133 printf(": internal PHY\n"); 134 sc->mii_inst = mii->mii_instance; 135 sc->mii_phy = ma->mii_phyno; 136 sc->mii_funcs = &dcphy_funcs; 137 sc->mii_pdata = mii; 138 sc->mii_flags = ma->mii_flags; 139 140 sc->mii_flags |= MIIF_NOISOLATE; 141 142 mii->mii_instance++; 143 144 dc_sc = mii->mii_ifp->if_softc; 145 CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0); 146 CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0); 147 148#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 149 150 switch(dc_sc->dc_csid) { 151 case COMPAQ_PRESARIO_ID: 152 /* Example of how to only allow 10Mbps modes. */ 153 sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; 154 break; 155 default: 156 if (dc_sc->dc_pmode == DC_PMODE_SIA) { 157 sc->mii_capabilities = 158 BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; 159 } else { 160 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, 161 sc->mii_inst), BMCR_LOOP|BMCR_S100); 162 163 sc->mii_capabilities = 164 BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| 165 BMSR_10TFDX|BMSR_10THDX; 166 } 167 break; 168 } 169 170 if (dc_sc->dc_type == DC_TYPE_21145) 171 sc->mii_capabilities = BMSR_10THDX; 172 173 sc->mii_capabilities &= ma->mii_capmask; 174 if (sc->mii_capabilities & BMSR_MEDIAMASK) 175 mii_phy_add_media(sc); 176#undef ADD 177} 178 179int 180dcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 181{ 182 struct dc_softc *dc_sc; 183 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 184 int reg; 185 u_int32_t mode; 186 187 if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0) 188 return (ENXIO); 189 190 dc_sc = mii->mii_ifp->if_softc; 191 192 switch (cmd) { 193 case MII_POLLSTAT: 194 /* 195 * If we're not polling our PHY instance, just return. 196 */ 197 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 198 return (0); 199 } 200 break; 201 202 case MII_MEDIACHG: 203 /* 204 * If the media indicates a different PHY instance, 205 * isolate ourselves. 206 */ 207 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 208 return (0); 209 } 210 211 /* 212 * If the interface is not up, don't do anything. 213 */ 214 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 215 break; 216 217 sc->mii_flags = 0; 218 mii->mii_media_active = IFM_NONE; 219 mode = CSR_READ_4(dc_sc, DC_NETCFG); 220 mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL| 221 DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL); 222 223 switch (IFM_SUBTYPE(ife->ifm_media)) { 224 case IFM_AUTO: 225 /*PHY_RESET(sc);*/ 226 sc->mii_flags &= ~MIIF_DOINGAUTO; 227 (void) dcphy_auto(sc, 0); 228 break; 229 case IFM_100_T4: 230 /* 231 * XXX Not supported as a manual setting right now. 232 */ 233 return (EINVAL); 234 case IFM_100_TX: 235 PHY_RESET(sc); 236 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 237 mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS| 238 DC_NETCFG_SCRAMBLER; 239 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 240 mode |= DC_NETCFG_FULLDUPLEX; 241 else 242 mode &= ~DC_NETCFG_FULLDUPLEX; 243 CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 244 break; 245 case IFM_10_T: 246 DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 247 DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF); 248 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 249 DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D); 250 else 251 DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F); 252 DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 253 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 254 mode &= ~DC_NETCFG_PORTSEL; 255 mode |= DC_NETCFG_SPEEDSEL; 256 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 257 mode |= DC_NETCFG_FULLDUPLEX; 258 else 259 mode &= ~DC_NETCFG_FULLDUPLEX; 260 CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 261 break; 262 default: 263 return(EINVAL); 264 } 265 break; 266 267 case MII_TICK: 268 /* 269 * If we're not currently selected, just return. 270 */ 271 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 272 return (0); 273 274 /* 275 * Is the interface even up? 276 */ 277 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 278 return (0); 279 280 /* 281 * Only used for autonegotiation. 282 */ 283 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 284 break; 285 286 reg = CSR_READ_4(dc_sc, DC_10BTSTAT); 287 if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 288 break; 289 290 /* 291 * Only retry autonegotiation every 5 seconds. 292 * 293 * Otherwise, fall through to calling dcphy_status() 294 * since real Intel 21143 chips don't show valid link 295 * status until autonegotiation is switched off, and 296 * that only happens in dcphy_status(). Without this, 297 * successful autonegotation is never recognised on 298 * these chips. 299 */ 300 if (++sc->mii_ticks != 50) 301 break; 302 303 sc->mii_ticks = 0; 304 sc->mii_flags &= ~MIIF_DOINGAUTO; 305 dcphy_auto(sc, 0); 306 307 break; 308 } 309 310 /* Update the media status. */ 311 mii_phy_status(sc); 312 313 /* Callback if something changed. */ 314 mii_phy_update(sc, cmd); 315 return (0); 316} 317 318void 319dcphy_status(struct mii_softc *sc) 320{ 321 struct mii_data *mii = sc->mii_pdata; 322 int reg, anlpar, tstat = 0; 323 struct dc_softc *dc_sc; 324 325 dc_sc = mii->mii_ifp->if_softc; 326 327 mii->mii_media_status = IFM_AVALID; 328 mii->mii_media_active = IFM_ETHER; 329 330 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 331 return; 332 333 reg = CSR_READ_4(dc_sc, DC_10BTSTAT); 334 if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 335 mii->mii_media_status |= IFM_ACTIVE; 336 337 if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) { 338 /* Erg, still trying, I guess... */ 339 tstat = CSR_READ_4(dc_sc, DC_10BTSTAT); 340 if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) { 341 if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) && 342 (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE) 343 goto skip; 344 mii->mii_media_active |= IFM_NONE; 345 return; 346 } 347 348 if (tstat & DC_TSTAT_LP_CAN_NWAY) { 349 anlpar = tstat >> 16; 350 if (anlpar & ANLPAR_T4 && 351 sc->mii_capabilities & BMSR_100TXHDX) 352 mii->mii_media_active |= IFM_100_T4; 353 else if (anlpar & ANLPAR_TX_FD && 354 sc->mii_capabilities & BMSR_100TXFDX) 355 mii->mii_media_active |= IFM_100_TX|IFM_FDX; 356 else if (anlpar & ANLPAR_TX && 357 sc->mii_capabilities & BMSR_100TXHDX) 358 mii->mii_media_active |= IFM_100_TX; 359 else if (anlpar & ANLPAR_10_FD) 360 mii->mii_media_active |= IFM_10_T|IFM_FDX; 361 else if (anlpar & ANLPAR_10) 362 mii->mii_media_active |= IFM_10_T; 363 else 364 mii->mii_media_active |= IFM_NONE; 365 if (DC_IS_INTEL(dc_sc)) 366 DC_CLRBIT(dc_sc, DC_10BTCTRL, 367 DC_TCTL_AUTONEGENBL); 368 return; 369 } 370 /* 371 * If the other side doesn't support NWAY, then the 372 * best we can do is determine if we have a 10Mbps or 373 * 100Mbps link. There's no way to know if the link 374 * is full or half duplex, so we default to half duplex 375 * and hope that the user is clever enough to manually 376 * change the media settings if we're wrong. 377 */ 378 if (!(reg & DC_TSTAT_LS100)) 379 mii->mii_media_active |= IFM_100_TX; 380 else if (!(reg & DC_TSTAT_LS10)) 381 mii->mii_media_active |= IFM_10_T; 382 else 383 mii->mii_media_active |= IFM_NONE; 384 if (DC_IS_INTEL(dc_sc)) 385 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 386 return; 387 } 388 389skip: 390 391 if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL) 392 mii->mii_media_active |= IFM_10_T; 393 else 394 mii->mii_media_active |= IFM_100_TX; 395 if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) 396 mii->mii_media_active |= IFM_FDX; 397 398 return; 399} 400 401int 402dcphy_auto(struct mii_softc *mii, int waitfor) 403{ 404 int i; 405 struct dc_softc *sc; 406 407 sc = mii->mii_pdata->mii_ifp->if_softc; 408 409 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 410 DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); 411 DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); 412 DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 413 if (mii->mii_capabilities & BMSR_100TXHDX) 414 CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF); 415 else 416 CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF); 417 DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 418 DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 419 DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE); 420 } 421 422 if (waitfor) { 423 /* Wait 500ms for it to complete. */ 424 for (i = 0; i < 500; i++) { 425 if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) 426 == DC_ASTAT_AUTONEGCMP) 427 return(0); 428 DELAY(1000); 429 } 430 /* 431 * Don't need to worry about clearing MIIF_DOINGAUTO. 432 * If that's set, a timeout is pending, and it will 433 * clear the flag. 434 */ 435 return(EIO); 436 } 437 438 /* 439 * Just let it finish asynchronously. This is for the benefit of 440 * the tick handler driving autonegotiation. Don't want 500ms 441 * delays all the time while the system is running! 442 */ 443 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) 444 mii->mii_flags |= MIIF_DOINGAUTO; 445 446 return(EJUSTRETURN); 447} 448 449void 450dcphy_reset(struct mii_softc *mii) 451{ 452 struct dc_softc *sc; 453 454 sc = mii->mii_pdata->mii_ifp->if_softc; 455 456 DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 457 DELAY(1000); 458 DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 459 460 return; 461} 462