1 /* $OpenBSD: jmphy.c,v 1.2 2008/10/20 00:05:38 brad 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/kernel.h> 39 #include <sys/device.h> 40 #include <sys/socket.h> 41 42 #include <net/if.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, mii_phy_activate 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 jmphy_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; 258 else 259 mii->mii_media_active |= IFM_HDX; 260 261 /* XXX Flow-control. */ 262 263 if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { 264 if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0) 265 mii->mii_media_active |= IFM_ETH_MASTER; 266 } 267 } 268 269 void 270 jmphy_reset(struct mii_softc *sc) 271 { 272 int i; 273 274 /* Disable sleep mode. */ 275 PHY_WRITE(sc, JMPHY_TMCTL, 276 PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB); 277 PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN); 278 279 for (i = 0; i < 1000; i++) { 280 DELAY(1); 281 if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0) 282 break; 283 } 284 } 285 286 uint16_t 287 jmphy_anar(struct ifmedia_entry *ife) 288 { 289 uint16_t anar; 290 291 anar = 0; 292 switch (IFM_SUBTYPE(ife->ifm_media)) { 293 case IFM_AUTO: 294 anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10; 295 break; 296 case IFM_1000_T: 297 break; 298 case IFM_100_TX: 299 anar |= ANAR_TX | ANAR_TX_FD; 300 break; 301 case IFM_10_T: 302 anar |= ANAR_10 | ANAR_10_FD; 303 break; 304 default: 305 break; 306 } 307 308 return (anar); 309 } 310 311 int 312 jmphy_auto(struct mii_softc *sc, struct ifmedia_entry *ife) 313 { 314 uint16_t anar, bmcr, gig; 315 316 gig = 0; 317 bmcr = PHY_READ(sc, MII_BMCR); 318 switch (IFM_SUBTYPE(ife->ifm_media)) { 319 case IFM_AUTO: 320 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; 321 break; 322 case IFM_1000_T: 323 gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; 324 break; 325 case IFM_100_TX: 326 case IFM_10_T: 327 break; 328 case IFM_NONE: 329 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN); 330 return (EJUSTRETURN); 331 default: 332 return (EINVAL); 333 } 334 335 if ((ife->ifm_media & IFM_LOOP) != 0) 336 bmcr |= BMCR_LOOP; 337 338 anar = jmphy_anar(ife); 339 /* XXX Always advertise pause capability. */ 340 anar |= (3 << 10); 341 342 if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) { 343 #ifdef notyet 344 struct mii_data *mii; 345 346 mii = sc->mii_pdata; 347 if ((mii->mii_media.ifm_media & IFM_ETH_MASTER) != 0) 348 gig |= GTCR_MAN_MS | GTCR_MAN_ADV; 349 #endif 350 PHY_WRITE(sc, MII_100T2CR, gig); 351 } 352 PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA); 353 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG); 354 355 return (EJUSTRETURN); 356 } 357