nsphy.c revision 105135
1321936Shselasky/* $NetBSD: nsphy.c,v 1.18 1999/07/14 23:57:36 thorpej Exp $ */ 2321936Shselasky 3321936Shselasky/*- 4321936Shselasky * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. 5321936Shselasky * All rights reserved. 6321936Shselasky * 7321936Shselasky * This code is derived from software contributed to The NetBSD Foundation 8321936Shselasky * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9321936Shselasky * NASA Ames Research Center. 10321936Shselasky * 11321936Shselasky * Redistribution and use in source and binary forms, with or without 12321936Shselasky * modification, are permitted provided that the following conditions 13321936Shselasky * are met: 14321936Shselasky * 1. Redistributions of source code must retain the above copyright 15321936Shselasky * notice, this list of conditions and the following disclaimer. 16321936Shselasky * 2. Redistributions in binary form must reproduce the above copyright 17321936Shselasky * notice, this list of conditions and the following disclaimer in the 18321936Shselasky * documentation and/or other materials provided with the distribution. 19321936Shselasky * 3. All advertising materials mentioning features or use of this software 20321936Shselasky * must display the following acknowledgement: 21321936Shselasky * This product includes software developed by the NetBSD 22321936Shselasky * Foundation, Inc. and its contributors. 23321936Shselasky * 4. Neither the name of The NetBSD Foundation nor the names of its 24321936Shselasky * contributors may be used to endorse or promote products derived 25321936Shselasky * from this software without specific prior written permission. 26321936Shselasky * 27321936Shselasky * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28321936Shselasky * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29321936Shselasky * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30321936Shselasky * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31321936Shselasky * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32321936Shselasky * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33321936Shselasky * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34321936Shselasky * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35321936Shselasky * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36321936Shselasky * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37321936Shselasky * POSSIBILITY OF SUCH DAMAGE. 38321936Shselasky */ 39321936Shselasky 40321936Shselasky/* 41321936Shselasky * Copyright (c) 1997 Manuel Bouyer. All rights reserved. 42321936Shselasky * 43321936Shselasky * Redistribution and use in source and binary forms, with or without 44321936Shselasky * modification, are permitted provided that the following conditions 45321936Shselasky * are met: 46321936Shselasky * 1. Redistributions of source code must retain the above copyright 47321936Shselasky * notice, this list of conditions and the following disclaimer. 48321936Shselasky * 2. Redistributions in binary form must reproduce the above copyright 49321936Shselasky * notice, this list of conditions and the following disclaimer in the 50321936Shselasky * documentation and/or other materials provided with the distribution. 51321936Shselasky * 3. All advertising materials mentioning features or use of this software 52321936Shselasky * must display the following acknowledgement: 53321936Shselasky * This product includes software developed by Manuel Bouyer. 54321936Shselasky * 4. The name of the author may not be used to endorse or promote products 55321936Shselasky * derived from this software without specific prior written permission. 56321936Shselasky * 57321936Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 58321936Shselasky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 59321936Shselasky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 60321936Shselasky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 61321936Shselasky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 62321936Shselasky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 63321936Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 64321936Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 65321936Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 66321936Shselasky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 67321936Shselasky */ 68321936Shselasky 69321936Shselasky/* 70321936Shselasky * driver for National Semiconductor's DP83840A ethernet 10/100 PHY 71321936Shselasky * Data Sheet available from www.national.com 72321936Shselasky */ 73321936Shselasky 74321936Shselasky#include <sys/param.h> 75321936Shselasky#include <sys/systm.h> 76321936Shselasky#include <sys/kernel.h> 77321936Shselasky#include <sys/socket.h> 78321936Shselasky#include <sys/errno.h> 79321936Shselasky#include <sys/module.h> 80321936Shselasky#include <sys/bus.h> 81321936Shselasky 82321936Shselasky#include <net/if.h> 83321936Shselasky#include <net/if_media.h> 84321936Shselasky 85321936Shselasky#include <dev/mii/mii.h> 86321936Shselasky#include <dev/mii/miivar.h> 87321936Shselasky#include <dev/mii/miidevs.h> 88321936Shselasky 89321936Shselasky#include <dev/mii/nsphyreg.h> 90321936Shselasky 91321936Shselasky#include "miibus_if.h" 92321936Shselasky 93321936Shselasky#if !defined(lint) 94321936Shselaskystatic const char rcsid[] = 95321936Shselasky "$FreeBSD: head/sys/dev/mii/nsphy.c 105135 2002-10-14 22:31:52Z alfred $"; 96321936Shselasky#endif 97321936Shselasky 98321936Shselaskystatic int nsphy_probe(device_t); 99321936Shselaskystatic int nsphy_attach(device_t); 100321936Shselasky 101321936Shselaskystatic device_method_t nsphy_methods[] = { 102321936Shselasky /* device interface */ 103321936Shselasky DEVMETHOD(device_probe, nsphy_probe), 104321936Shselasky DEVMETHOD(device_attach, nsphy_attach), 105321936Shselasky DEVMETHOD(device_detach, mii_phy_detach), 106321936Shselasky DEVMETHOD(device_shutdown, bus_generic_shutdown), 107321936Shselasky { 0, 0 } 108321936Shselasky}; 109321936Shselasky 110321936Shselaskystatic devclass_t nsphy_devclass; 111321936Shselasky 112321936Shselaskystatic driver_t nsphy_driver = { 113321936Shselasky "nsphy", 114321936Shselasky nsphy_methods, 115321936Shselasky sizeof(struct mii_softc) 116321936Shselasky}; 117321936Shselasky 118321936ShselaskyDRIVER_MODULE(nsphy, miibus, nsphy_driver, nsphy_devclass, 0, 0); 119321936Shselasky 120321936Shselaskystatic int nsphy_service(struct mii_softc *, struct mii_data *, int); 121321936Shselaskystatic void nsphy_status(struct mii_softc *); 122321936Shselasky 123321936Shselaskystatic int 124321936Shselaskynsphy_probe(dev) 125321936Shselasky device_t dev; 126321936Shselasky{ 127321936Shselasky struct mii_attach_args *ma; 128321936Shselasky 129321936Shselasky ma = device_get_ivars(dev); 130321936Shselasky 131321936Shselasky if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_NATSEMI && 132321936Shselasky MII_MODEL(ma->mii_id2) == MII_MODEL_NATSEMI_DP83840) { 133321936Shselasky device_set_desc(dev, MII_STR_NATSEMI_DP83840); 134321936Shselasky } else 135321936Shselasky return (ENXIO); 136321936Shselasky 137321936Shselasky return (0); 138321936Shselasky} 139321936Shselasky 140321936Shselaskystatic int 141321936Shselaskynsphy_attach(dev) 142321936Shselasky device_t dev; 143321936Shselasky{ 144321936Shselasky struct mii_softc *sc; 145321936Shselasky struct mii_attach_args *ma; 146321936Shselasky struct mii_data *mii; 147321936Shselasky 148321936Shselasky sc = device_get_softc(dev); 149321936Shselasky ma = device_get_ivars(dev); 150321936Shselasky sc->mii_dev = device_get_parent(dev); 151321936Shselasky mii = device_get_softc(sc->mii_dev); 152321936Shselasky LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 153321936Shselasky 154321936Shselasky sc->mii_inst = mii->mii_instance; 155321936Shselasky sc->mii_phy = ma->mii_phyno; 156321936Shselasky sc->mii_service = nsphy_service; 157321936Shselasky sc->mii_pdata = mii; 158321936Shselasky 159321936Shselasky#ifdef foo 160321936Shselasky /* 161321936Shselasky * i82557 wedges if all of its PHYs are isolated! 162321936Shselasky */ 163321936Shselasky if (strcmp(device_get_name(device_get_parent(sc->mii_dev)), 164321936Shselasky "fxp") == 0 && mii->mii_instance == 0) 165321936Shselasky#endif 166321936Shselasky 167321936Shselasky sc->mii_flags |= MIIF_NOISOLATE; 168321936Shselasky mii->mii_instance++; 169321936Shselasky 170321936Shselasky#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 171321936Shselasky 172321936Shselasky#if 0 173321936Shselasky /* Can't do this on the i82557! */ 174321936Shselasky ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), 175321936Shselasky BMCR_ISO); 176321936Shselasky#endif 177321936Shselasky ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), 178321936Shselasky BMCR_LOOP|BMCR_S100); 179321936Shselasky 180321936Shselasky mii_phy_reset(sc); 181321936Shselasky 182321936Shselasky sc->mii_capabilities = 183321936Shselasky PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 184321936Shselasky device_printf(dev, " "); 185321936Shselasky mii_add_media(sc); 186321936Shselasky printf("\n"); 187321936Shselasky#undef ADD 188321936Shselasky 189321936Shselasky MIIBUS_MEDIAINIT(sc->mii_dev); 190321936Shselasky return(0); 191321936Shselasky} 192321936Shselasky 193321936Shselaskystatic int 194321936Shselaskynsphy_service(sc, mii, cmd) 195321936Shselasky struct mii_softc *sc; 196321936Shselasky struct mii_data *mii; 197321936Shselasky int cmd; 198321936Shselasky{ 199321936Shselasky struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 200321936Shselasky int reg; 201321936Shselasky 202321936Shselasky switch (cmd) { 203321936Shselasky case MII_POLLSTAT: 204321936Shselasky /* 205321936Shselasky * If we're not polling our PHY instance, just return. 206321936Shselasky */ 207321936Shselasky if (IFM_INST(ife->ifm_media) != sc->mii_inst) 208321936Shselasky return (0); 209321936Shselasky break; 210321936Shselasky 211321936Shselasky case MII_MEDIACHG: 212321936Shselasky /* 213321936Shselasky * If the media indicates a different PHY instance, 214321936Shselasky * isolate ourselves. 215321936Shselasky */ 216321936Shselasky if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 217321936Shselasky reg = PHY_READ(sc, MII_BMCR); 218321936Shselasky PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 219321936Shselasky return (0); 220321936Shselasky } 221321936Shselasky 222321936Shselasky /* 223321936Shselasky * If the interface is not up, don't do anything. 224321936Shselasky */ 225321936Shselasky if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 226321936Shselasky break; 227321936Shselasky 228321936Shselasky reg = PHY_READ(sc, MII_NSPHY_PCR); 229321936Shselasky 230321936Shselasky /* 231321936Shselasky * Set up the PCR to use LED4 to indicate full-duplex 232321936Shselasky * in both 10baseT and 100baseTX modes. 233321936Shselasky */ 234321936Shselasky reg |= PCR_LED4MODE; 235321936Shselasky 236321936Shselasky /* 237321936Shselasky * Make sure Carrier Intgrity Monitor function is 238321936Shselasky * disabled (normal for Node operation, but sometimes 239321936Shselasky * it's not set?!) 240321936Shselasky */ 241321936Shselasky reg |= PCR_CIMDIS; 242321936Shselasky 243321936Shselasky /* 244321936Shselasky * Make sure "force link good" is set to normal mode. 245321936Shselasky * It's only intended for debugging. 246321936Shselasky */ 247321936Shselasky reg |= PCR_FLINK100; 248321936Shselasky 249321936Shselasky /* 250321936Shselasky * Mystery bits which are supposedly `reserved', 251321936Shselasky * but we seem to need to set them when the PHY 252321936Shselasky * is connected to some interfaces: 253321936Shselasky * 254321936Shselasky * 0x0400 is needed for fxp 255321936Shselasky * (Intel EtherExpress Pro 10+/100B, 82557 chip) 256321936Shselasky * (nsphy with a DP83840 chip) 257321936Shselasky * 0x0100 may be needed for some other card 258321936Shselasky */ 259321936Shselasky reg |= 0x0100 | 0x0400; 260321936Shselasky 261321936Shselasky if (strcmp(device_get_name(device_get_parent(sc->mii_dev)), 262321936Shselasky "fxp") == 0) 263321936Shselasky PHY_WRITE(sc, MII_NSPHY_PCR, reg); 264321936Shselasky 265321936Shselasky switch (IFM_SUBTYPE(ife->ifm_media)) { 266321936Shselasky case IFM_AUTO: 267321936Shselasky /* 268321936Shselasky * If we're already in auto mode, just return. 269321936Shselasky */ 270321936Shselasky if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN) 271321936Shselasky return (0); 272321936Shselasky (void) mii_phy_auto(sc); 273321936Shselasky break; 274321936Shselasky case IFM_100_T4: 275321936Shselasky /* 276321936Shselasky * XXX Not supported as a manual setting right now. 277321936Shselasky */ 278321936Shselasky return (EINVAL); 279321936Shselasky default: 280321936Shselasky /* 281321936Shselasky * BMCR data is stored in the ifmedia entry. 282321936Shselasky */ 283321936Shselasky PHY_WRITE(sc, MII_ANAR, 284321936Shselasky mii_anar(ife->ifm_media)); 285321936Shselasky PHY_WRITE(sc, MII_BMCR, ife->ifm_data); 286321936Shselasky } 287321936Shselasky break; 288321936Shselasky 289321936Shselasky case MII_TICK: 290321936Shselasky /* 291321936Shselasky * If we're not currently selected, just return. 292321936Shselasky */ 293321936Shselasky if (IFM_INST(ife->ifm_media) != sc->mii_inst) 294321936Shselasky return (0); 295321936Shselasky if (mii_phy_tick(sc) == EJUSTRETURN) 296321936Shselasky return (0); 297321936Shselasky break; 298321936Shselasky } 299321936Shselasky 300321936Shselasky /* Update the media status. */ 301321936Shselasky nsphy_status(sc); 302321936Shselasky 303321936Shselasky /* Callback if something changed. */ 304321936Shselasky mii_phy_update(sc, cmd); 305321936Shselasky return (0); 306321936Shselasky} 307321936Shselasky 308321936Shselaskystatic void 309321936Shselaskynsphy_status(sc) 310321936Shselasky struct mii_softc *sc; 311321936Shselasky{ 312321936Shselasky struct mii_data *mii = sc->mii_pdata; 313321936Shselasky int bmsr, bmcr, par, anlpar; 314321936Shselasky 315321936Shselasky mii->mii_media_status = IFM_AVALID; 316321936Shselasky mii->mii_media_active = IFM_ETHER; 317321936Shselasky 318321936Shselasky bmsr = PHY_READ(sc, MII_BMSR) | 319321936Shselasky PHY_READ(sc, MII_BMSR); 320321936Shselasky if (bmsr & BMSR_LINK) 321321936Shselasky mii->mii_media_status |= IFM_ACTIVE; 322321936Shselasky 323321936Shselasky bmcr = PHY_READ(sc, MII_BMCR); 324321936Shselasky if (bmcr & BMCR_ISO) { 325321936Shselasky mii->mii_media_active |= IFM_NONE; 326321936Shselasky mii->mii_media_status = 0; 327321936Shselasky return; 328321936Shselasky } 329321936Shselasky 330321936Shselasky if (bmcr & BMCR_LOOP) 331321936Shselasky mii->mii_media_active |= IFM_LOOP; 332321936Shselasky 333321936Shselasky if (bmcr & BMCR_AUTOEN) { 334321936Shselasky /* 335321936Shselasky * The PAR status bits are only valid of autonegotiation 336321936Shselasky * has completed (or it's disabled). 337321936Shselasky */ 338321936Shselasky if ((bmsr & BMSR_ACOMP) == 0) { 339321936Shselasky /* Erg, still trying, I guess... */ 340321936Shselasky mii->mii_media_active |= IFM_NONE; 341321936Shselasky return; 342321936Shselasky } 343321936Shselasky 344321936Shselasky /* 345321936Shselasky * Argh. The PAR doesn't seem to indicate duplex mode 346321936Shselasky * properly! Determine media based on link partner's 347321936Shselasky * advertised capabilities. 348321936Shselasky */ 349321936Shselasky if (PHY_READ(sc, MII_ANER) & ANER_LPAN) { 350321936Shselasky anlpar = PHY_READ(sc, MII_ANAR) & 351321936Shselasky PHY_READ(sc, MII_ANLPAR); 352321936Shselasky if (anlpar & ANLPAR_T4) 353321936Shselasky mii->mii_media_active |= IFM_100_T4; 354321936Shselasky else if (anlpar & ANLPAR_TX_FD) 355321936Shselasky mii->mii_media_active |= IFM_100_TX|IFM_FDX; 356321936Shselasky else if (anlpar & ANLPAR_TX) 357321936Shselasky mii->mii_media_active |= IFM_100_TX; 358321936Shselasky else if (anlpar & ANLPAR_10_FD) 359321936Shselasky mii->mii_media_active |= IFM_10_T|IFM_FDX; 360321936Shselasky else if (anlpar & ANLPAR_10) 361321936Shselasky mii->mii_media_active |= IFM_10_T; 362321936Shselasky else 363321936Shselasky mii->mii_media_active |= IFM_NONE; 364321936Shselasky return; 365321936Shselasky } 366321936Shselasky 367321936Shselasky /* 368321936Shselasky * Link partner is not capable of autonegotiation. 369321936Shselasky * We will never be in full-duplex mode if this is 370321936Shselasky * the case, so reading the PAR is OK. 371321936Shselasky */ 372321936Shselasky par = PHY_READ(sc, MII_NSPHY_PAR); 373321936Shselasky if (par & PAR_10) 374321936Shselasky mii->mii_media_active |= IFM_10_T; 375321936Shselasky else 376321936Shselasky mii->mii_media_active |= IFM_100_TX; 377321936Shselasky#if 0 378321936Shselasky if (par & PAR_FDX) 379321936Shselasky mii->mii_media_active |= IFM_FDX; 380321936Shselasky#endif 381321936Shselasky } else 382321936Shselasky mii->mii_media_active |= mii_media_from_bmcr(bmcr); 383321936Shselasky} 384321936Shselasky