1098ca2bdSWarner Losh /*- 2c0d7d4d4SBill Paul * Copyright (c) 2000 3c0d7d4d4SBill Paul * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. 4c0d7d4d4SBill Paul * 5c0d7d4d4SBill Paul * Redistribution and use in source and binary forms, with or without 6c0d7d4d4SBill Paul * modification, are permitted provided that the following conditions 7c0d7d4d4SBill Paul * are met: 8c0d7d4d4SBill Paul * 1. Redistributions of source code must retain the above copyright 9c0d7d4d4SBill Paul * notice, this list of conditions and the following disclaimer. 10c0d7d4d4SBill Paul * 2. Redistributions in binary form must reproduce the above copyright 11c0d7d4d4SBill Paul * notice, this list of conditions and the following disclaimer in the 12c0d7d4d4SBill Paul * documentation and/or other materials provided with the distribution. 13c0d7d4d4SBill Paul * 3. All advertising materials mentioning features or use of this software 14c0d7d4d4SBill Paul * must display the following acknowledgement: 15c0d7d4d4SBill Paul * This product includes software developed by Bill Paul. 16c0d7d4d4SBill Paul * 4. Neither the name of the author nor the names of any co-contributors 17c0d7d4d4SBill Paul * may be used to endorse or promote products derived from this software 18c0d7d4d4SBill Paul * without specific prior written permission. 19c0d7d4d4SBill Paul * 20c0d7d4d4SBill Paul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21c0d7d4d4SBill Paul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22c0d7d4d4SBill Paul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23c0d7d4d4SBill Paul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24c0d7d4d4SBill Paul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25c0d7d4d4SBill Paul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26c0d7d4d4SBill Paul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27c0d7d4d4SBill Paul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28c0d7d4d4SBill Paul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29c0d7d4d4SBill Paul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30c0d7d4d4SBill Paul * THE POSSIBILITY OF SUCH DAMAGE. 31c0d7d4d4SBill Paul */ 32c0d7d4d4SBill Paul 33aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 34aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 35aad970f1SDavid E. O'Brien 36c0d7d4d4SBill Paul /* 37c0d7d4d4SBill Paul * driver for the XaQti XMAC II's internal PHY. This is sort of 38c0d7d4d4SBill Paul * like a 10/100 PHY, except the only thing we're really autoselecting 39c0d7d4d4SBill Paul * here is full/half duplex. Speed is always 1000mbps. 40c0d7d4d4SBill Paul */ 41c0d7d4d4SBill Paul 42c0d7d4d4SBill Paul #include <sys/param.h> 43c0d7d4d4SBill Paul #include <sys/systm.h> 44c0d7d4d4SBill Paul #include <sys/kernel.h> 4541ee9f1cSPoul-Henning Kamp #include <sys/module.h> 46c0d7d4d4SBill Paul #include <sys/socket.h> 47c0d7d4d4SBill Paul #include <sys/bus.h> 48c0d7d4d4SBill Paul 49c0d7d4d4SBill Paul #include <net/if.h> 50c0d7d4d4SBill Paul #include <net/if_media.h> 51c0d7d4d4SBill Paul 52c0d7d4d4SBill Paul #include <dev/mii/mii.h> 53c0d7d4d4SBill Paul #include <dev/mii/miivar.h> 542d3ce713SDavid E. O'Brien #include "miidevs.h" 55c0d7d4d4SBill Paul 56c0d7d4d4SBill Paul #include <dev/mii/xmphyreg.h> 57c0d7d4d4SBill Paul 58c0d7d4d4SBill Paul #include "miibus_if.h" 59c0d7d4d4SBill Paul 60e51a25f8SAlfred Perlstein static int xmphy_probe(device_t); 61e51a25f8SAlfred Perlstein static int xmphy_attach(device_t); 62c0d7d4d4SBill Paul 63c0d7d4d4SBill Paul static device_method_t xmphy_methods[] = { 64c0d7d4d4SBill Paul /* device interface */ 65c0d7d4d4SBill Paul DEVMETHOD(device_probe, xmphy_probe), 66c0d7d4d4SBill Paul DEVMETHOD(device_attach, xmphy_attach), 67279fe8d1SPoul-Henning Kamp DEVMETHOD(device_detach, mii_phy_detach), 68c0d7d4d4SBill Paul DEVMETHOD(device_shutdown, bus_generic_shutdown), 69c0d7d4d4SBill Paul { 0, 0 } 70c0d7d4d4SBill Paul }; 71c0d7d4d4SBill Paul 72c0d7d4d4SBill Paul static devclass_t xmphy_devclass; 73c0d7d4d4SBill Paul 74c0d7d4d4SBill Paul static driver_t xmphy_driver = { 75c0d7d4d4SBill Paul "xmphy", 76c0d7d4d4SBill Paul xmphy_methods, 77c0d7d4d4SBill Paul sizeof(struct mii_softc) 78c0d7d4d4SBill Paul }; 79c0d7d4d4SBill Paul 80c0d7d4d4SBill Paul DRIVER_MODULE(xmphy, miibus, xmphy_driver, xmphy_devclass, 0, 0); 81c0d7d4d4SBill Paul 82e51a25f8SAlfred Perlstein static int xmphy_service(struct mii_softc *, struct mii_data *, int); 83e51a25f8SAlfred Perlstein static void xmphy_status(struct mii_softc *); 84fd94424cSPoul-Henning Kamp static int xmphy_mii_phy_auto(struct mii_softc *); 85c0d7d4d4SBill Paul 86a35b9333SMarius Strobl static const struct mii_phydesc xmphys[] = { 87a35b9333SMarius Strobl { MII_OUI_xxXAQTI, MII_MODEL_XAQTI_XMACII, MII_STR_XAQTI_XMACII }, 88a35b9333SMarius Strobl MII_PHY_DESC(JATO, BASEX), 89a35b9333SMarius Strobl MII_PHY_END 90a35b9333SMarius Strobl }; 91a35b9333SMarius Strobl 929c1c2e99SAlfred Perlstein static int 937d830ac9SWarner Losh xmphy_probe(device_t dev) 94c0d7d4d4SBill Paul { 95c0d7d4d4SBill Paul 96a35b9333SMarius Strobl return (mii_phy_dev_probe(dev, xmphys, BUS_PROBE_DEFAULT)); 97134c58d3SBill Paul } 98134c58d3SBill Paul 999c1c2e99SAlfred Perlstein static int 1007d830ac9SWarner Losh xmphy_attach(device_t dev) 101c0d7d4d4SBill Paul { 102c0d7d4d4SBill Paul struct mii_softc *sc; 103c0d7d4d4SBill Paul struct mii_attach_args *ma; 104c0d7d4d4SBill Paul struct mii_data *mii; 105c0d7d4d4SBill Paul const char *sep = ""; 106c0d7d4d4SBill Paul 107c0d7d4d4SBill Paul sc = device_get_softc(dev); 108c0d7d4d4SBill Paul ma = device_get_ivars(dev); 109c0d7d4d4SBill Paul sc->mii_dev = device_get_parent(dev); 1107b9a15f7SMarius Strobl mii = ma->mii_data; 111c0d7d4d4SBill Paul LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 112c0d7d4d4SBill Paul 113de1add1eSMarius Strobl sc->mii_inst = mii->mii_instance++; 114c0d7d4d4SBill Paul sc->mii_phy = ma->mii_phyno; 115c0d7d4d4SBill Paul sc->mii_service = xmphy_service; 116c0d7d4d4SBill Paul sc->mii_pdata = mii; 117c0d7d4d4SBill Paul 118c0d7d4d4SBill Paul sc->mii_flags |= MIIF_NOISOLATE; 119de1add1eSMarius Strobl sc->mii_anegticks = MII_ANEGTICKS; 120de1add1eSMarius Strobl 121de1add1eSMarius Strobl mii_phy_reset(sc); 122c0d7d4d4SBill Paul 123c0d7d4d4SBill Paul #define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) 124c0d7d4d4SBill Paul #define PRINT(s) printf("%s%s", sep, s); sep = ", " 125c0d7d4d4SBill Paul 126c0d7d4d4SBill Paul device_printf(dev, " "); 127c0d7d4d4SBill Paul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, sc->mii_inst), 128c0d7d4d4SBill Paul XMPHY_BMCR_FDX); 129c0d7d4d4SBill Paul PRINT("1000baseSX"); 130c0d7d4d4SBill Paul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, sc->mii_inst), 0); 131c0d7d4d4SBill Paul PRINT("1000baseSX-FDX"); 132c0d7d4d4SBill Paul ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), 0); 133c0d7d4d4SBill Paul PRINT("auto"); 134c0d7d4d4SBill Paul 135c0d7d4d4SBill Paul printf("\n"); 136c0d7d4d4SBill Paul #undef ADD 137c0d7d4d4SBill Paul #undef PRINT 138c0d7d4d4SBill Paul 139c0d7d4d4SBill Paul MIIBUS_MEDIAINIT(sc->mii_dev); 140c0d7d4d4SBill Paul return (0); 141c0d7d4d4SBill Paul } 142c0d7d4d4SBill Paul 143d9730b8bSJonathan Lemon static int 1447d830ac9SWarner Losh xmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 145c0d7d4d4SBill Paul { 146c0d7d4d4SBill Paul struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 147c0d7d4d4SBill Paul int reg; 148c0d7d4d4SBill Paul 149c0d7d4d4SBill Paul switch (cmd) { 150c0d7d4d4SBill Paul case MII_POLLSTAT: 151c0d7d4d4SBill Paul break; 152c0d7d4d4SBill Paul 153c0d7d4d4SBill Paul case MII_MEDIACHG: 154c0d7d4d4SBill Paul /* 155c0d7d4d4SBill Paul * If the interface is not up, don't do anything. 156c0d7d4d4SBill Paul */ 157c0d7d4d4SBill Paul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 158c0d7d4d4SBill Paul break; 159c0d7d4d4SBill Paul 160c0d7d4d4SBill Paul switch (IFM_SUBTYPE(ife->ifm_media)) { 161c0d7d4d4SBill Paul case IFM_AUTO: 162134c58d3SBill Paul #ifdef foo 163c0d7d4d4SBill Paul /* 164c0d7d4d4SBill Paul * If we're already in auto mode, just return. 165c0d7d4d4SBill Paul */ 166c0d7d4d4SBill Paul if (PHY_READ(sc, XMPHY_MII_BMCR) & XMPHY_BMCR_AUTOEN) 167c0d7d4d4SBill Paul return (0); 168134c58d3SBill Paul #endif 169fd94424cSPoul-Henning Kamp (void) xmphy_mii_phy_auto(sc); 170c0d7d4d4SBill Paul break; 171c0d7d4d4SBill Paul case IFM_1000_SX: 172c0d7d4d4SBill Paul mii_phy_reset(sc); 173c0d7d4d4SBill Paul if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { 174c0d7d4d4SBill Paul PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_FDX); 175c0d7d4d4SBill Paul PHY_WRITE(sc, XMPHY_MII_BMCR, XMPHY_BMCR_FDX); 176c0d7d4d4SBill Paul } else { 177c0d7d4d4SBill Paul PHY_WRITE(sc, XMPHY_MII_ANAR, XMPHY_ANAR_HDX); 178c0d7d4d4SBill Paul PHY_WRITE(sc, XMPHY_MII_BMCR, 0); 179c0d7d4d4SBill Paul } 180c0d7d4d4SBill Paul break; 181c0d7d4d4SBill Paul case IFM_100_T4: 182c0d7d4d4SBill Paul case IFM_100_TX: 183c0d7d4d4SBill Paul case IFM_10_T: 184c0d7d4d4SBill Paul default: 185c0d7d4d4SBill Paul return (EINVAL); 186c0d7d4d4SBill Paul } 187c0d7d4d4SBill Paul break; 188c0d7d4d4SBill Paul 189c0d7d4d4SBill Paul case MII_TICK: 190c0d7d4d4SBill Paul /* 191c0d7d4d4SBill Paul * Is the interface even up? 192c0d7d4d4SBill Paul */ 193c0d7d4d4SBill Paul if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 194c0d7d4d4SBill Paul return (0); 195c0d7d4d4SBill Paul 196c0d7d4d4SBill Paul /* 197d9730b8bSJonathan Lemon * Only used for autonegotiation. 198d9730b8bSJonathan Lemon */ 199d9730b8bSJonathan Lemon if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 200d9730b8bSJonathan Lemon break; 201d9730b8bSJonathan Lemon 202d9730b8bSJonathan Lemon /* 203d9730b8bSJonathan Lemon * Check to see if we have link. If we do, we don't 204d9730b8bSJonathan Lemon * need to restart the autonegotiation process. Read 205d9730b8bSJonathan Lemon * the BMSR twice in case it's latched. 206d9730b8bSJonathan Lemon */ 207d9730b8bSJonathan Lemon reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 208d9730b8bSJonathan Lemon if (reg & BMSR_LINK) 209d9730b8bSJonathan Lemon break; 210d9730b8bSJonathan Lemon 211de1add1eSMarius Strobl /* Only retry autonegotiation every mii_anegticks seconds. */ 212de1add1eSMarius Strobl if (sc->mii_ticks <= sc->mii_anegticks) 2139a54cbb9SAndre Oppermann break; 214c0d7d4d4SBill Paul 215c0d7d4d4SBill Paul sc->mii_ticks = 0; 216c0d7d4d4SBill Paul 217c0d7d4d4SBill Paul mii_phy_reset(sc); 218fd94424cSPoul-Henning Kamp xmphy_mii_phy_auto(sc); 219c0d7d4d4SBill Paul return (0); 220c0d7d4d4SBill Paul } 221c0d7d4d4SBill Paul 222c0d7d4d4SBill Paul /* Update the media status. */ 223c0d7d4d4SBill Paul xmphy_status(sc); 224c0d7d4d4SBill Paul 225c0d7d4d4SBill Paul /* Callback if something changed. */ 226d9730b8bSJonathan Lemon mii_phy_update(sc, cmd); 227c0d7d4d4SBill Paul return (0); 228c0d7d4d4SBill Paul } 229c0d7d4d4SBill Paul 230d9730b8bSJonathan Lemon static void 2317d830ac9SWarner Losh xmphy_status(struct mii_softc *sc) 232c0d7d4d4SBill Paul { 233c0d7d4d4SBill Paul struct mii_data *mii = sc->mii_pdata; 234c0d7d4d4SBill Paul int bmsr, bmcr, anlpar; 235c0d7d4d4SBill Paul 236c0d7d4d4SBill Paul mii->mii_media_status = IFM_AVALID; 237c0d7d4d4SBill Paul mii->mii_media_active = IFM_ETHER; 238c0d7d4d4SBill Paul 239c0d7d4d4SBill Paul bmsr = PHY_READ(sc, XMPHY_MII_BMSR) | 240c0d7d4d4SBill Paul PHY_READ(sc, XMPHY_MII_BMSR); 241c0d7d4d4SBill Paul if (bmsr & XMPHY_BMSR_LINK) 242c0d7d4d4SBill Paul mii->mii_media_status |= IFM_ACTIVE; 243c0d7d4d4SBill Paul 244c0d7d4d4SBill Paul /* Do dummy read of extended status register. */ 245c0d7d4d4SBill Paul bmcr = PHY_READ(sc, XMPHY_MII_EXTSTS); 246c0d7d4d4SBill Paul 247c0d7d4d4SBill Paul bmcr = PHY_READ(sc, XMPHY_MII_BMCR); 248c0d7d4d4SBill Paul 249c0d7d4d4SBill Paul if (bmcr & XMPHY_BMCR_LOOP) 250c0d7d4d4SBill Paul mii->mii_media_active |= IFM_LOOP; 251c0d7d4d4SBill Paul 252c0d7d4d4SBill Paul 253c0d7d4d4SBill Paul if (bmcr & XMPHY_BMCR_AUTOEN) { 254c0d7d4d4SBill Paul if ((bmsr & XMPHY_BMSR_ACOMP) == 0) { 255c0d7d4d4SBill Paul if (bmsr & XMPHY_BMSR_LINK) { 256c0d7d4d4SBill Paul mii->mii_media_active |= IFM_1000_SX|IFM_HDX; 257c0d7d4d4SBill Paul return; 258c0d7d4d4SBill Paul } 259c0d7d4d4SBill Paul /* Erg, still trying, I guess... */ 260c0d7d4d4SBill Paul mii->mii_media_active |= IFM_NONE; 261c0d7d4d4SBill Paul return; 262c0d7d4d4SBill Paul } 263c0d7d4d4SBill Paul 264c0d7d4d4SBill Paul mii->mii_media_active |= IFM_1000_SX; 265c0d7d4d4SBill Paul anlpar = PHY_READ(sc, XMPHY_MII_ANAR) & 266c0d7d4d4SBill Paul PHY_READ(sc, XMPHY_MII_ANLPAR); 267c0d7d4d4SBill Paul if (anlpar & XMPHY_ANLPAR_FDX) 268c0d7d4d4SBill Paul mii->mii_media_active |= IFM_FDX; 269c0d7d4d4SBill Paul else 270c0d7d4d4SBill Paul mii->mii_media_active |= IFM_HDX; 271c0d7d4d4SBill Paul return; 272c0d7d4d4SBill Paul } 273c0d7d4d4SBill Paul 274c0d7d4d4SBill Paul mii->mii_media_active |= IFM_1000_SX; 275c0d7d4d4SBill Paul if (bmcr & XMPHY_BMCR_FDX) 276c0d7d4d4SBill Paul mii->mii_media_active |= IFM_FDX; 277c0d7d4d4SBill Paul else 278c0d7d4d4SBill Paul mii->mii_media_active |= IFM_HDX; 279c0d7d4d4SBill Paul } 280c0d7d4d4SBill Paul 281c0d7d4d4SBill Paul static int 2827d830ac9SWarner Losh xmphy_mii_phy_auto(struct mii_softc *mii) 283c0d7d4d4SBill Paul { 284fd94424cSPoul-Henning Kamp int anar = 0; 285c0d7d4d4SBill Paul 286134c58d3SBill Paul anar = PHY_READ(mii, XMPHY_MII_ANAR); 287134c58d3SBill Paul anar |= XMPHY_ANAR_FDX|XMPHY_ANAR_HDX; 288134c58d3SBill Paul PHY_WRITE(mii, XMPHY_MII_ANAR, anar); 289134c58d3SBill Paul DELAY(1000); 290c0d7d4d4SBill Paul PHY_WRITE(mii, XMPHY_MII_BMCR, 291c0d7d4d4SBill Paul XMPHY_BMCR_AUTOEN | XMPHY_BMCR_STARTNEG); 292c0d7d4d4SBill Paul 293c0d7d4d4SBill Paul return (EJUSTRETURN); 294c0d7d4d4SBill Paul } 295