1 /* 2 * Copyright (c) 2007 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Sepherosa Ziehau <sepherosa@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sys/dev/netif/mii_layer/truephy.c,v 1.1 2007/10/12 14:12:42 sephe Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/bus.h> 39 #include <sys/kernel.h> 40 41 #include <net/if.h> 42 #include <net/if_media.h> 43 44 #include <dev/netif/mii_layer/mii.h> 45 #include <dev/netif/mii_layer/miivar.h> 46 #include <dev/netif/mii_layer/miidevs.h> 47 #include <dev/netif/mii_layer/truephyreg.h> 48 49 #include "miibus_if.h" 50 51 static int truephy_service(struct mii_softc *, struct mii_data *, int); 52 static int truephy_attach(device_t); 53 static int truephy_probe(device_t); 54 static void truephy_reset(struct mii_softc *); 55 static void truephy_status(struct mii_softc *); 56 57 static device_method_t truephy_methods[] = { 58 /* device interface */ 59 DEVMETHOD(device_probe, truephy_probe), 60 DEVMETHOD(device_attach, truephy_attach), 61 DEVMETHOD(device_detach, ukphy_detach), 62 DEVMETHOD(device_shutdown, bus_generic_shutdown), 63 { 0, 0 } 64 }; 65 66 static const struct mii_phydesc truephys[] = { 67 MII_PHYDESC(AGERE, ET1011C), 68 MII_PHYDESC_NULL 69 }; 70 71 static devclass_t truephy_devclass; 72 73 static driver_t truephy_driver = { 74 "truephy", 75 truephy_methods, 76 sizeof(struct mii_softc) 77 }; 78 79 DRIVER_MODULE(truephy, miibus, truephy_driver, truephy_devclass, 0, 0); 80 81 static const struct truephy_dsp { 82 uint16_t index; 83 uint16_t data; 84 } truephy_dspcode[] = { 85 { 0x880b, 0x0926 }, /* AfeIfCreg4B1000Msbs */ 86 { 0x880c, 0x0926 }, /* AfeIfCreg4B100Msbs */ 87 { 0x880d, 0x0926 }, /* AfeIfCreg4B10Msbs */ 88 89 { 0x880e, 0xb4d3 }, /* AfeIfCreg4B1000Lsbs */ 90 { 0x880f, 0xb4d3 }, /* AfeIfCreg4B100Lsbs */ 91 { 0x8810, 0xb4d3 }, /* AfeIfCreg4B10Lsbs */ 92 93 { 0x8805, 0xb03e }, /* AfeIfCreg3B1000Msbs */ 94 { 0x8806, 0xb03e }, /* AfeIfCreg3B100Msbs */ 95 { 0x8807, 0xff00 }, /* AfeIfCreg3B10Msbs */ 96 97 { 0x8808, 0xe090 }, /* AfeIfCreg3B1000Lsbs */ 98 { 0x8809, 0xe110 }, /* AfeIfCreg3B100Lsbs */ 99 { 0x880a, 0x0000 }, /* AfeIfCreg3B10Lsbs */ 100 101 { 0x300d, 1 }, /* DisableNorm */ 102 103 { 0x280c, 0x0180 }, /* LinkHoldEnd */ 104 105 { 0x1c21, 0x0002 }, /* AlphaM */ 106 107 { 0x3821, 6 }, /* FfeLkgTx0 */ 108 { 0x381d, 1 }, /* FfeLkg1g4 */ 109 { 0x381e, 1 }, /* FfeLkg1g5 */ 110 { 0x381f, 1 }, /* FfeLkg1g6 */ 111 { 0x3820, 1 }, /* FfeLkg1g7 */ 112 113 { 0x8402, 0x01f0 }, /* Btinact */ 114 { 0x800e, 20 }, /* LftrainTime */ 115 { 0x800f, 24 }, /* DvguardTime */ 116 { 0x8010, 46 } /* IdlguardTime */ 117 }; 118 119 static int 120 truephy_probe(device_t dev) 121 { 122 struct mii_attach_args *ma = device_get_ivars(dev); 123 const struct mii_phydesc *mpd; 124 125 mpd = mii_phy_match(ma, truephys); 126 if (mpd != NULL) { 127 device_set_desc(dev, mpd->mpd_name); 128 return 0; 129 } 130 return ENXIO; 131 } 132 133 static int 134 truephy_attach(device_t dev) 135 { 136 struct mii_softc *sc; 137 struct mii_attach_args *ma; 138 struct mii_data *mii; 139 140 sc = device_get_softc(dev); 141 ma = device_get_ivars(dev); 142 143 mii_softc_init(sc, ma); 144 sc->mii_dev = device_get_parent(dev); 145 mii = device_get_softc(sc->mii_dev); 146 LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); 147 148 sc->mii_inst = mii->mii_instance; 149 sc->mii_service = truephy_service; 150 sc->mii_reset = truephy_reset; 151 sc->mii_pdata = mii; 152 153 sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP; 154 155 mii->mii_instance++; 156 157 truephy_reset(sc); 158 159 sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; 160 if (sc->mii_capabilities & BMSR_EXTSTAT) { 161 sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); 162 /* No 1000baseT half-duplex support */ 163 sc->mii_extcapabilities &= ~EXTSR_1000THDX; 164 } 165 166 device_printf(dev, " "); 167 if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && 168 (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) 169 kprintf("no media present"); 170 else 171 mii_phy_add_media(sc); 172 kprintf("\n"); 173 174 MIIBUS_MEDIAINIT(sc->mii_dev); 175 return 0; 176 } 177 178 static int 179 truephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 180 { 181 struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 182 int bmcr; 183 184 switch (cmd) { 185 case MII_POLLSTAT: 186 /* 187 * If we're not polling our PHY instance, just return. 188 */ 189 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 190 return 0; 191 break; 192 193 case MII_MEDIACHG: 194 /* 195 * If the media indicates a different PHY instance, 196 * isolate ourselves. 197 */ 198 if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 199 bmcr = PHY_READ(sc, MII_BMCR); 200 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); 201 return 0; 202 } 203 204 /* 205 * If the interface is not up, don't do anything. 206 */ 207 if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 208 break; 209 210 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 211 bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_AUTOEN; 212 PHY_WRITE(sc, MII_BMCR, bmcr); 213 PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_PDOWN); 214 } 215 216 mii_phy_set_media(sc); 217 218 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 219 bmcr = PHY_READ(sc, MII_BMCR) & ~BMCR_PDOWN; 220 PHY_WRITE(sc, MII_BMCR, bmcr); 221 222 if (IFM_SUBTYPE(ife->ifm_media) == IFM_1000_T) { 223 PHY_WRITE(sc, MII_BMCR, 224 bmcr | BMCR_AUTOEN | BMCR_STARTNEG); 225 } 226 } 227 break; 228 229 case MII_TICK: 230 /* 231 * If we're not currently selected, just return. 232 */ 233 if (IFM_INST(ife->ifm_media) != sc->mii_inst) 234 return 0; 235 236 if (mii_phy_tick(sc) == EJUSTRETURN) 237 return 0; 238 break; 239 } 240 241 /* Update the media status. */ 242 truephy_status(sc); 243 244 /* Callback if something changed. */ 245 mii_phy_update(sc, cmd); 246 return 0; 247 } 248 249 static void 250 truephy_reset(struct mii_softc *sc) 251 { 252 int i; 253 254 for (i = 0; i < 2; ++i) { 255 PHY_READ(sc, MII_PHYIDR1); 256 PHY_READ(sc, MII_PHYIDR2); 257 258 PHY_READ(sc, TRUEPHY_CTRL); 259 PHY_WRITE(sc, TRUEPHY_CTRL, 0x6); 260 261 PHY_WRITE(sc, TRUEPHY_INDEX, 0x402); 262 PHY_READ(sc, TRUEPHY_DATA); 263 264 PHY_WRITE(sc, TRUEPHY_CTRL, 0x2); 265 } 266 267 PHY_READ(sc, MII_BMCR); 268 PHY_READ(sc, TRUEPHY_CTRL); 269 PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_PDOWN | BMCR_S1000); 270 PHY_WRITE(sc, TRUEPHY_CTRL, 0x7); 271 272 #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 273 274 for (i = 0; i < N(truephy_dspcode); ++i) { 275 const struct truephy_dsp *dsp = &truephy_dspcode[i]; 276 277 PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); 278 PHY_WRITE(sc, TRUEPHY_DATA, dsp->data); 279 280 PHY_WRITE(sc, TRUEPHY_INDEX, dsp->index); 281 PHY_READ(sc, TRUEPHY_DATA); 282 } 283 284 #undef N 285 286 PHY_READ(sc, MII_BMCR); 287 PHY_READ(sc, TRUEPHY_CTRL); 288 PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_S1000); 289 PHY_WRITE(sc, TRUEPHY_CTRL, 0x2); 290 291 mii_phy_reset(sc); 292 } 293 294 static void 295 truephy_status(struct mii_softc *sc) 296 { 297 struct mii_data *mii = sc->mii_pdata; 298 int bmsr, bmcr, sr; 299 300 mii->mii_media_status = IFM_AVALID; 301 mii->mii_media_active = IFM_ETHER; 302 303 sr = PHY_READ(sc, TRUEPHY_SR); 304 bmcr = PHY_READ(sc, MII_BMCR); 305 306 bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 307 if (bmsr & BMSR_LINK) 308 mii->mii_media_status |= IFM_ACTIVE; 309 310 if (bmcr & BMCR_AUTOEN) { 311 if ((bmsr & BMSR_ACOMP) == 0) { 312 mii->mii_media_active |= IFM_NONE; 313 return; 314 } 315 } 316 317 switch (sr & TRUEPHY_SR_SPD_MASK) { 318 case TRUEPHY_SR_SPD_1000T: 319 mii->mii_media_active |= IFM_1000_T; 320 break; 321 case TRUEPHY_SR_SPD_100TX: 322 mii->mii_media_active |= IFM_100_TX; 323 break; 324 case TRUEPHY_SR_SPD_10T: 325 mii->mii_media_active |= IFM_10_T; 326 break; 327 default: 328 mii->mii_media_active |= IFM_NONE; 329 break; 330 } 331 332 if (sr & TRUEPHY_SR_FDX) 333 mii->mii_media_active |= IFM_FDX; 334 } 335