1 /*- 2 * Copyright (c) 2006, Pyun YongHyeon <yongari@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * 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 AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 /* 33 * Driver for the IC Plus IP1000A/IP1001 10/100/1000 PHY. 34 */ 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/module.h> 40 #include <sys/socket.h> 41 #include <sys/bus.h> 42 43 #include <net/if.h> 44 #include <net/if_var.h> 45 #include <net/if_media.h> 46 47 #include <dev/mii/mii.h> 48 #include <dev/mii/miivar.h> 49 #include "miidevs.h" 50 51 #include <dev/mii/ip1000phyreg.h> 52 53 #include "miibus_if.h" 54 55 #include <machine/bus.h> 56 #include <dev/stge/if_stgereg.h> 57 58 static int ip1000phy_probe(device_t); 59 static int ip1000phy_attach(device_t); 60 61 static device_method_t ip1000phy_methods[] = { 62 /* device interface */ 63 DEVMETHOD(device_probe, ip1000phy_probe), 64 DEVMETHOD(device_attach, ip1000phy_attach), 65 DEVMETHOD(device_detach, mii_phy_detach), 66 DEVMETHOD(device_shutdown, bus_generic_shutdown), 67 DEVMETHOD_END 68 }; 69 70 static devclass_t ip1000phy_devclass; 71 static driver_t ip1000phy_driver = { 72 "ip1000phy", 73 ip1000phy_methods, 74 sizeof(struct mii_softc) 75 }; 76 77 DRIVER_MODULE(ip1000phy, miibus, ip1000phy_driver, ip1000phy_devclass, 0, 0); 78 79 static int ip1000phy_service(struct mii_softc *, struct mii_data *, int); 80 static void ip1000phy_status(struct mii_softc *); 81 static void ip1000phy_reset(struct mii_softc *); 82 static int ip1000phy_mii_phy_auto(struct mii_softc *, int); 83 84 static const struct mii_phydesc ip1000phys[] = { 85 MII_PHY_DESC(xxICPLUS, IP1000A), 86 MII_PHY_DESC(xxICPLUS, IP1001), 87 MII_PHY_END 88 }; 89 90 static const struct mii_phy_funcs ip1000phy_funcs = { 91 ip1000phy_service, 92 ip1000phy_status, 93 ip1000phy_reset 94 }; 95 96 static int 97 ip1000phy_probe(device_t dev) 98 { 99 100 return (mii_phy_dev_probe(dev, ip1000phys, BUS_PROBE_DEFAULT)); 101 } 102 103 static int 104 ip1000phy_attach(device_t dev) 105 { 106 struct mii_attach_args *ma; 107 u_int flags; 108 109 ma = device_get_ivars(dev); 110 flags = MIIF_NOISOLATE | MIIF_NOMANPAUSE; 111 if (MII_MODEL(ma->mii_id2) == MII_MODEL_xxICPLUS_IP1000A && 112 strcmp(ma->mii_data->mii_ifp->if_dname, "stge") == 0 && 113 (miibus_get_flags(dev) & MIIF_MACPRIV0) != 0) 114 flags |= MIIF_PHYPRIV0; 115 mii_phy_dev_attach(dev, flags, &ip1000phy_funcs, 1); 116 return (0); 117 } 118 119 static int 120 ip1000phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 121 { 122 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 123 uint32_t gig, reg, speed; 124 125 switch (cmd) { 126 case MII_POLLSTAT: 127 break; 128 129 case MII_MEDIACHG: 130 PHY_RESET(sc); 131 switch (IFM_SUBTYPE(ife->ifm_media)) { 132 case IFM_AUTO: 133 (void)ip1000phy_mii_phy_auto(sc, ife->ifm_media); 134 goto done; 135 136 case IFM_1000_T: 137 /* 138 * XXX 139 * Manual 1000baseT setting doesn't seem to work. 140 */ 141 speed = IP1000PHY_BMCR_1000; 142 break; 143 144 case IFM_100_TX: 145 speed = IP1000PHY_BMCR_100; 146 break; 147 148 case IFM_10_T: 149 speed = IP1000PHY_BMCR_10; 150 break; 151 152 default: 153 return (EINVAL); 154 } 155 156 if ((ife->ifm_media & IFM_FDX) != 0) { 157 speed |= IP1000PHY_BMCR_FDX; 158 gig = IP1000PHY_1000CR_1000T_FDX; 159 } else 160 gig = IP1000PHY_1000CR_1000T; 161 162 if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 163 gig |= 164 IP1000PHY_1000CR_MASTER | IP1000PHY_1000CR_MANUAL; 165 if ((ife->ifm_media & IFM_ETH_MASTER) != 0) 166 gig |= IP1000PHY_1000CR_MMASTER; 167 } else 168 gig = 0; 169 PHY_WRITE(sc, IP1000PHY_MII_1000CR, gig); 170 PHY_WRITE(sc, IP1000PHY_MII_BMCR, speed); 171 172 done: 173 break; 174 175 case MII_TICK: 176 /* 177 * Only used for autonegotiation. 178 */ 179 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 180 sc->mii_ticks = 0; 181 break; 182 } 183 184 /* 185 * check for link. 186 */ 187 reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 188 if (reg & BMSR_LINK) { 189 sc->mii_ticks = 0; 190 break; 191 } 192 193 /* Announce link loss right after it happens */ 194 if (sc->mii_ticks++ == 0) 195 break; 196 197 /* 198 * Only retry autonegotiation every mii_anegticks seconds. 199 */ 200 if (sc->mii_ticks <= sc->mii_anegticks) 201 break; 202 203 sc->mii_ticks = 0; 204 ip1000phy_mii_phy_auto(sc, ife->ifm_media); 205 break; 206 } 207 208 /* Update the media status. */ 209 PHY_STATUS(sc); 210 211 /* Callback if something changed. */ 212 mii_phy_update(sc, cmd); 213 return (0); 214 } 215 216 static void 217 ip1000phy_status(struct mii_softc *sc) 218 { 219 struct mii_data *mii = sc->mii_pdata; 220 uint32_t bmsr, bmcr, stat; 221 222 mii->mii_media_status = IFM_AVALID; 223 mii->mii_media_active = IFM_ETHER; 224 225 bmsr = PHY_READ(sc, IP1000PHY_MII_BMSR) | 226 PHY_READ(sc, IP1000PHY_MII_BMSR); 227 if ((bmsr & IP1000PHY_BMSR_LINK) != 0) 228 mii->mii_media_status |= IFM_ACTIVE; 229 230 bmcr = PHY_READ(sc, IP1000PHY_MII_BMCR); 231 if ((bmcr & IP1000PHY_BMCR_LOOP) != 0) 232 mii->mii_media_active |= IFM_LOOP; 233 234 if ((bmcr & IP1000PHY_BMCR_AUTOEN) != 0) { 235 if ((bmsr & IP1000PHY_BMSR_ANEGCOMP) == 0) { 236 /* Erg, still trying, I guess... */ 237 mii->mii_media_active |= IFM_NONE; 238 return; 239 } 240 } 241 242 if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) { 243 stat = PHY_READ(sc, IP1000PHY_LSR); 244 switch (stat & IP1000PHY_LSR_SPEED_MASK) { 245 case IP1000PHY_LSR_SPEED_10: 246 mii->mii_media_active |= IFM_10_T; 247 break; 248 case IP1000PHY_LSR_SPEED_100: 249 mii->mii_media_active |= IFM_100_TX; 250 break; 251 case IP1000PHY_LSR_SPEED_1000: 252 mii->mii_media_active |= IFM_1000_T; 253 break; 254 default: 255 mii->mii_media_active |= IFM_NONE; 256 return; 257 } 258 if ((stat & IP1000PHY_LSR_FULL_DUPLEX) != 0) 259 mii->mii_media_active |= IFM_FDX; 260 else 261 mii->mii_media_active |= IFM_HDX; 262 } else { 263 stat = PHY_READ(sc, STGE_PhyCtrl); 264 switch (PC_LinkSpeed(stat)) { 265 case PC_LinkSpeed_Down: 266 mii->mii_media_active |= IFM_NONE; 267 return; 268 case PC_LinkSpeed_10: 269 mii->mii_media_active |= IFM_10_T; 270 break; 271 case PC_LinkSpeed_100: 272 mii->mii_media_active |= IFM_100_TX; 273 break; 274 case PC_LinkSpeed_1000: 275 mii->mii_media_active |= IFM_1000_T; 276 break; 277 default: 278 mii->mii_media_active |= IFM_NONE; 279 return; 280 } 281 if ((stat & PC_PhyDuplexStatus) != 0) 282 mii->mii_media_active |= IFM_FDX; 283 else 284 mii->mii_media_active |= IFM_HDX; 285 } 286 287 if ((mii->mii_media_active & IFM_FDX) != 0) 288 mii->mii_media_active |= mii_phy_flowstatus(sc); 289 290 if ((mii->mii_media_active & IFM_1000_T) != 0) { 291 stat = PHY_READ(sc, IP1000PHY_MII_1000SR); 292 if ((stat & IP1000PHY_1000SR_MASTER) != 0) 293 mii->mii_media_active |= IFM_ETH_MASTER; 294 } 295 } 296 297 static int 298 ip1000phy_mii_phy_auto(struct mii_softc *sc, int media) 299 { 300 uint32_t reg; 301 302 reg = 0; 303 if (sc->mii_mpd_model == MII_MODEL_xxICPLUS_IP1001) { 304 reg = PHY_READ(sc, IP1000PHY_MII_ANAR); 305 reg &= ~(IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE); 306 reg |= IP1000PHY_ANAR_NP; 307 } 308 reg |= IP1000PHY_ANAR_10T | IP1000PHY_ANAR_10T_FDX | 309 IP1000PHY_ANAR_100TX | IP1000PHY_ANAR_100TX_FDX; 310 if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0) 311 reg |= IP1000PHY_ANAR_PAUSE | IP1000PHY_ANAR_APAUSE; 312 PHY_WRITE(sc, IP1000PHY_MII_ANAR, reg | IP1000PHY_ANAR_CSMA); 313 314 reg = IP1000PHY_1000CR_1000T | IP1000PHY_1000CR_1000T_FDX; 315 if (sc->mii_mpd_model != MII_MODEL_xxICPLUS_IP1001) 316 reg |= IP1000PHY_1000CR_MASTER; 317 PHY_WRITE(sc, IP1000PHY_MII_1000CR, reg); 318 PHY_WRITE(sc, IP1000PHY_MII_BMCR, (IP1000PHY_BMCR_FDX | 319 IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_STARTNEG)); 320 321 return (EJUSTRETURN); 322 } 323 324 static void 325 ip1000phy_load_dspcode(struct mii_softc *sc) 326 { 327 328 PHY_WRITE(sc, 31, 0x0001); 329 PHY_WRITE(sc, 27, 0x01e0); 330 PHY_WRITE(sc, 31, 0x0002); 331 PHY_WRITE(sc, 27, 0xeb8e); 332 PHY_WRITE(sc, 31, 0x0000); 333 PHY_WRITE(sc, 30, 0x005e); 334 PHY_WRITE(sc, 9, 0x0700); 335 336 DELAY(50); 337 } 338 339 static void 340 ip1000phy_reset(struct mii_softc *sc) 341 { 342 uint32_t reg; 343 344 mii_phy_reset(sc); 345 346 /* clear autoneg/full-duplex as we don't want it after reset */ 347 reg = PHY_READ(sc, IP1000PHY_MII_BMCR); 348 reg &= ~(IP1000PHY_BMCR_AUTOEN | IP1000PHY_BMCR_FDX); 349 PHY_WRITE(sc, MII_BMCR, reg); 350 351 if ((sc->mii_flags & MIIF_PHYPRIV0) != 0) 352 ip1000phy_load_dspcode(sc); 353 } 354