dcphy.c revision 1.1
1/* $OpenBSD: dcphy.c,v 1.1 2000/01/09 01:15:16 jason 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.2 1999/12/13 21:45:13 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/malloc.h> 51#include <sys/socket.h> 52#include <sys/errno.h> 53#include <sys/socket.h> 54 55#include <net/if.h> 56#include <net/if_dl.h> 57#include <net/if_types.h> 58#include <net/if_media.h> 59#include <netinet/in.h> 60#include <netinet/if_ether.h> 61 62#include <dev/mii/mii.h> 63#include <dev/mii/miivar.h> 64#include <dev/mii/miidevs.h> 65 66#include <machine/bus.h> 67 68#include <dev/pci/pcivar.h> 69 70#include <dev/pci/if_dcreg.h> 71 72#define DC_SETBIT(sc, reg, x) \ 73 CSR_WRITE_4(sc, reg, \ 74 CSR_READ_4(sc, reg) | x) 75 76#define DC_CLRBIT(sc, reg, x) \ 77 CSR_WRITE_4(sc, reg, \ 78 CSR_READ_4(sc, reg) & ~x) 79 80#define MIIF_AUTOTIMEOUT 0x0004 81 82/* 83 * This is the subsystem ID for the built-in 21143 ethernet 84 * in several Compaq Presario systems. Apparently these are 85 * 10Mbps only, so we need to treat them specially. 86 */ 87#define COMPAQ_PRESARIO_ID 0xb0bb0e11 88 89int dcphy_match __P((struct device *, void *, void *)); 90void dcphy_attach __P((struct device *, struct device *, void *)); 91 92struct cfattach dcphy_ca = { 93 sizeof(struct mii_softc), dcphy_match, dcphy_attach 94}; 95 96struct cfdriver dcphy_cd = { 97 NULL, "dcphy", DV_DULL 98}; 99 100int dcphy_service __P((struct mii_softc *, struct mii_data *, int)); 101void dcphy_status __P((struct mii_softc *)); 102int dcphy_auto __P((struct mii_softc *, int)); 103void dcphy_reset __P((struct mii_softc *)); 104 105int 106dcphy_match(parent, match, aux) 107 struct device *parent; 108 void *match, *aux; 109{ 110 struct mii_attach_args *ma = aux; 111 112 /* 113 * The dc driver will report the 21143 vendor and device 114 * ID to let us know that it wants us to attach. 115 */ 116 if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC && 117 MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC) 118 return (10); 119 120 return (0); 121} 122 123void 124dcphy_attach(parent, self, aux) 125 struct device *parent; 126 struct device *self; 127 void *aux; 128{ 129 struct mii_softc *sc = (struct mii_softc *)self; 130 struct mii_attach_args *ma = aux; 131 struct mii_data *mii = ma->mii_data; 132 struct dc_softc *dc_sc; 133 134 printf(": internal PHY\n"); 135 sc->mii_inst = mii->mii_instance; 136 sc->mii_phy = ma->mii_phyno; 137 sc->mii_service = dcphy_service; 138 sc->mii_pdata = mii; 139 140 sc->mii_flags |= MIIF_NOISOLATE; 141 mii->mii_instance++; 142 143 dc_sc = mii->mii_ifp->if_softc; 144 CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0); 145 CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0); 146 147#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 148 149 switch(dc_sc->dc_csid) { 150 case COMPAQ_PRESARIO_ID: 151 /* Example of how to only allow 10Mbps modes. */ 152 sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; 153 break; 154 default: 155 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, 156 sc->mii_inst), BMCR_LOOP|BMCR_S100); 157 158 sc->mii_capabilities = 159 BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| 160 BMSR_10TFDX|BMSR_10THDX; 161 break; 162 } 163 164 sc->mii_capabilities &= ma->mii_capmask; 165 if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) 166 printf("no media present"); 167 else 168 mii_add_media(sc); 169#undef ADD 170} 171 172int 173dcphy_service(sc, mii, cmd) 174 struct mii_softc *sc; 175 struct mii_data *mii; 176 int cmd; 177{ 178 struct dc_softc *dc_sc; 179 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 180 int reg; 181 u_int32_t mode; 182 183 dc_sc = mii->mii_ifp->if_softc; 184 185 switch (cmd) { 186 case MII_POLLSTAT: 187 /* 188 * If we're not polling our PHY instance, just return. 189 */ 190 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 191 return (0); 192 } 193 break; 194 195 case MII_MEDIACHG: 196 /* 197 * If the media indicates a different PHY instance, 198 * isolate ourselves. 199 */ 200 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 201 return (0); 202 } 203 204 /* 205 * If the interface is not up, don't do anything. 206 */ 207 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 208 break; 209 210 sc->mii_flags = 0; 211 mii->mii_media_active = IFM_NONE; 212 mode = CSR_READ_4(dc_sc, DC_NETCFG); 213 mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL| 214 DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL); 215 216 switch (IFM_SUBTYPE(ife->ifm_media)) { 217 case IFM_AUTO: 218 /*dcphy_reset(sc);*/ 219 (void) dcphy_auto(sc, 0); 220 break; 221 case IFM_100_T4: 222 /* 223 * XXX Not supported as a manual setting right now. 224 */ 225 return (EINVAL); 226 case IFM_100_TX: 227 dcphy_reset(sc); 228 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 229 mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS| 230 DC_NETCFG_SCRAMBLER; 231 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 232 mode |= DC_NETCFG_FULLDUPLEX; 233 else 234 mode &= ~DC_NETCFG_FULLDUPLEX; 235 CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 236 break; 237 case IFM_10_T: 238 DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 239 DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF); 240 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 241 DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D); 242 else 243 DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F); 244 DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 245 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 246 mode &= ~DC_NETCFG_PORTSEL; 247 mode |= DC_NETCFG_SPEEDSEL; 248 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 249 mode |= DC_NETCFG_FULLDUPLEX; 250 else 251 mode &= ~DC_NETCFG_FULLDUPLEX; 252 CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 253 break; 254 default: 255 return(EINVAL); 256 break; 257 } 258 break; 259 260 case MII_TICK: 261 /* 262 * If we're not currently selected, just return. 263 */ 264 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 265 return (0); 266 267 /* 268 * Only used for autonegotiation. 269 */ 270 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 271 return (0); 272 273 /* 274 * Is the interface even up? 275 */ 276 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 277 return (0); 278 279 if (sc->mii_flags & MIIF_DOINGAUTO) { 280 if (++sc->mii_ticks != 5) 281 return(0); 282 else { 283 sc->mii_ticks = 0; 284 sc->mii_flags &= ~MIIF_DOINGAUTO; 285 sc->mii_flags |= MIIF_AUTOTIMEOUT; 286 } 287 } 288 289 sc->mii_flags &= ~MIIF_DOINGAUTO; 290 291 /* 292 * Check to see if we have link. If we do, we don't 293 * need to restart the autonegotiation process. Read 294 * the BMSR twice in case it's latched. 295 */ 296 reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & 297 (DC_TSTAT_LS10|DC_TSTAT_LS100); 298 299 if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX && 300 !(reg & DC_TSTAT_LS100)) { 301 if (sc->mii_flags & MIIF_AUTOTIMEOUT) { 302 sc->mii_flags &= ~MIIF_AUTOTIMEOUT; 303 break; 304 } else 305 return(0); 306 } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T && 307 !(reg & DC_TSTAT_LS10)) { 308 if (sc->mii_flags & MIIF_AUTOTIMEOUT) { 309 sc->mii_flags &= ~MIIF_AUTOTIMEOUT; 310 break; 311 } else 312 return(0); 313 } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE && 314 (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))) { 315 if (sc->mii_flags & MIIF_AUTOTIMEOUT) { 316 sc->mii_flags &= ~MIIF_AUTOTIMEOUT; 317 break; 318 } else 319 return(0); 320 } else if (CSR_READ_4(dc_sc, DC_ISR) & DC_ISR_LINKGOOD) { 321 if (sc->mii_flags & MIIF_AUTOTIMEOUT) { 322 sc->mii_flags &= ~MIIF_AUTOTIMEOUT; 323 break; 324 } else 325 return(0); 326 } 327 328 sc->mii_ticks = 0; 329 /*dcphy_reset(sc);*/ 330 dcphy_auto(sc, 0); 331 332 break; 333 } 334 335 /* Update the media status. */ 336 dcphy_status(sc); 337 338 /* Callback if something changed. */ 339 if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { 340 (*mii->mii_statchg)(sc->mii_dev.dv_parent); 341 sc->mii_active = mii->mii_media_active; 342 } 343 return (0); 344} 345 346void 347dcphy_status(sc) 348 struct mii_softc *sc; 349{ 350 struct mii_data *mii = sc->mii_pdata; 351 int reg, anlpar; 352 struct dc_softc *dc_sc; 353 354 dc_sc = mii->mii_ifp->if_softc; 355 356 mii->mii_media_status = IFM_AVALID; 357 mii->mii_media_active = IFM_ETHER; 358 359 reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & 360 (DC_TSTAT_LS10|DC_TSTAT_LS100); 361 362 if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 363 mii->mii_media_status |= IFM_ACTIVE; 364 365 if (sc->mii_flags & MIIF_DOINGAUTO) { 366 mii->mii_media_active |= IFM_NONE; 367 return; 368 } 369 370 if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL && 371 CSR_READ_4(dc_sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) { 372 /* Erg, still trying, I guess... */ 373 if ((CSR_READ_4(dc_sc, DC_10BTSTAT) & 374 DC_ASTAT_AUTONEGCMP) != DC_ASTAT_AUTONEGCMP) { 375 mii->mii_media_active |= IFM_NONE; 376 return; 377 } 378 379 if (CSR_READ_4(dc_sc, DC_10BTSTAT) & DC_TSTAT_LP_CAN_NWAY) { 380 anlpar = CSR_READ_4(dc_sc, DC_10BTSTAT) >> 16; 381 if (anlpar & ANLPAR_T4 && 382 sc->mii_capabilities & BMSR_100TXHDX) 383 mii->mii_media_active |= IFM_100_T4; 384 else if (anlpar & ANLPAR_TX_FD && 385 sc->mii_capabilities & BMSR_100TXHDX) 386 mii->mii_media_active |= IFM_100_TX|IFM_FDX; 387 else if (anlpar & ANLPAR_TX && 388 sc->mii_capabilities & BMSR_100TXHDX) 389 mii->mii_media_active |= IFM_100_TX; 390 else if (anlpar & ANLPAR_10_FD) 391 mii->mii_media_active |= IFM_10_T|IFM_FDX; 392 else if (anlpar & ANLPAR_10) 393 mii->mii_media_active |= IFM_10_T; 394 else 395 mii->mii_media_active |= IFM_NONE; 396 if (DC_IS_INTEL(dc_sc)) 397 DC_CLRBIT(dc_sc, DC_10BTCTRL, 398 DC_TCTL_AUTONEGENBL); 399 return; 400 } 401 /* 402 * If the other side doesn't support NWAY, then the 403 * best we can do is determine if we have a 10Mbps or 404 * 100Mbps link. There's no way to know if the link 405 * is full or half duplex, so we default to half duplex 406 * and hope that the user is clever enough to manually 407 * change the media settings if we're wrong. 408 */ 409 if (!(reg & DC_TSTAT_LS100)) 410 mii->mii_media_active |= IFM_100_TX; 411 else if (!(reg & DC_TSTAT_LS10)) 412 mii->mii_media_active |= IFM_10_T; 413 else 414 mii->mii_media_active |= IFM_NONE; 415 if (DC_IS_INTEL(dc_sc)) 416 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 417 return; 418 } 419 420 if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SCRAMBLER) 421 mii->mii_media_active |= IFM_100_TX; 422 else 423 mii->mii_media_active |= IFM_10_T; 424 if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) 425 mii->mii_media_active |= IFM_FDX; 426 427 return; 428} 429 430int 431dcphy_auto(mii, waitfor) 432 struct mii_softc *mii; 433 int waitfor; 434{ 435 int i; 436 struct dc_softc *sc; 437 438 sc = mii->mii_pdata->mii_ifp->if_softc; 439 440 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 441 DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); 442 DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); 443 DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 444 if (mii->mii_capabilities & BMSR_100TXHDX) 445 CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF); 446 else 447 CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF); 448 DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 449 DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 450 DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE); 451 } 452 453 if (waitfor) { 454 /* Wait 500ms for it to complete. */ 455 for (i = 0; i < 500; i++) { 456 if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) 457 == DC_ASTAT_AUTONEGCMP) 458 return(0); 459 DELAY(1000); 460 } 461 /* 462 * Don't need to worry about clearing MIIF_DOINGAUTO. 463 * If that's set, a timeout is pending, and it will 464 * clear the flag. 465 */ 466 return(EIO); 467 } 468 469 /* 470 * Just let it finish asynchronously. This is for the benefit of 471 * the tick handler driving autonegotiation. Don't want 500ms 472 * delays all the time while the system is running! 473 */ 474 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) 475 mii->mii_flags |= MIIF_DOINGAUTO; 476 477 return(EJUSTRETURN); 478} 479 480void 481dcphy_reset(mii) 482 struct mii_softc *mii; 483{ 484 struct dc_softc *sc; 485 486 sc = mii->mii_pdata->mii_ifp->if_softc; 487 488 DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 489 DELAY(1000); 490 DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 491 492 return; 493} 494