dcphy.c revision 1.7
1169695Skan/* $OpenBSD: dcphy.c,v 1.7 2002/05/04 11:30:06 fgsch Exp $ */ 2169695Skan 3169695Skan/* 4169695Skan * Copyright (c) 1997, 1998, 1999 5169695Skan * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. 6169695Skan * 7169695Skan * Redistribution and use in source and binary forms, with or without 8169695Skan * modification, are permitted provided that the following conditions 9169695Skan * are met: 10169695Skan * 1. Redistributions of source code must retain the above copyright 11169695Skan * notice, this list of conditions and the following disclaimer. 12169695Skan * 2. Redistributions in binary form must reproduce the above copyright 13169695Skan * notice, this list of conditions and the following disclaimer in the 14169695Skan * documentation and/or other materials provided with the distribution. 15169695Skan * 3. All advertising materials mentioning features or use of this software 16169695Skan * must display the following acknowledgement: 17169695Skan * This product includes software developed by Bill Paul. 18169695Skan * 4. Neither the name of the author nor the names of any co-contributors 19169695Skan * may be used to endorse or promote products derived from this software 20169695Skan * without specific prior written permission. 21169695Skan * 22169695Skan * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 23169695Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24169695Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25169695Skan * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 26169695Skan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27169695Skan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28169695Skan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29169695Skan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30169695Skan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31169695Skan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32169695Skan * THE POSSIBILITY OF SUCH DAMAGE. 33169695Skan * 34169695Skan * $FreeBSD: src/sys/dev/mii/dcphy.c,v 1.6 2000/10/05 17:36:14 wpaul Exp $ 35169695Skan */ 36169695Skan 37169695Skan/* 38169695Skan * Pseudo-driver for internal NWAY support on DEC 21143 and workalike 39169695Skan * controllers. Technically we're abusing the miibus code to handle 40169695Skan * media selection and NWAY support here since there is no MII 41169695Skan * interface. However the logical operations are roughly the same, 42169695Skan * and the alternative is to create a fake MII interface in the driver, 43169695Skan * which is harder to do. 44169695Skan */ 45169695Skan 46169695Skan#include <sys/param.h> 47169695Skan#include <sys/device.h> 48169695Skan#include <sys/systm.h> 49169695Skan#include <sys/kernel.h> 50169695Skan#include <sys/socket.h> 51169695Skan#include <sys/errno.h> 52169695Skan#include <sys/socket.h> 53169695Skan 54169695Skan#include <net/if.h> 55169695Skan#include <net/if_dl.h> 56169695Skan#include <net/if_types.h> 57169695Skan#include <net/if_media.h> 58169695Skan#include <netinet/in.h> 59169695Skan#include <netinet/if_ether.h> 60169695Skan 61283010Spfg#include <dev/mii/mii.h> 62169695Skan#include <dev/mii/miivar.h> 63169695Skan#include <dev/mii/miidevs.h> 64169695Skan 65283010Spfg#include <machine/bus.h> 66169695Skan 67169695Skan#include <dev/pci/pcivar.h> 68169695Skan 69169695Skan#include <dev/ic/dcreg.h> 70169695Skan 71169695Skan#define DC_SETBIT(sc, reg, x) \ 72169695Skan CSR_WRITE_4(sc, reg, \ 73169695Skan CSR_READ_4(sc, reg) | x) 74169695Skan 75169695Skan#define DC_CLRBIT(sc, reg, x) \ 76169695Skan CSR_WRITE_4(sc, reg, \ 77169695Skan CSR_READ_4(sc, reg) & ~x) 78169695Skan 79169695Skan#define MIIF_AUTOTIMEOUT 0x0004 80169695Skan 81169695Skan/* 82169695Skan * This is the subsystem ID for the built-in 21143 ethernet 83169695Skan * in several Compaq Presario systems. Apparently these are 84169695Skan * 10Mbps only, so we need to treat them specially. 85169695Skan */ 86169695Skan#define COMPAQ_PRESARIO_ID 0xb0bb0e11 87169695Skan 88169695Skanint dcphy_match(struct device *, void *, void *); 89169695Skanvoid dcphy_attach(struct device *, struct device *, void *); 90169695Skan 91169695Skanstruct cfattach dcphy_ca = { 92169695Skan sizeof(struct mii_softc), dcphy_match, dcphy_attach, mii_phy_detach, 93169695Skan mii_phy_activate 94169695Skan}; 95169695Skan 96169695Skanstruct cfdriver dcphy_cd = { 97169695Skan NULL, "dcphy", DV_DULL 98169695Skan}; 99169695Skan 100169695Skanint dcphy_service(struct mii_softc *, struct mii_data *, int); 101169695Skanvoid dcphy_status(struct mii_softc *); 102169695Skanint dcphy_auto(struct mii_softc *, int); 103169695Skanvoid dcphy_reset(struct mii_softc *); 104169695Skan 105169695Skanint 106169695Skandcphy_match(parent, match, aux) 107169695Skan struct device *parent; 108169695Skan void *match, *aux; 109169695Skan{ 110169695Skan struct mii_attach_args *ma = aux; 111169695Skan 112169695Skan /* 113169695Skan * The dc driver will report the 21143 vendor and device 114169695Skan * ID to let us know that it wants us to attach. 115169695Skan */ 116169695Skan if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC && 117169695Skan MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC) 118169695Skan return (10); 119169695Skan 120169695Skan return (0); 121169695Skan} 122169695Skan 123169695Skanvoid 124169695Skandcphy_attach(parent, self, aux) 125169695Skan struct device *parent; 126169695Skan struct device *self; 127169695Skan void *aux; 128169695Skan{ 129169695Skan struct mii_softc *sc = (struct mii_softc *)self; 130169695Skan struct mii_attach_args *ma = aux; 131169695Skan struct mii_data *mii = ma->mii_data; 132283010Spfg struct dc_softc *dc_sc; 133169695Skan 134169695Skan printf(": internal PHY\n"); 135169695Skan sc->mii_inst = mii->mii_instance; 136169695Skan sc->mii_phy = ma->mii_phyno; 137169695Skan sc->mii_service = dcphy_service; 138169695Skan sc->mii_status = dcphy_status; 139169695Skan sc->mii_pdata = mii; 140169695Skan sc->mii_flags = mii->mii_flags; 141169695Skan 142169695Skan sc->mii_flags |= MIIF_NOISOLATE; 143169695Skan mii->mii_instance++; 144169695Skan 145169695Skan dc_sc = mii->mii_ifp->if_softc; 146169695Skan CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0); 147169695Skan CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0); 148169695Skan 149169695Skan#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 150169695Skan 151169695Skan switch(dc_sc->dc_csid) { 152169695Skan case COMPAQ_PRESARIO_ID: 153169695Skan /* Example of how to only allow 10Mbps modes. */ 154169695Skan sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; 155169695Skan break; 156169695Skan default: 157283010Spfg if (dc_sc->dc_pmode == DC_PMODE_SIA) { 158169695Skan sc->mii_capabilities = 159169695Skan BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; 160169695Skan } else { 161169695Skan ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, 162169695Skan sc->mii_inst), BMCR_LOOP|BMCR_S100); 163169695Skan 164169695Skan sc->mii_capabilities = 165169695Skan BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| 166169695Skan BMSR_10TFDX|BMSR_10THDX; 167169695Skan } 168169695Skan break; 169169695Skan } 170169695Skan 171169695Skan sc->mii_capabilities &= ma->mii_capmask; 172169695Skan if (sc->mii_capabilities & BMSR_MEDIAMASK) 173169695Skan mii_phy_add_media(sc); 174169695Skan#undef ADD 175169695Skan} 176169695Skan 177169695Skanint 178169695Skandcphy_service(sc, mii, cmd) 179169695Skan struct mii_softc *sc; 180169695Skan struct mii_data *mii; 181169695Skan int cmd; 182169695Skan{ 183169695Skan struct dc_softc *dc_sc; 184169695Skan struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 185169695Skan int reg; 186169695Skan u_int32_t mode; 187169695Skan 188169695Skan if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0) 189169695Skan return (ENXIO); 190169695Skan 191169695Skan dc_sc = mii->mii_ifp->if_softc; 192283010Spfg 193283010Spfg switch (cmd) { 194283010Spfg case MII_POLLSTAT: 195283010Spfg /* 196283010Spfg * If we're not polling our PHY instance, just return. 197283010Spfg */ 198283010Spfg if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 199283010Spfg return (0); 200283010Spfg } 201283010Spfg break; 202283010Spfg 203283010Spfg case MII_MEDIACHG: 204283010Spfg /* 205283010Spfg * If the media indicates a different PHY instance, 206283010Spfg * isolate ourselves. 207283010Spfg */ 208283010Spfg if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 209283010Spfg return (0); 210283010Spfg } 211283010Spfg 212283010Spfg /* 213169695Skan * If the interface is not up, don't do anything. 214169695Skan */ 215169695Skan if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 216169695Skan break; 217169695Skan 218169695Skan sc->mii_flags = 0; 219169695Skan mii->mii_media_active = IFM_NONE; 220169695Skan mode = CSR_READ_4(dc_sc, DC_NETCFG); 221169695Skan mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL| 222169695Skan DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL); 223169695Skan 224169695Skan switch (IFM_SUBTYPE(ife->ifm_media)) { 225169695Skan case IFM_AUTO: 226169695Skan /*dcphy_reset(sc);*/ 227169695Skan sc->mii_flags &= ~MIIF_DOINGAUTO; 228169695Skan (void) dcphy_auto(sc, 0); 229169695Skan break; 230169695Skan case IFM_100_T4: 231169695Skan /* 232169695Skan * XXX Not supported as a manual setting right now. 233169695Skan */ 234169695Skan return (EINVAL); 235169695Skan case IFM_100_TX: 236169695Skan dcphy_reset(sc); 237169695Skan DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 238169695Skan mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS| 239169695Skan DC_NETCFG_SCRAMBLER; 240169695Skan if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 241169695Skan mode |= DC_NETCFG_FULLDUPLEX; 242169695Skan else 243169695Skan mode &= ~DC_NETCFG_FULLDUPLEX; 244169695Skan CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 245169695Skan break; 246169695Skan case IFM_10_T: 247169695Skan DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 248169695Skan DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF); 249169695Skan if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 250169695Skan DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D); 251169695Skan else 252169695Skan DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F); 253169695Skan DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 254169695Skan DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 255169695Skan mode &= ~DC_NETCFG_PORTSEL; 256169695Skan mode |= DC_NETCFG_SPEEDSEL; 257169695Skan if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 258169695Skan mode |= DC_NETCFG_FULLDUPLEX; 259169695Skan else 260169695Skan mode &= ~DC_NETCFG_FULLDUPLEX; 261169695Skan CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 262169695Skan break; 263169695Skan default: 264169695Skan return(EINVAL); 265169695Skan break; 266169695Skan } 267169695Skan break; 268169695Skan 269169695Skan case MII_TICK: 270169695Skan /* 271169695Skan * If we're not currently selected, just return. 272169695Skan */ 273169695Skan if (IFM_INST(ife->ifm_media) != sc->mii_inst) 274169695Skan return (0); 275169695Skan 276169695Skan /* 277169695Skan * Only used for autonegotiation. 278169695Skan */ 279169695Skan if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 280169695Skan return (0); 281169695Skan 282169695Skan /* 283169695Skan * Is the interface even up? 284169695Skan */ 285169695Skan if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 286169695Skan return (0); 287169695Skan 288169695Skan reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & 289169695Skan (DC_TSTAT_LS10|DC_TSTAT_LS100); 290169695Skan 291169695Skan if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 292169695Skan return (0); 293169695Skan 294169695Skan /* 295169695Skan * Only retry autonegotiation every 5 seconds. 296169695Skan */ 297169695Skan if (++sc->mii_ticks != 50) 298169695Skan return (0); 299169695Skan 300169695Skan sc->mii_ticks = 0; 301169695Skan /*if (DC_IS_INTEL(dc_sc))*/ 302169695Skan sc->mii_flags &= ~MIIF_DOINGAUTO; 303169695Skan dcphy_auto(sc, 0); 304169695Skan 305169695Skan break; 306169695Skan } 307169695Skan 308169695Skan /* Update the media status. */ 309169695Skan mii_phy_status(sc); 310169695Skan 311 /* Callback if something changed. */ 312 mii_phy_update(sc, cmd); 313 return (0); 314} 315 316void 317dcphy_status(sc) 318 struct mii_softc *sc; 319{ 320 struct mii_data *mii = sc->mii_pdata; 321 int reg, anlpar, tstat = 0; 322 struct dc_softc *dc_sc; 323 324 dc_sc = mii->mii_ifp->if_softc; 325 326 mii->mii_media_status = IFM_AVALID; 327 mii->mii_media_active = IFM_ETHER; 328 329 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 330 return; 331 332 reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & 333 (DC_TSTAT_LS10|DC_TSTAT_LS100); 334 335 if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 336 mii->mii_media_status |= IFM_ACTIVE; 337 338 if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) { 339 /* Erg, still trying, I guess... */ 340 tstat = CSR_READ_4(dc_sc, DC_10BTSTAT); 341 if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) { 342 if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) && 343 (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE) 344 goto skip; 345 mii->mii_media_active |= IFM_NONE; 346 return; 347 } 348 349 if (tstat & DC_TSTAT_LP_CAN_NWAY) { 350 anlpar = tstat >> 16; 351 if (anlpar & ANLPAR_T4 && 352 sc->mii_capabilities & BMSR_100TXHDX) 353 mii->mii_media_active |= IFM_100_T4; 354 else if (anlpar & ANLPAR_TX_FD && 355 sc->mii_capabilities & BMSR_100TXFDX) 356 mii->mii_media_active |= IFM_100_TX|IFM_FDX; 357 else if (anlpar & ANLPAR_TX && 358 sc->mii_capabilities & BMSR_100TXHDX) 359 mii->mii_media_active |= IFM_100_TX; 360 else if (anlpar & ANLPAR_10_FD) 361 mii->mii_media_active |= IFM_10_T|IFM_FDX; 362 else if (anlpar & ANLPAR_10) 363 mii->mii_media_active |= IFM_10_T; 364 else 365 mii->mii_media_active |= IFM_NONE; 366 if (DC_IS_INTEL(dc_sc)) 367 DC_CLRBIT(dc_sc, DC_10BTCTRL, 368 DC_TCTL_AUTONEGENBL); 369 return; 370 } 371 /* 372 * If the other side doesn't support NWAY, then the 373 * best we can do is determine if we have a 10Mbps or 374 * 100Mbps link. There's no way to know if the link 375 * is full or half duplex, so we default to half duplex 376 * and hope that the user is clever enough to manually 377 * change the media settings if we're wrong. 378 */ 379 if (!(reg & DC_TSTAT_LS100)) 380 mii->mii_media_active |= IFM_100_TX; 381 else if (!(reg & DC_TSTAT_LS10)) 382 mii->mii_media_active |= IFM_10_T; 383 else 384 mii->mii_media_active |= IFM_NONE; 385 if (DC_IS_INTEL(dc_sc)) 386 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 387 return; 388 } 389 390skip: 391 392 if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL) 393 mii->mii_media_active |= IFM_10_T; 394 else 395 mii->mii_media_active |= IFM_100_TX; 396 if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) 397 mii->mii_media_active |= IFM_FDX; 398 399 return; 400} 401 402int 403dcphy_auto(mii, waitfor) 404 struct mii_softc *mii; 405 int waitfor; 406{ 407 int i; 408 struct dc_softc *sc; 409 410 sc = mii->mii_pdata->mii_ifp->if_softc; 411 412 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 413 DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); 414 DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); 415 DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 416 if (mii->mii_capabilities & BMSR_100TXHDX) 417 CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF); 418 else 419 CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF); 420 DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 421 DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 422 DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE); 423 } 424 425 if (waitfor) { 426 /* Wait 500ms for it to complete. */ 427 for (i = 0; i < 500; i++) { 428 if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) 429 == DC_ASTAT_AUTONEGCMP) 430 return(0); 431 DELAY(1000); 432 } 433 /* 434 * Don't need to worry about clearing MIIF_DOINGAUTO. 435 * If that's set, a timeout is pending, and it will 436 * clear the flag. 437 */ 438 return(EIO); 439 } 440 441 /* 442 * Just let it finish asynchronously. This is for the benefit of 443 * the tick handler driving autonegotiation. Don't want 500ms 444 * delays all the time while the system is running! 445 */ 446 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) 447 mii->mii_flags |= MIIF_DOINGAUTO; 448 449 return(EJUSTRETURN); 450} 451 452void 453dcphy_reset(mii) 454 struct mii_softc *mii; 455{ 456 struct dc_softc *sc; 457 458 sc = mii->mii_pdata->mii_ifp->if_softc; 459 460 DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 461 DELAY(1000); 462 DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 463 464 return; 465} 466