1 1.8 msaitoh /* $NetBSD: rdcphy.c,v 1.8 2020/04/08 03:01:58 msaitoh Exp $ */ 2 1.1 bouyer 3 1.1 bouyer /*- 4 1.1 bouyer * Copyright (c) 2010, Pyun YongHyeon <yongari (at) FreeBSD.org> 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 9 1.1 bouyer * are met: 10 1.1 bouyer * 1. Redistributions of source code must retain the above copyright 11 1.1 bouyer * notice unmodified, this list of conditions, and the following 12 1.1 bouyer * disclaimer. 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 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 1.1 bouyer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 1.1 bouyer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 1.1 bouyer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 1.1 bouyer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 1.1 bouyer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 1.1 bouyer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 1.1 bouyer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 1.1 bouyer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 1.1 bouyer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 1.1 bouyer * SUCH DAMAGE. 28 1.1 bouyer */ 29 1.1 bouyer 30 1.1 bouyer /* FreeBSD: src/sys/dev/mii/rdcphy.c,v 1.1 2010/12/30 23:50:25 yongari Exp */ 31 1.1 bouyer 32 1.1 bouyer /* 33 1.1 bouyer * Driver for the RDC Semiconductor R6040 10/100 PHY. 34 1.1 bouyer */ 35 1.1 bouyer #include <sys/cdefs.h> 36 1.8 msaitoh __KERNEL_RCSID(0, "$NetBSD: rdcphy.c,v 1.8 2020/04/08 03:01:58 msaitoh Exp $"); 37 1.1 bouyer 38 1.1 bouyer #include <sys/param.h> 39 1.1 bouyer #include <sys/systm.h> 40 1.1 bouyer #include <sys/kernel.h> 41 1.1 bouyer #include <sys/device.h> 42 1.1 bouyer #include <sys/socket.h> 43 1.1 bouyer #include <sys/errno.h> 44 1.1 bouyer 45 1.1 bouyer #include <sys/bus.h> 46 1.1 bouyer 47 1.1 bouyer #include <net/if.h> 48 1.1 bouyer #include <net/if_media.h> 49 1.1 bouyer 50 1.1 bouyer #include <dev/mii/mii.h> 51 1.1 bouyer #include <dev/mii/miivar.h> 52 1.1 bouyer #include <dev/mii/miidevs.h> 53 1.1 bouyer 54 1.1 bouyer #include <dev/mii/rdcphyreg.h> 55 1.1 bouyer 56 1.1 bouyer struct rdcphy_softc { 57 1.1 bouyer struct mii_softc sc_mii; 58 1.1 bouyer int sc_mii_link_tick; 59 1.1 bouyer #define RDCPHY_MANNEG_TICK 3 60 1.1 bouyer }; 61 1.1 bouyer 62 1.1 bouyer static int rdcphymatch(device_t, cfdata_t, void *); 63 1.1 bouyer static void rdcphyattach(device_t, device_t, void *); 64 1.1 bouyer 65 1.1 bouyer CFATTACH_DECL_NEW(rdcphy, sizeof(struct rdcphy_softc), 66 1.1 bouyer rdcphymatch, rdcphyattach, mii_phy_detach, mii_phy_activate); 67 1.1 bouyer 68 1.1 bouyer 69 1.1 bouyer static int rdcphy_service(struct mii_softc *, struct mii_data *, int); 70 1.1 bouyer static void rdcphy_status(struct mii_softc *); 71 1.1 bouyer 72 1.1 bouyer static const struct mii_phy_funcs rdcphy_funcs = { 73 1.1 bouyer rdcphy_service, rdcphy_status, mii_phy_reset, 74 1.1 bouyer }; 75 1.1 bouyer 76 1.1 bouyer static const struct mii_phydesc rdcphys[] = { 77 1.6 msaitoh MII_PHY_DESC(xxRDC, R6040), 78 1.8 msaitoh MII_PHY_DESC(xxRDC, R6040_2), 79 1.8 msaitoh MII_PHY_DESC(xxRDC, R6040_3), 80 1.3 christos MII_PHY_END, 81 1.1 bouyer }; 82 1.1 bouyer 83 1.1 bouyer static int 84 1.1 bouyer rdcphymatch(device_t parent, cfdata_t match, void *aux) 85 1.1 bouyer { 86 1.1 bouyer struct mii_attach_args *ma = aux; 87 1.1 bouyer 88 1.1 bouyer if (mii_phy_match(ma, rdcphys) != NULL) 89 1.4 msaitoh return 10; 90 1.1 bouyer 91 1.4 msaitoh return 0; 92 1.1 bouyer } 93 1.1 bouyer 94 1.1 bouyer static void 95 1.1 bouyer rdcphyattach(device_t parent, device_t self, void *aux) 96 1.1 bouyer { 97 1.1 bouyer struct rdcphy_softc *rsc = device_private(self); 98 1.1 bouyer struct mii_softc *sc = &rsc->sc_mii; 99 1.1 bouyer struct mii_attach_args *ma = aux; 100 1.1 bouyer struct mii_data *mii = ma->mii_data; 101 1.1 bouyer 102 1.1 bouyer const struct mii_phydesc *mpd; 103 1.1 bouyer 104 1.1 bouyer mpd = mii_phy_match(ma, rdcphys); 105 1.1 bouyer aprint_naive(": Media interface\n"); 106 1.1 bouyer aprint_normal(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); 107 1.1 bouyer 108 1.1 bouyer sc->mii_dev = self; 109 1.1 bouyer sc->mii_inst = mii->mii_instance; 110 1.1 bouyer sc->mii_phy = ma->mii_phyno; 111 1.1 bouyer sc->mii_funcs = &rdcphy_funcs; 112 1.1 bouyer sc->mii_pdata = mii; 113 1.1 bouyer sc->mii_flags = ma->mii_flags; 114 1.1 bouyer 115 1.7 thorpej mii_lock(mii); 116 1.7 thorpej 117 1.1 bouyer PHY_RESET(sc); 118 1.1 bouyer 119 1.2 msaitoh PHY_READ(sc, MII_BMSR, &sc->mii_capabilities); 120 1.2 msaitoh sc->mii_capabilities &= ma->mii_capmask; 121 1.1 bouyer if (sc->mii_capabilities & BMSR_EXTSTAT) 122 1.2 msaitoh PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities); 123 1.5 msaitoh 124 1.7 thorpej mii_unlock(mii); 125 1.7 thorpej 126 1.1 bouyer mii_phy_add_media(sc); 127 1.1 bouyer } 128 1.1 bouyer 129 1.1 bouyer static int 130 1.1 bouyer rdcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 131 1.1 bouyer { 132 1.1 bouyer struct rdcphy_softc *rsc = (struct rdcphy_softc *)sc; 133 1.1 bouyer struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 134 1.1 bouyer 135 1.7 thorpej KASSERT(mii_locked(mii)); 136 1.7 thorpej 137 1.1 bouyer switch (cmd) { 138 1.1 bouyer case MII_POLLSTAT: 139 1.1 bouyer break; 140 1.1 bouyer 141 1.1 bouyer case MII_MEDIACHG: 142 1.4 msaitoh /* If the interface is not up, don't do anything. */ 143 1.1 bouyer if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 144 1.1 bouyer break; 145 1.1 bouyer 146 1.1 bouyer mii_phy_setmedia(sc); 147 1.1 bouyer switch (IFM_SUBTYPE(ife->ifm_media)) { 148 1.1 bouyer case IFM_100_TX: 149 1.1 bouyer case IFM_10_T: 150 1.1 bouyer /* 151 1.4 msaitoh * Report fake lost link event to parent driver. This 152 1.4 msaitoh * will stop MAC of parent driver and make it possible 153 1.4 msaitoh * to reconfigure MAC after completion of link 154 1.4 msaitoh * establishment. 155 1.4 msaitoh * Note, the parent MAC seems to require restarting MAC 156 1.4 msaitoh * when underlying any PHY configuration was changed 157 1.4 msaitoh * even if the resolved speed/duplex was not changed at 158 1.1 bouyer * all. 159 1.1 bouyer */ 160 1.1 bouyer mii->mii_media_status = 0; 161 1.1 bouyer mii->mii_media_active = IFM_ETHER | IFM_NONE; 162 1.1 bouyer rsc->sc_mii_link_tick = RDCPHY_MANNEG_TICK; 163 1.1 bouyer /* Immediately report link down. */ 164 1.1 bouyer mii_phy_update(sc, MII_MEDIACHG); 165 1.4 msaitoh return 0; 166 1.1 bouyer default: 167 1.1 bouyer break; 168 1.1 bouyer } 169 1.1 bouyer break; 170 1.1 bouyer 171 1.1 bouyer case MII_TICK: 172 1.1 bouyer if (mii_phy_tick(sc) == EJUSTRETURN) 173 1.4 msaitoh return 0; 174 1.1 bouyer if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 175 1.1 bouyer /* 176 1.4 msaitoh * It seems the PHY hardware does not correctly report 177 1.4 msaitoh * link status changes when manual link configuration 178 1.4 msaitoh * is in progress. It is also possible for the PHY to 179 1.4 msaitoh * complete establishing a link within one second such 180 1.4 msaitoh * that mii(4) did not notice the link change. 181 1.4 msaitoh * To workaround the issue, emulate lost link event and 182 1.4 msaitoh * wait for 3 seconds when manual link configuration 183 1.4 msaitoh * is in progress. 3 seconds would be long enough to 184 1.4 msaitoh * absorb transient link flips. 185 1.1 bouyer */ 186 1.1 bouyer if (rsc->sc_mii_link_tick > 0) { 187 1.1 bouyer rsc->sc_mii_link_tick--; 188 1.4 msaitoh return 0; 189 1.1 bouyer } 190 1.1 bouyer } 191 1.1 bouyer break; 192 1.1 bouyer } 193 1.1 bouyer 194 1.1 bouyer /* Update the media status. */ 195 1.1 bouyer rdcphy_status(sc); 196 1.1 bouyer 197 1.1 bouyer /* Callback if something changed. */ 198 1.1 bouyer mii_phy_update(sc, cmd); 199 1.4 msaitoh return 0; 200 1.1 bouyer } 201 1.1 bouyer 202 1.1 bouyer static void 203 1.1 bouyer rdcphy_status(struct mii_softc *sc) 204 1.1 bouyer { 205 1.1 bouyer struct mii_data *mii = sc->mii_pdata; 206 1.2 msaitoh uint16_t bmsr, bmcr, physts; 207 1.1 bouyer 208 1.7 thorpej KASSERT(mii_locked(mii)); 209 1.7 thorpej 210 1.1 bouyer mii->mii_media_status = IFM_AVALID; 211 1.1 bouyer mii->mii_media_active = IFM_ETHER; 212 1.1 bouyer 213 1.2 msaitoh PHY_READ(sc, MII_BMSR, &bmsr); 214 1.2 msaitoh PHY_READ(sc, MII_BMSR, &bmsr); 215 1.2 msaitoh PHY_READ(sc, MII_RDCPHY_STATUS, &physts); 216 1.1 bouyer 217 1.1 bouyer if ((physts & STATUS_LINK_UP) != 0) 218 1.1 bouyer mii->mii_media_status |= IFM_ACTIVE; 219 1.1 bouyer 220 1.2 msaitoh PHY_READ(sc, MII_BMCR, &bmcr); 221 1.1 bouyer if ((bmcr & BMCR_ISO) != 0) { 222 1.1 bouyer mii->mii_media_active |= IFM_NONE; 223 1.1 bouyer mii->mii_media_status = 0; 224 1.1 bouyer return; 225 1.1 bouyer } 226 1.1 bouyer 227 1.1 bouyer if ((bmcr & BMCR_LOOP) != 0) 228 1.1 bouyer mii->mii_media_active |= IFM_LOOP; 229 1.1 bouyer 230 1.1 bouyer if ((bmcr & BMCR_AUTOEN) != 0) { 231 1.1 bouyer if ((bmsr & BMSR_ACOMP) == 0) { 232 1.1 bouyer /* Erg, still trying, I guess... */ 233 1.1 bouyer mii->mii_media_active |= IFM_NONE; 234 1.1 bouyer return; 235 1.1 bouyer } 236 1.1 bouyer } 237 1.1 bouyer 238 1.1 bouyer switch (physts & STATUS_SPEED_MASK) { 239 1.1 bouyer case STATUS_SPEED_100: 240 1.1 bouyer mii->mii_media_active |= IFM_100_TX; 241 1.1 bouyer break; 242 1.1 bouyer case STATUS_SPEED_10: 243 1.1 bouyer mii->mii_media_active |= IFM_10_T; 244 1.1 bouyer break; 245 1.1 bouyer default: 246 1.1 bouyer mii->mii_media_active |= IFM_NONE; 247 1.1 bouyer return; 248 1.1 bouyer } 249 1.1 bouyer if ((physts & STATUS_FULL_DUPLEX) != 0) 250 1.1 bouyer mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 251 1.1 bouyer else 252 1.1 bouyer mii->mii_media_active |= IFM_HDX; 253 1.1 bouyer } 254