dcphy.c revision 1.9
1/* $OpenBSD: dcphy.c,v 1.9 2004/09/26 00:59:58 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(parent, match, aux) 111 struct device *parent; 112 void *match, *aux; 113{ 114 struct mii_attach_args *ma = aux; 115 116 /* 117 * The dc driver will report the 21143 vendor and device 118 * ID to let us know that it wants us to attach. 119 */ 120 if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_xxDEC && 121 MII_MODEL(ma->mii_id2) == MII_MODEL_xxDEC_xxDC) 122 return (10); 123 124 return (0); 125} 126 127void 128dcphy_attach(parent, self, aux) 129 struct device *parent; 130 struct device *self; 131 void *aux; 132{ 133 struct mii_softc *sc = (struct mii_softc *)self; 134 struct mii_attach_args *ma = aux; 135 struct mii_data *mii = ma->mii_data; 136 struct dc_softc *dc_sc; 137 138 printf(": internal PHY\n"); 139 sc->mii_inst = mii->mii_instance; 140 sc->mii_phy = ma->mii_phyno; 141 sc->mii_funcs = &dcphy_funcs; 142 sc->mii_pdata = mii; 143 sc->mii_flags = mii->mii_flags; 144 145 sc->mii_flags |= MIIF_NOISOLATE; 146 mii->mii_instance++; 147 148 dc_sc = mii->mii_ifp->if_softc; 149 CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0); 150 CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0); 151 152#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 153 154 switch(dc_sc->dc_csid) { 155 case COMPAQ_PRESARIO_ID: 156 /* Example of how to only allow 10Mbps modes. */ 157 sc->mii_capabilities = BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; 158 break; 159 default: 160 if (dc_sc->dc_pmode == DC_PMODE_SIA) { 161 sc->mii_capabilities = 162 BMSR_ANEG|BMSR_10TFDX|BMSR_10THDX; 163 } else { 164 ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, 165 sc->mii_inst), BMCR_LOOP|BMCR_S100); 166 167 sc->mii_capabilities = 168 BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| 169 BMSR_10TFDX|BMSR_10THDX; 170 } 171 break; 172 } 173 174 if (dc_sc->dc_type == DC_TYPE_21145) 175 sc->mii_capabilities = BMSR_10THDX; 176 177 sc->mii_capabilities &= ma->mii_capmask; 178 if (sc->mii_capabilities & BMSR_MEDIAMASK) 179 mii_phy_add_media(sc); 180#undef ADD 181} 182 183int 184dcphy_service(sc, mii, cmd) 185 struct mii_softc *sc; 186 struct mii_data *mii; 187 int cmd; 188{ 189 struct dc_softc *dc_sc; 190 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 191 int reg; 192 u_int32_t mode; 193 194 if ((sc->mii_dev.dv_flags & DVF_ACTIVE) == 0) 195 return (ENXIO); 196 197 dc_sc = mii->mii_ifp->if_softc; 198 199 switch (cmd) { 200 case MII_POLLSTAT: 201 /* 202 * If we're not polling our PHY instance, just return. 203 */ 204 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 205 return (0); 206 } 207 break; 208 209 case MII_MEDIACHG: 210 /* 211 * If the media indicates a different PHY instance, 212 * isolate ourselves. 213 */ 214 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 215 return (0); 216 } 217 218 /* 219 * If the interface is not up, don't do anything. 220 */ 221 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 222 break; 223 224 sc->mii_flags = 0; 225 mii->mii_media_active = IFM_NONE; 226 mode = CSR_READ_4(dc_sc, DC_NETCFG); 227 mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL| 228 DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL); 229 230 switch (IFM_SUBTYPE(ife->ifm_media)) { 231 case IFM_AUTO: 232 /*PHY_RESET(sc);*/ 233 sc->mii_flags &= ~MIIF_DOINGAUTO; 234 (void) dcphy_auto(sc, 0); 235 break; 236 case IFM_100_T4: 237 /* 238 * XXX Not supported as a manual setting right now. 239 */ 240 return (EINVAL); 241 case IFM_100_TX: 242 PHY_RESET(sc); 243 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 244 mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS| 245 DC_NETCFG_SCRAMBLER; 246 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 247 mode |= DC_NETCFG_FULLDUPLEX; 248 else 249 mode &= ~DC_NETCFG_FULLDUPLEX; 250 CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 251 break; 252 case IFM_10_T: 253 DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 254 DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF); 255 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 256 DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D); 257 else 258 DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F); 259 DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); 260 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 261 mode &= ~DC_NETCFG_PORTSEL; 262 mode |= DC_NETCFG_SPEEDSEL; 263 if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) 264 mode |= DC_NETCFG_FULLDUPLEX; 265 else 266 mode &= ~DC_NETCFG_FULLDUPLEX; 267 CSR_WRITE_4(dc_sc, DC_NETCFG, mode); 268 break; 269 default: 270 return(EINVAL); 271 break; 272 } 273 break; 274 275 case MII_TICK: 276 /* 277 * If we're not currently selected, just return. 278 */ 279 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 280 return (0); 281 282 /* 283 * Only used for autonegotiation. 284 */ 285 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 286 return (0); 287 288 /* 289 * Is the interface even up? 290 */ 291 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 292 return (0); 293 294 reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & 295 (DC_TSTAT_LS10|DC_TSTAT_LS100); 296 297 if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 298 return (0); 299 300 /* 301 * Only retry autonegotiation every 5 seconds. 302 */ 303 if (++sc->mii_ticks != 50) 304 return (0); 305 306 sc->mii_ticks = 0; 307 /*if (DC_IS_INTEL(dc_sc))*/ 308 sc->mii_flags &= ~MIIF_DOINGAUTO; 309 dcphy_auto(sc, 0); 310 311 break; 312 } 313 314 /* Update the media status. */ 315 mii_phy_status(sc); 316 317 /* Callback if something changed. */ 318 mii_phy_update(sc, cmd); 319 return (0); 320} 321 322void 323dcphy_status(sc) 324 struct mii_softc *sc; 325{ 326 struct mii_data *mii = sc->mii_pdata; 327 int reg, anlpar, tstat = 0; 328 struct dc_softc *dc_sc; 329 330 dc_sc = mii->mii_ifp->if_softc; 331 332 mii->mii_media_status = IFM_AVALID; 333 mii->mii_media_active = IFM_ETHER; 334 335 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 336 return; 337 338 reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & 339 (DC_TSTAT_LS10|DC_TSTAT_LS100); 340 341 if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) 342 mii->mii_media_status |= IFM_ACTIVE; 343 344 if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL) { 345 /* Erg, still trying, I guess... */ 346 tstat = CSR_READ_4(dc_sc, DC_10BTSTAT); 347 if ((tstat & DC_TSTAT_ANEGSTAT) != DC_ASTAT_AUTONEGCMP) { 348 if ((DC_IS_MACRONIX(dc_sc) || DC_IS_PNICII(dc_sc)) && 349 (tstat & DC_TSTAT_ANEGSTAT) == DC_ASTAT_DISABLE) 350 goto skip; 351 mii->mii_media_active |= IFM_NONE; 352 return; 353 } 354 355 if (tstat & DC_TSTAT_LP_CAN_NWAY) { 356 anlpar = tstat >> 16; 357 if (anlpar & ANLPAR_T4 && 358 sc->mii_capabilities & BMSR_100TXHDX) 359 mii->mii_media_active |= IFM_100_T4; 360 else if (anlpar & ANLPAR_TX_FD && 361 sc->mii_capabilities & BMSR_100TXFDX) 362 mii->mii_media_active |= IFM_100_TX|IFM_FDX; 363 else if (anlpar & ANLPAR_TX && 364 sc->mii_capabilities & BMSR_100TXHDX) 365 mii->mii_media_active |= IFM_100_TX; 366 else if (anlpar & ANLPAR_10_FD) 367 mii->mii_media_active |= IFM_10_T|IFM_FDX; 368 else if (anlpar & ANLPAR_10) 369 mii->mii_media_active |= IFM_10_T; 370 else 371 mii->mii_media_active |= IFM_NONE; 372 if (DC_IS_INTEL(dc_sc)) 373 DC_CLRBIT(dc_sc, DC_10BTCTRL, 374 DC_TCTL_AUTONEGENBL); 375 return; 376 } 377 /* 378 * If the other side doesn't support NWAY, then the 379 * best we can do is determine if we have a 10Mbps or 380 * 100Mbps link. There's no way to know if the link 381 * is full or half duplex, so we default to half duplex 382 * and hope that the user is clever enough to manually 383 * change the media settings if we're wrong. 384 */ 385 if (!(reg & DC_TSTAT_LS100)) 386 mii->mii_media_active |= IFM_100_TX; 387 else if (!(reg & DC_TSTAT_LS10)) 388 mii->mii_media_active |= IFM_10_T; 389 else 390 mii->mii_media_active |= IFM_NONE; 391 if (DC_IS_INTEL(dc_sc)) 392 DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 393 return; 394 } 395 396skip: 397 398 if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL) 399 mii->mii_media_active |= IFM_10_T; 400 else 401 mii->mii_media_active |= IFM_100_TX; 402 if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) 403 mii->mii_media_active |= IFM_FDX; 404 405 return; 406} 407 408int 409dcphy_auto(mii, waitfor) 410 struct mii_softc *mii; 411 int waitfor; 412{ 413 int i; 414 struct dc_softc *sc; 415 416 sc = mii->mii_pdata->mii_ifp->if_softc; 417 418 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { 419 DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); 420 DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); 421 DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 422 if (mii->mii_capabilities & BMSR_100TXHDX) 423 CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF); 424 else 425 CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFF); 426 DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 427 DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); 428 DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE); 429 } 430 431 if (waitfor) { 432 /* Wait 500ms for it to complete. */ 433 for (i = 0; i < 500; i++) { 434 if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) 435 == DC_ASTAT_AUTONEGCMP) 436 return(0); 437 DELAY(1000); 438 } 439 /* 440 * Don't need to worry about clearing MIIF_DOINGAUTO. 441 * If that's set, a timeout is pending, and it will 442 * clear the flag. 443 */ 444 return(EIO); 445 } 446 447 /* 448 * Just let it finish asynchronously. This is for the benefit of 449 * the tick handler driving autonegotiation. Don't want 500ms 450 * delays all the time while the system is running! 451 */ 452 if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) 453 mii->mii_flags |= MIIF_DOINGAUTO; 454 455 return(EJUSTRETURN); 456} 457 458void 459dcphy_reset(mii) 460 struct mii_softc *mii; 461{ 462 struct dc_softc *sc; 463 464 sc = mii->mii_pdata->mii_ifp->if_softc; 465 466 DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); 467 DELAY(1000); 468 DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); 469 470 return; 471} 472