1 /* $OpenBSD: jmphy.c,v 1.6 2015/03/14 03:38:48 jsg Exp $ */ 2 /*- 3 * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> 4 * 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 unmodified, this list of conditions, and the following 11 * 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: src/sys/dev/mii/jmphy.c,v 1.1 2008/05/27 01:16:40 yongari Exp $ 29 * $DragonFly: src/sys/dev/netif/mii_layer/jmphy.c,v 1.1 2008/07/22 11:28:49 sephe Exp $ 30 */ 31 32 /* 33 * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY. 34 */ 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/device.h> 39 #include <sys/socket.h> 40 41 #include <net/if.h> 42 #include <net/if_var.h> 43 #include <net/if_media.h> 44 45 #include <dev/mii/mii.h> 46 #include <dev/mii/miivar.h> 47 #include <dev/mii/miidevs.h> 48 #include <dev/mii/jmphyreg.h> 49 50 int jmphy_service(struct mii_softc *, struct mii_data *, int); 51 void jmphy_status(struct mii_softc *); 52 int jmphy_match(struct device *, void *, void *); 53 void jmphy_attach(struct device *, struct device *, void *); 54 void jmphy_reset(struct mii_softc *); 55 uint16_t jmphy_anar(struct ifmedia_entry *); 56 int jmphy_auto(struct mii_softc *, struct ifmedia_entry *); 57 58 const struct mii_phy_funcs jmphy_funcs = { 59 jmphy_service, jmphy_status, jmphy_reset, 60 }; 61 62 struct cfattach jmphy_ca = { 63 sizeof (struct mii_softc), jmphy_match, jmphy_attach, 64 mii_phy_detach 65 }; 66 67 struct cfdriver jmphy_cd = { 68 NULL, "jmphy", DV_DULL 69 }; 70 71 static const struct mii_phydesc jmphys[] = { 72 { MII_OUI_JMICRON, MII_MODEL_JMICRON_JMP202, 73 MII_STR_JMICRON_JMP202 }, 74 { MII_OUI_JMICRON, MII_MODEL_JMICRON_JMP211, 75 MII_STR_JMICRON_JMP211 }, 76 { 0, 0, 77 NULL }, 78 }; 79 80 int 81 jmphy_match(struct device *parent, void *match, void *aux) 82 { 83 struct mii_attach_args *ma = aux; 84 85 if (mii_phy_match(ma, jmphys) != NULL) 86 return (10); 87 88 return (0); 89 } 90 91 void 92 jmphy_attach(struct device *parent, struct device *self, void *aux) 93 { 94 struct mii_softc *sc = (struct mii_softc *)self; 95 struct mii_attach_args *ma = aux; 96 struct mii_data *mii = ma->mii_data; 97 const struct mii_phydesc *mpd; 98 99 mpd = mii_phy_match(ma, jmphys); 100 printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 101 102 sc->mii_inst = mii->mii_instance; 103 sc->mii_phy = ma->mii_phyno; 104 sc->mii_funcs = &jmphy_funcs; 105 sc->mii_model = MII_MODEL(ma->mii_id2); 106 sc->mii_pdata = mii; 107 sc->mii_flags = ma->mii_flags; 108 109 sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP; 110 111 PHY_RESET(sc); 112 113 sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 114 if (sc->mii_capabilities & BMSR_EXTSTAT) 115 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 116 117 if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && 118 (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) 119 ; 120 else 121 mii_phy_add_media(sc); 122 } 123 124 int 125 jmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 126 { 127 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 128 uint16_t bmcr; 129 130 switch (cmd) { 131 case MII_POLLSTAT: 132 /* 133 * If we're not polling our PHY instance, just return. 134 */ 135 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 136 return (0); 137 break; 138 139 case MII_MEDIACHG: 140 /* 141 * If the media indicates a different PHY instance, 142 * isolate ourselves. 143 */ 144 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 145 bmcr = PHY_READ(sc, MII_BMCR); 146 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); 147 return (0); 148 } 149 150 /* 151 * If the interface is not up, don't do anything. 152 */ 153 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 154 break; 155 156 if (jmphy_auto(sc, ife) != EJUSTRETURN) 157 return (EINVAL); 158 break; 159 160 case MII_TICK: 161 /* 162 * If we're not currently selected, just return. 163 */ 164 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 165 return (0); 166 167 /* 168 * Is the interface even up? 169 */ 170 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 171 return (0); 172 173 /* 174 * Only used for autonegotiation. 175 */ 176 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) 177 break; 178 179 /* Check for link. */ 180 if (PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) { 181 sc->mii_ticks = 0; 182 break; 183 } 184 185 /* Announce link loss right after it happens. */ 186 if (sc->mii_ticks++ == 0) 187 break; 188 if (sc->mii_ticks <= sc->mii_anegticks) 189 return (0); 190 191 sc->mii_ticks = 0; 192 jmphy_auto(sc, ife); 193 break; 194 } 195 196 /* Update the media status. */ 197 jmphy_status(sc); 198 199 /* Callback if something changed. */ 200 mii_phy_update(sc, cmd); 201 return (0); 202 } 203 204 void 205 jmphy_status(struct mii_softc *sc) 206 { 207 struct mii_data *mii = sc->mii_pdata; 208 int bmcr, ssr; 209 210 mii->mii_media_status = IFM_AVALID; 211 mii->mii_media_active = IFM_ETHER; 212 213 ssr = PHY_READ(sc, JMPHY_SSR); 214 if ((ssr & JMPHY_SSR_LINK_UP) != 0) 215 mii->mii_media_status |= IFM_ACTIVE; 216 217 bmcr = PHY_READ(sc, MII_BMCR); 218 if ((bmcr & BMCR_ISO) != 0) { 219 mii->mii_media_active |= IFM_NONE; 220 mii->mii_media_status = 0; 221 return; 222 } 223 224 if ((bmcr & BMCR_LOOP) != 0) 225 mii->mii_media_active |= IFM_LOOP; 226 227 if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) { 228 /* Erg, still trying, I guess... */ 229 mii->mii_media_active |= IFM_NONE; 230 return; 231 } 232 233 switch ((ssr & JMPHY_SSR_SPEED_MASK)) { 234 case JMPHY_SSR_SPEED_1000: 235 mii->mii_media_active |= IFM_1000_T; 236 /* 237 * jmphy(4) got a valid link so reset mii_ticks. 238 * Resetting mii_ticks is needed in order to 239 * detect link loss after auto-negotiation. 240 */ 241 sc->mii_ticks = 0; 242 break; 243 case JMPHY_SSR_SPEED_100: 244 mii->mii_media_active |= IFM_100_TX; 245 sc->mii_ticks = 0; 246 break; 247 case JMPHY_SSR_SPEED_10: 248 mii->mii_media_active |= IFM_10_T; 249 sc->mii_ticks = 0; 250 break; 251 default: 252 mii->mii_media_active |= IFM_NONE; 253 return; 254 } 255 256 if ((ssr & JMPHY_SSR_DUPLEX) != 0) 257 mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 258 else 259 mii->mii_media_active |= IFM_HDX; 260 261 if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { 262 if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0) 263 mii->mii_media_active |= IFM_ETH_MASTER; 264 } 265 } 266 267 void 268 jmphy_reset(struct mii_softc *sc) 269 { 270 int i; 271 272 /* Disable sleep mode. */ 273 PHY_WRITE(sc, JMPHY_TMCTL, 274 PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB); 275 PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN); 276 277 for (i = 0; i < 1000; i++) { 278 DELAY(1); 279 if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0) 280 break; 281 } 282 } 283 284 uint16_t 285 jmphy_anar(struct ifmedia_entry *ife) 286 { 287 uint16_t anar; 288 289 anar = 0; 290 switch (IFM_SUBTYPE(ife->ifm_media)) { 291 case IFM_AUTO: 292 anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10; 293 break; 294 case IFM_1000_T: 295 break; 296 case IFM_100_TX: 297 anar |= ANAR_TX | ANAR_TX_FD; 298 break; 299 case IFM_10_T: 300 anar |= ANAR_10 | ANAR_10_FD; 301 break; 302 default: 303 break; 304 } 305 306 return (anar); 307 } 308 309 int 310 jmphy_auto(struct mii_softc *sc, struct ifmedia_entry *ife) 311 { 312 uint16_t anar, bmcr, gig; 313 314 gig = 0; 315 bmcr = PHY_READ(sc, MII_BMCR); 316 switch (IFM_SUBTYPE(ife->ifm_media)) { 317 case IFM_AUTO: 318 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; 319 break; 320 case IFM_1000_T: 321 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; 322 break; 323 case IFM_100_TX: 324 case IFM_10_T: 325 break; 326 case IFM_NONE: 327 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN); 328 return (EJUSTRETURN); 329 default: 330 return (EINVAL); 331 } 332 333 if ((ife->ifm_media & IFM_LOOP) != 0) 334 bmcr |= BMCR_LOOP; 335 336 anar = jmphy_anar(ife); 337 if (sc->mii_flags & MIIF_DOPAUSE) 338 anar |= ANAR_PAUSE_TOWARDS; 339 340 if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) { 341 #ifdef notyet 342 struct mii_data *mii; 343 344 mii = sc->mii_pdata; 345 if ((mii->mii_media.ifm_media & IFM_ETH_MASTER) != 0) 346 gig |= GTCR_MAN_MS | GTCR_MAN_ADV; 347 #endif 348 PHY_WRITE(sc, MII_100T2CR, gig); 349 } 350 PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA); 351 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG); 352 353 return (EJUSTRETURN); 354 } 355