1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2006 Benno Rice. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 /* $FreeBSD: head/sys/dev/mii/smscphy.c 326255 2017-11-27 14:52:40Z pfg $ */ 29 30 /* 31 * Driver for the SMSC LAN8710A 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/device.h> 38 #include <sys/socket.h> 39 #include <sys/errno.h> 40 41 #include <net/if.h> 42 #include <net/if_media.h> 43 44 #include <dev/mii/mii.h> 45 #include <dev/mii/miivar.h> 46 #include <dev/mii/miidevs.h> 47 48 /* PHY special control/status register */ 49 #define SMSCPHY_SPCSR 0x1f 50 #define SPCSR_SPDIND_10 0x0004 51 #define SPCSR_SPDIND_100 0x0008 52 #define SPCSR_SPDIND_SPDMASK 0x000c 53 #define SPCSR_SPDIND_FDX 0x0010 54 55 static int smscphy_match(device_t, cfdata_t, void *); 56 static void smscphy_attach(device_t, device_t, void *); 57 58 CFATTACH_DECL_NEW(smscphy, sizeof (struct mii_softc), 59 smscphy_match, smscphy_attach, mii_phy_detach, mii_phy_activate); 60 61 static void smscphy_power(struct mii_softc *, bool); 62 static int smscphy_service(struct mii_softc *, struct mii_data *, int); 63 static void smscphy_auto(struct mii_softc *, int); 64 static void smscphy_status(struct mii_softc *); 65 66 static const struct mii_phydesc smscphys[] = { 67 MII_PHY_DESC(SMSC, LAN8700), 68 MII_PHY_DESC(SMSC, LAN8710_LAN8720), 69 MII_PHY_END 70 }; 71 72 static const struct mii_phy_funcs smscphy_funcs = { 73 smscphy_service, 74 smscphy_status, 75 mii_phy_reset 76 }; 77 78 static int 79 smscphy_match(device_t dev, cfdata_t match, void *aux) 80 { 81 struct mii_attach_args *ma = aux; 82 83 if (mii_phy_match(ma, smscphys) != NULL) 84 return 10; 85 86 return 0; 87 } 88 89 static void 90 smscphy_attach(device_t parent, device_t self, void *aux) 91 { 92 struct mii_softc *sc = device_private(self); 93 struct mii_attach_args *ma = aux; 94 struct mii_data *mii = ma->mii_data; 95 const struct mii_phydesc *mpd; 96 97 mpd = mii_phy_match(ma, smscphys); 98 aprint_naive(": Media interface\n"); 99 aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 100 101 sc->mii_dev = self; 102 sc->mii_inst = mii->mii_instance; 103 sc->mii_phy = ma->mii_phyno; 104 sc->mii_funcs = &smscphy_funcs; 105 sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2); 106 sc->mii_mpd_model = MII_MODEL(ma->mii_id2); 107 sc->mii_mpd_rev = MII_REV(ma->mii_id2); 108 sc->mii_pdata = mii; 109 sc->mii_flags = ma->mii_flags; 110 111 mii_lock(mii); 112 113 PHY_RESET(sc); 114 115 PHY_READ(sc, MII_BMSR, &sc->mii_capabilities); 116 sc->mii_capabilities &= ma->mii_capmask; 117 118 mii_unlock(mii); 119 120 mii_phy_add_media(sc); 121 } 122 123 static void 124 smscphy_power(struct mii_softc *sc, bool power) 125 { 126 uint16_t bmcr, new; 127 128 PHY_READ(sc, MII_BMCR, &bmcr); 129 if (power) 130 new = bmcr & ~BMCR_PDOWN; 131 else 132 new = bmcr | BMCR_PDOWN; 133 if (bmcr != new) 134 PHY_WRITE(sc, MII_BMCR, new); 135 } 136 137 static int 138 smscphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 139 { 140 struct ifmedia_entry *ife; 141 uint16_t reg; 142 143 KASSERT(mii_locked(mii)); 144 145 ife = mii->mii_media.ifm_cur; 146 147 switch (cmd) { 148 case MII_POLLSTAT: 149 break; 150 151 case MII_MEDIACHG: 152 /* Try to power up the PHY in case it's down */ 153 if (IFM_SUBTYPE(ife->ifm_media) != IFM_NONE) 154 smscphy_power(sc, true); 155 156 switch (IFM_SUBTYPE(ife->ifm_media)) { 157 case IFM_AUTO: 158 smscphy_auto(sc, ife->ifm_media); 159 break; 160 161 default: 162 mii_phy_setmedia(sc); 163 if (IFM_SUBTYPE(ife->ifm_media) == IFM_NONE) 164 smscphy_power(sc, false); 165 break; 166 } 167 break; 168 169 case MII_TICK: 170 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 171 break; 172 173 PHY_READ(sc, MII_BMSR, ®); 174 PHY_READ(sc, MII_BMSR, ®); 175 if (reg & BMSR_LINK) { 176 sc->mii_ticks = 0; 177 break; 178 } 179 180 if (++sc->mii_ticks <= MII_ANEGTICKS) 181 break; 182 183 PHY_RESET(sc); 184 smscphy_auto(sc, ife->ifm_media); 185 break; 186 } 187 188 /* Update the media status. */ 189 PHY_STATUS(sc); 190 191 /* Callback if something changed. */ 192 mii_phy_update(sc, cmd); 193 return 0; 194 } 195 196 static void 197 smscphy_auto(struct mii_softc *sc, int media) 198 { 199 uint16_t anar; 200 201 sc->mii_ticks = 0; 202 anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA; 203 if ((media & IFM_FLOW) != 0) 204 anar |= ANAR_FC; 205 PHY_WRITE(sc, MII_ANAR, anar); 206 /* Apparently this helps. */ 207 PHY_READ(sc, MII_ANAR, &anar); 208 PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG); 209 } 210 211 static void 212 smscphy_status(struct mii_softc *sc) 213 { 214 struct mii_data *mii = sc->mii_pdata; 215 uint16_t bmcr, bmsr, status; 216 217 KASSERT(mii_locked(mii)); 218 219 mii->mii_media_status = IFM_AVALID; 220 mii->mii_media_active = IFM_ETHER; 221 222 PHY_READ(sc, MII_BMSR, &bmsr); 223 PHY_READ(sc, MII_BMSR, &bmsr); 224 if ((bmsr & BMSR_LINK) != 0) 225 mii->mii_media_status |= IFM_ACTIVE; 226 227 PHY_READ(sc, MII_BMCR, &bmcr); 228 if ((bmcr & BMCR_ISO) != 0) { 229 mii->mii_media_active |= IFM_NONE; 230 mii->mii_media_status = 0; 231 return; 232 } 233 234 if ((bmcr & BMCR_LOOP) != 0) 235 mii->mii_media_active |= IFM_LOOP; 236 237 if ((bmcr & BMCR_AUTOEN) != 0) { 238 if ((bmsr & BMSR_ACOMP) == 0) { 239 /* Erg, still trying, I guess... */ 240 mii->mii_media_active |= IFM_NONE; 241 return; 242 } 243 } 244 245 PHY_READ(sc, SMSCPHY_SPCSR, &status); 246 if ((status & SPCSR_SPDIND_SPDMASK) == SPCSR_SPDIND_100) 247 mii->mii_media_active |= IFM_100_TX; 248 else 249 mii->mii_media_active |= IFM_10_T; 250 if (status & SPCSR_SPDIND_FDX) 251 mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 252 else 253 mii->mii_media_active |= IFM_HDX; 254 } 255