1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2010, Pyun YongHyeon <yongari@FreeBSD.org> 5 * All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 /* 34 * Driver for the RDC Semiconductor R6040 10/100 PHY. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/module.h> 41 #include <sys/socket.h> 42 #include <sys/bus.h> 43 44 #include <net/if.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/rdcphyreg.h> 52 53 #include "miibus_if.h" 54 55 static device_probe_t rdcphy_probe; 56 static device_attach_t rdcphy_attach; 57 58 struct rdcphy_softc { 59 struct mii_softc mii_sc; 60 int mii_link_tick; 61 #define RDCPHY_MANNEG_TICK 3 62 }; 63 64 static device_method_t rdcphy_methods[] = { 65 /* device interface */ 66 DEVMETHOD(device_probe, rdcphy_probe), 67 DEVMETHOD(device_attach, rdcphy_attach), 68 DEVMETHOD(device_detach, mii_phy_detach), 69 DEVMETHOD(device_shutdown, bus_generic_shutdown), 70 DEVMETHOD_END 71 }; 72 73 static devclass_t rdcphy_devclass; 74 75 static driver_t rdcphy_driver = { 76 "rdcphy", 77 rdcphy_methods, 78 sizeof(struct rdcphy_softc) 79 }; 80 81 DRIVER_MODULE(rdcphy, miibus, rdcphy_driver, rdcphy_devclass, 0, 0); 82 83 static int rdcphy_service(struct mii_softc *, struct mii_data *, int); 84 static void rdcphy_status(struct mii_softc *); 85 86 static const struct mii_phydesc rdcphys[] = { 87 MII_PHY_DESC(RDC, R6040), 88 MII_PHY_DESC(RDC, R6040_2), 89 MII_PHY_END 90 }; 91 92 static const struct mii_phy_funcs rdcphy_funcs = { 93 rdcphy_service, 94 rdcphy_status, 95 mii_phy_reset 96 }; 97 98 static int 99 rdcphy_probe(device_t dev) 100 { 101 102 return (mii_phy_dev_probe(dev, rdcphys, BUS_PROBE_DEFAULT)); 103 } 104 105 static int 106 rdcphy_attach(device_t dev) 107 { 108 109 mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &rdcphy_funcs, 1); 110 return (0); 111 } 112 113 static int 114 rdcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 115 { 116 struct rdcphy_softc *rsc; 117 struct ifmedia_entry *ife; 118 119 rsc = (struct rdcphy_softc *)sc; 120 ife = mii->mii_media.ifm_cur; 121 122 switch (cmd) { 123 case MII_POLLSTAT: 124 break; 125 126 case MII_MEDIACHG: 127 mii_phy_setmedia(sc); 128 switch (IFM_SUBTYPE(ife->ifm_media)) { 129 case IFM_100_TX: 130 case IFM_10_T: 131 /* 132 * Report fake lost link event to parent 133 * driver. This will stop MAC of parent 134 * driver and make it possible to reconfigure 135 * MAC after completion of link establishment. 136 * Note, the parent MAC seems to require 137 * restarting MAC when underlying any PHY 138 * configuration was changed even if the 139 * resolved speed/duplex was not changed at 140 * all. 141 */ 142 mii->mii_media_status = 0; 143 mii->mii_media_active = IFM_ETHER | IFM_NONE; 144 rsc->mii_link_tick = RDCPHY_MANNEG_TICK; 145 /* Immediately report link down. */ 146 mii_phy_update(sc, MII_MEDIACHG); 147 return (0); 148 default: 149 break; 150 } 151 break; 152 153 case MII_TICK: 154 if (mii_phy_tick(sc) == EJUSTRETURN) 155 return (0); 156 if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) { 157 /* 158 * It seems the PHY hardware does not correctly 159 * report link status changes when manual link 160 * configuration is in progress. It is also 161 * possible for the PHY to complete establishing 162 * a link within one second such that mii(4) 163 * did not notice the link change. To workaround 164 * the issue, emulate lost link event and wait 165 * for 3 seconds when manual link configuration 166 * is in progress. 3 seconds would be long 167 * enough to absorb transient link flips. 168 */ 169 if (rsc->mii_link_tick > 0) { 170 rsc->mii_link_tick--; 171 return (0); 172 } 173 } 174 break; 175 } 176 177 /* Update the media status. */ 178 PHY_STATUS(sc); 179 180 /* Callback if something changed. */ 181 mii_phy_update(sc, cmd); 182 return (0); 183 } 184 185 static void 186 rdcphy_status(struct mii_softc *sc) 187 { 188 struct mii_data *mii; 189 int bmsr, bmcr, physts; 190 191 mii = sc->mii_pdata; 192 193 mii->mii_media_status = IFM_AVALID; 194 mii->mii_media_active = IFM_ETHER; 195 196 bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); 197 physts = PHY_READ(sc, MII_RDCPHY_STATUS); 198 199 if ((physts & STATUS_LINK_UP) != 0) 200 mii->mii_media_status |= IFM_ACTIVE; 201 202 bmcr = PHY_READ(sc, MII_BMCR); 203 if ((bmcr & BMCR_ISO) != 0) { 204 mii->mii_media_active |= IFM_NONE; 205 mii->mii_media_status = 0; 206 return; 207 } 208 209 if ((bmcr & BMCR_LOOP) != 0) 210 mii->mii_media_active |= IFM_LOOP; 211 212 if ((bmcr & BMCR_AUTOEN) != 0) { 213 if ((bmsr & BMSR_ACOMP) == 0) { 214 /* Erg, still trying, I guess... */ 215 mii->mii_media_active |= IFM_NONE; 216 return; 217 } 218 } 219 220 switch (physts & STATUS_SPEED_MASK) { 221 case STATUS_SPEED_100: 222 mii->mii_media_active |= IFM_100_TX; 223 break; 224 case STATUS_SPEED_10: 225 mii->mii_media_active |= IFM_10_T; 226 break; 227 default: 228 mii->mii_media_active |= IFM_NONE; 229 return; 230 } 231 if ((physts & STATUS_FULL_DUPLEX) != 0) 232 mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc); 233 else 234 mii->mii_media_active |= IFM_HDX; 235 } 236