1 1.19 thorpej /* $NetBSD: ikphy.c,v 1.19 2020/03/15 23:04:50 thorpej Exp $ */ 2 1.1 bouyer 3 1.1 bouyer /******************************************************************************* 4 1.1 bouyer Copyright (c) 2001-2005, Intel Corporation 5 1.1 bouyer All rights reserved. 6 1.1 bouyer 7 1.1 bouyer Redistribution and use in source and binary forms, with or without 8 1.1 bouyer modification, are permitted provided that the following conditions are met: 9 1.1 bouyer 10 1.1 bouyer 1. Redistributions of source code must retain the above copyright notice, 11 1.1 bouyer this list of conditions and the following disclaimer. 12 1.1 bouyer 13 1.1 bouyer 2. Redistributions in binary form must reproduce the above copyright 14 1.1 bouyer notice, this list of conditions and the following disclaimer in the 15 1.1 bouyer documentation and/or other materials provided with the distribution. 16 1.1 bouyer 17 1.1 bouyer 3. Neither the name of the Intel Corporation nor the names of its 18 1.1 bouyer contributors may be used to endorse or promote products derived from 19 1.1 bouyer this software without specific prior written permission. 20 1.1 bouyer 21 1.1 bouyer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 1.1 bouyer AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 1.1 bouyer IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 1.1 bouyer ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 1.1 bouyer LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 1.1 bouyer CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 1.1 bouyer SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 1.1 bouyer INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 1.1 bouyer CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 1.1 bouyer ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 1.1 bouyer POSSIBILITY OF SUCH DAMAGE. 32 1.1 bouyer *******************************************************************************/ 33 1.1 bouyer /* 34 1.1 bouyer * Copyright (c) 2006 Manuel Bouyer. All rights reserved. 35 1.1 bouyer * 36 1.1 bouyer * Redistribution and use in source and binary forms, with or without 37 1.1 bouyer * modification, are permitted provided that the following conditions 38 1.1 bouyer * are met: 39 1.1 bouyer * 1. Redistributions of source code must retain the above copyright 40 1.1 bouyer * notice, this list of conditions and the following disclaimer. 41 1.1 bouyer * 2. Redistributions in binary form must reproduce the above copyright 42 1.1 bouyer * notice, this list of conditions and the following disclaimer in the 43 1.1 bouyer * documentation and/or other materials provided with the distribution. 44 1.1 bouyer * 45 1.1 bouyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 46 1.1 bouyer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 47 1.1 bouyer * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 48 1.1 bouyer * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 49 1.1 bouyer * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 50 1.1 bouyer * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 51 1.1 bouyer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 52 1.1 bouyer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 53 1.1 bouyer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 54 1.1 bouyer * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 55 1.1 bouyer */ 56 1.1 bouyer 57 1.1 bouyer /* 58 1.1 bouyer * driver for Intel's i82563 ethernet 10/100/1000 PHY 59 1.1 bouyer */ 60 1.1 bouyer 61 1.1 bouyer #include <sys/cdefs.h> 62 1.19 thorpej __KERNEL_RCSID(0, "$NetBSD: ikphy.c,v 1.19 2020/03/15 23:04:50 thorpej Exp $"); 63 1.1 bouyer 64 1.1 bouyer #include <sys/param.h> 65 1.1 bouyer #include <sys/systm.h> 66 1.1 bouyer #include <sys/kernel.h> 67 1.1 bouyer #include <sys/device.h> 68 1.1 bouyer #include <sys/socket.h> 69 1.1 bouyer #include <sys/errno.h> 70 1.1 bouyer 71 1.1 bouyer #include <net/if.h> 72 1.1 bouyer #include <net/if_media.h> 73 1.1 bouyer 74 1.1 bouyer #include <dev/mii/mii.h> 75 1.1 bouyer #include <dev/mii/miivar.h> 76 1.1 bouyer #include <dev/mii/miidevs.h> 77 1.1 bouyer 78 1.1 bouyer #include <dev/mii/ikphyreg.h> 79 1.1 bouyer 80 1.7 xtraeme static int ikphymatch(device_t, cfdata_t, void *); 81 1.7 xtraeme static void ikphyattach(device_t, device_t, void *); 82 1.1 bouyer 83 1.7 xtraeme CFATTACH_DECL_NEW(ikphy, sizeof(struct mii_softc), 84 1.1 bouyer ikphymatch, ikphyattach, mii_phy_detach, mii_phy_activate); 85 1.1 bouyer 86 1.1 bouyer static int ikphy_service(struct mii_softc *, struct mii_data *, int); 87 1.1 bouyer static void ikphy_status(struct mii_softc *); 88 1.1 bouyer static void ikphy_setmedia(struct mii_softc *); 89 1.1 bouyer 90 1.1 bouyer static const struct mii_phy_funcs ikphy_funcs = { 91 1.1 bouyer ikphy_service, ikphy_status, mii_phy_reset, 92 1.1 bouyer }; 93 1.1 bouyer 94 1.1 bouyer static const struct mii_phydesc ikphys[] = { 95 1.14 christos MII_PHY_DESC(xxMARVELL, I82563), 96 1.14 christos MII_PHY_END, 97 1.1 bouyer }; 98 1.1 bouyer 99 1.1 bouyer static int 100 1.7 xtraeme ikphymatch(device_t parent, cfdata_t match, void *aux) 101 1.1 bouyer { 102 1.1 bouyer struct mii_attach_args *ma = aux; 103 1.1 bouyer 104 1.1 bouyer if (mii_phy_match(ma, ikphys) != NULL) 105 1.15 msaitoh return 10; 106 1.1 bouyer 107 1.15 msaitoh return 0; 108 1.1 bouyer } 109 1.1 bouyer 110 1.1 bouyer static void 111 1.7 xtraeme ikphyattach(device_t parent, device_t self, void *aux) 112 1.1 bouyer { 113 1.1 bouyer struct mii_softc *sc = device_private(self); 114 1.1 bouyer struct mii_attach_args *ma = aux; 115 1.1 bouyer struct mii_data *mii = ma->mii_data; 116 1.1 bouyer const struct mii_phydesc *mpd; 117 1.1 bouyer 118 1.1 bouyer mpd = mii_phy_match(ma, ikphys); 119 1.1 bouyer aprint_naive(": Media interface\n"); 120 1.1 bouyer aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 121 1.1 bouyer 122 1.7 xtraeme sc->mii_dev = self; 123 1.1 bouyer sc->mii_inst = mii->mii_instance; 124 1.1 bouyer sc->mii_phy = ma->mii_phyno; 125 1.12 msaitoh sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2); 126 1.12 msaitoh sc->mii_mpd_model = MII_MODEL(ma->mii_id2); 127 1.12 msaitoh sc->mii_mpd_rev = MII_REV(ma->mii_id2); 128 1.1 bouyer sc->mii_funcs = &ikphy_funcs; 129 1.1 bouyer sc->mii_pdata = mii; 130 1.1 bouyer sc->mii_flags = ma->mii_flags; 131 1.1 bouyer 132 1.19 thorpej mii_lock(mii); 133 1.19 thorpej 134 1.1 bouyer PHY_RESET(sc); 135 1.1 bouyer 136 1.13 msaitoh PHY_READ(sc, MII_BMSR, &sc->mii_capabilities); 137 1.13 msaitoh sc->mii_capabilities &= ma->mii_capmask; 138 1.1 bouyer if (sc->mii_capabilities & BMSR_EXTSTAT) 139 1.13 msaitoh PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities); 140 1.18 msaitoh 141 1.19 thorpej mii_unlock(mii); 142 1.19 thorpej 143 1.18 msaitoh mii_phy_add_media(sc); 144 1.1 bouyer } 145 1.1 bouyer 146 1.1 bouyer static int 147 1.1 bouyer ikphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 148 1.1 bouyer { 149 1.1 bouyer struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 150 1.13 msaitoh uint16_t reg; 151 1.1 bouyer 152 1.19 thorpej KASSERT(mii_locked(mii)); 153 1.19 thorpej 154 1.1 bouyer switch (cmd) { 155 1.1 bouyer case MII_POLLSTAT: 156 1.15 msaitoh /* If we're not polling our PHY instance, just return. */ 157 1.1 bouyer if (IFM_INST(ife->ifm_media) != sc->mii_inst) 158 1.15 msaitoh return 0; 159 1.1 bouyer break; 160 1.1 bouyer 161 1.1 bouyer case MII_MEDIACHG: 162 1.1 bouyer /* 163 1.1 bouyer * If the media indicates a different PHY instance, 164 1.1 bouyer * isolate ourselves. 165 1.1 bouyer */ 166 1.1 bouyer if (IFM_INST(ife->ifm_media) != sc->mii_inst) { 167 1.13 msaitoh PHY_READ(sc, MII_BMCR, ®); 168 1.1 bouyer PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); 169 1.15 msaitoh return 0; 170 1.1 bouyer } 171 1.1 bouyer 172 1.15 msaitoh /* If the interface is not up, don't do anything. */ 173 1.1 bouyer if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 174 1.1 bouyer break; 175 1.1 bouyer 176 1.1 bouyer ikphy_setmedia(sc); 177 1.1 bouyer break; 178 1.1 bouyer 179 1.1 bouyer case MII_TICK: 180 1.15 msaitoh /* If we're not currently selected, just return. */ 181 1.1 bouyer if (IFM_INST(ife->ifm_media) != sc->mii_inst) 182 1.15 msaitoh return 0; 183 1.1 bouyer 184 1.1 bouyer if (mii_phy_tick(sc) == EJUSTRETURN) 185 1.15 msaitoh return 0; 186 1.1 bouyer break; 187 1.1 bouyer 188 1.1 bouyer case MII_DOWN: 189 1.1 bouyer mii_phy_down(sc); 190 1.15 msaitoh return 0; 191 1.1 bouyer } 192 1.1 bouyer 193 1.1 bouyer /* Update the media status. */ 194 1.1 bouyer mii_phy_status(sc); 195 1.1 bouyer 196 1.1 bouyer /* Callback if something changed. */ 197 1.1 bouyer mii_phy_update(sc, cmd); 198 1.15 msaitoh return 0; 199 1.1 bouyer } 200 1.1 bouyer 201 1.1 bouyer static void 202 1.1 bouyer ikphy_setmedia(struct mii_softc *sc) 203 1.1 bouyer { 204 1.1 bouyer uint16_t phy_data; 205 1.1 bouyer struct mii_data *mii = sc->mii_pdata; 206 1.1 bouyer struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 207 1.1 bouyer 208 1.19 thorpej KASSERT(mii_locked(mii)); 209 1.19 thorpej 210 1.1 bouyer /* Enable CRS on TX for half-duplex operation. */ 211 1.13 msaitoh PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL, &phy_data); 212 1.1 bouyer phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; 213 1.1 bouyer /* Use 25MHz for both link down and 1000BASE-T for Tx clock */ 214 1.1 bouyer phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ; 215 1.1 bouyer PHY_WRITE(sc, GG82563_PHY_MAC_SPEC_CTRL, phy_data); 216 1.1 bouyer 217 1.16 msaitoh /* Set mdi/mid-x options */ 218 1.13 msaitoh PHY_READ(sc, GG82563_PHY_SPEC_CTRL, &phy_data); 219 1.1 bouyer phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK; 220 1.1 bouyer if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) 221 1.1 bouyer phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO; 222 1.1 bouyer else 223 1.1 bouyer phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI; 224 1.16 msaitoh /* Set polarity correction */ 225 1.1 bouyer phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE; 226 1.1 bouyer PHY_WRITE(sc, GG82563_PHY_SPEC_CTRL, phy_data); 227 1.1 bouyer 228 1.1 bouyer /* SW Reset the PHY so all changes take effect */ 229 1.1 bouyer PHY_RESET(sc); 230 1.1 bouyer 231 1.16 msaitoh /* For the i80003 */ 232 1.13 msaitoh PHY_READ(sc, GG82563_PHY_SPEC_CTRL_2, &phy_data); 233 1.1 bouyer phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG; 234 1.1 bouyer PHY_WRITE(sc, GG82563_PHY_SPEC_CTRL_2, phy_data); 235 1.1 bouyer 236 1.1 bouyer /* Enable Electrical Idle on the PHY */ 237 1.13 msaitoh PHY_READ(sc, GG82563_PHY_PWR_MGMT_CTRL, &phy_data); 238 1.1 bouyer phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE; 239 1.1 bouyer PHY_WRITE(sc, GG82563_PHY_PWR_MGMT_CTRL, phy_data); 240 1.1 bouyer 241 1.13 msaitoh PHY_READ(sc, GG82563_PHY_KMRN_MODE_CTRL, &phy_data); 242 1.1 bouyer phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; 243 1.1 bouyer PHY_WRITE(sc, GG82563_PHY_KMRN_MODE_CTRL, phy_data); 244 1.1 bouyer 245 1.1 bouyer /* 246 1.1 bouyer * Workaround: Disable padding in Kumeran interface in the MAC 247 1.1 bouyer * and in the PHY to avoid CRC errors. 248 1.1 bouyer */ 249 1.13 msaitoh PHY_READ(sc, GG82563_PHY_INBAND_CTRL, &phy_data); 250 1.1 bouyer phy_data |= GG82563_ICR_DIS_PADDING; 251 1.1 bouyer PHY_WRITE(sc, GG82563_PHY_INBAND_CTRL, phy_data); 252 1.1 bouyer 253 1.1 bouyer mii_phy_setmedia(sc); 254 1.1 bouyer if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 255 1.1 bouyer /* 256 1.15 msaitoh * When not in auto mode, we need to restart nego 257 1.1 bouyer * anyway, or a switch from a fixed mode to another 258 1.1 bouyer * fixed mode may not be seen by the switch. 259 1.1 bouyer */ 260 1.13 msaitoh PHY_READ(sc, MII_BMCR, &phy_data); 261 1.13 msaitoh PHY_WRITE(sc, MII_BMCR, phy_data | BMCR_STARTNEG); 262 1.1 bouyer } 263 1.13 msaitoh PHY_READ(sc, GG82563_PHY_MAC_SPEC_CTRL, &phy_data); 264 1.1 bouyer phy_data &= ~GG82563_MSCR_TX_CLK_MASK; 265 1.16 msaitoh switch (IFM_SUBTYPE(ife->ifm_media)) { 266 1.1 bouyer case IFM_10_T: 267 1.1 bouyer phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ; 268 1.1 bouyer break; 269 1.1 bouyer case IFM_100_TX: 270 1.1 bouyer phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25MHZ; 271 1.1 bouyer break; 272 1.1 bouyer case IFM_1000_T: 273 1.1 bouyer phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ; 274 1.1 bouyer break; 275 1.1 bouyer } 276 1.1 bouyer phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; 277 1.1 bouyer PHY_WRITE(sc, GG82563_PHY_MAC_SPEC_CTRL, phy_data); 278 1.1 bouyer } 279 1.1 bouyer 280 1.1 bouyer static void 281 1.1 bouyer ikphy_status(struct mii_softc *sc) 282 1.1 bouyer { 283 1.1 bouyer struct mii_data *mii = sc->mii_pdata; 284 1.1 bouyer struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 285 1.13 msaitoh uint16_t pssr, bmcr, gtsr, kmrn; 286 1.1 bouyer 287 1.19 thorpej KASSERT(mii_locked(mii)); 288 1.19 thorpej 289 1.1 bouyer mii->mii_media_status = IFM_AVALID; 290 1.1 bouyer mii->mii_media_active = IFM_ETHER; 291 1.1 bouyer 292 1.13 msaitoh PHY_READ(sc, GG82563_PHY_SPEC_STATUS, &pssr); 293 1.1 bouyer 294 1.1 bouyer if (pssr & GG82563_PSSR_LINK) 295 1.1 bouyer mii->mii_media_status |= IFM_ACTIVE; 296 1.1 bouyer 297 1.13 msaitoh PHY_READ(sc, MII_BMCR, &bmcr); 298 1.1 bouyer if (bmcr & BMCR_ISO) { 299 1.1 bouyer mii->mii_media_active |= IFM_NONE; 300 1.1 bouyer mii->mii_media_status = 0; 301 1.1 bouyer return; 302 1.1 bouyer } 303 1.1 bouyer 304 1.1 bouyer if (bmcr & BMCR_LOOP) 305 1.1 bouyer mii->mii_media_active |= IFM_LOOP; 306 1.1 bouyer 307 1.1 bouyer if (bmcr & BMCR_AUTOEN) { 308 1.1 bouyer /* 309 1.17 msaitoh * The media status bits are only valid if autonegotiation 310 1.1 bouyer * has completed (or it's disabled). 311 1.1 bouyer */ 312 1.1 bouyer if ((pssr & GG82563_PSSR_SPEED_DUPLEX_RESOLVED) == 0) { 313 1.1 bouyer /* Erg, still trying, I guess... */ 314 1.1 bouyer mii->mii_media_active |= IFM_NONE; 315 1.1 bouyer return; 316 1.1 bouyer } 317 1.1 bouyer 318 1.1 bouyer switch (pssr & GG82563_PSSR_SPEED_MASK) { 319 1.1 bouyer case GG82563_PSSR_SPEED_1000MBPS: 320 1.1 bouyer mii->mii_media_active |= IFM_1000_T; 321 1.13 msaitoh PHY_READ(sc, MII_100T2SR, >sr); 322 1.1 bouyer if (gtsr & GTSR_MS_RES) 323 1.1 bouyer mii->mii_media_active |= IFM_ETH_MASTER; 324 1.1 bouyer break; 325 1.1 bouyer 326 1.1 bouyer case GG82563_PSSR_SPEED_100MBPS: 327 1.1 bouyer mii->mii_media_active |= IFM_100_TX; 328 1.1 bouyer break; 329 1.1 bouyer 330 1.1 bouyer case GG82563_PSSR_SPEED_10MBPS: 331 1.1 bouyer mii->mii_media_active |= IFM_10_T; 332 1.1 bouyer break; 333 1.1 bouyer 334 1.1 bouyer default: 335 1.1 bouyer mii->mii_media_active |= IFM_NONE; 336 1.1 bouyer mii->mii_media_status = 0; 337 1.1 bouyer return; 338 1.1 bouyer } 339 1.1 bouyer 340 1.1 bouyer if (pssr & GG82563_PSSR_DUPLEX) 341 1.1 bouyer mii->mii_media_active |= 342 1.1 bouyer IFM_FDX | mii_phy_flowstatus(sc); 343 1.10 msaitoh else 344 1.10 msaitoh mii->mii_media_active |= IFM_HDX; 345 1.1 bouyer } else 346 1.1 bouyer mii->mii_media_active = ife->ifm_media; 347 1.13 msaitoh PHY_READ(sc, GG82563_PHY_KMRN_MODE_CTRL, &kmrn); 348 1.1 bouyer if (mii->mii_media_active & IFM_FDX) 349 1.1 bouyer kmrn &= ~GG82563_KMCR_PASS_FALSE_CARRIER; 350 1.1 bouyer else 351 1.1 bouyer kmrn |= GG82563_KMCR_PASS_FALSE_CARRIER; 352 1.1 bouyer PHY_WRITE(sc, GG82563_PHY_KMRN_MODE_CTRL, kmrn); 353 1.1 bouyer } 354