1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following 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 #include <sys/param.h> 29 #include <sys/bus.h> 30 #include <sys/errno.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/module.h> 34 #include <sys/socket.h> 35 #include <sys/sockio.h> 36 #include <sys/sysctl.h> 37 #include <sys/systm.h> 38 39 #include <net/if.h> 40 #include <net/if_var.h> 41 #include <net/if_arp.h> 42 #include <net/ethernet.h> 43 #include <net/if_dl.h> 44 #include <net/if_media.h> 45 #include <net/if_types.h> 46 47 #include <machine/bus.h> 48 #include <dev/iicbus/iic.h> 49 #include <dev/iicbus/iiconf.h> 50 #include <dev/iicbus/iicbus.h> 51 #include <dev/mii/mii.h> 52 #include <dev/mii/miivar.h> 53 #include <dev/mdio/mdio.h> 54 #include <dev/extres/clk/clk.h> 55 #include <dev/extres/hwreset/hwreset.h> 56 57 #include <dev/fdt/fdt_common.h> 58 #include <dev/ofw/ofw_bus.h> 59 #include <dev/ofw/ofw_bus_subr.h> 60 61 #include <dev/etherswitch/etherswitch.h> 62 63 #include <dev/etherswitch/ar40xx/ar40xx_var.h> 64 #include <dev/etherswitch/ar40xx/ar40xx_reg.h> 65 #include <dev/etherswitch/ar40xx/ar40xx_hw.h> 66 #include <dev/etherswitch/ar40xx/ar40xx_hw_mdio.h> 67 #include <dev/etherswitch/ar40xx/ar40xx_hw_port.h> 68 #include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h> 69 #include <dev/etherswitch/ar40xx/ar40xx_phy.h> 70 #include <dev/etherswitch/ar40xx/ar40xx_debug.h> 71 72 #include "mdio_if.h" 73 #include "miibus_if.h" 74 #include "etherswitch_if.h" 75 76 77 int 78 ar40xx_phy_tick(struct ar40xx_softc *sc) 79 { 80 struct mii_softc *miisc; 81 struct mii_data *mii; 82 int phy; 83 uint32_t reg; 84 85 AR40XX_LOCK_ASSERT(sc); 86 87 AR40XX_REG_BARRIER_READ(sc); 88 /* 89 * Loop over; update phy port status here 90 */ 91 for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { 92 /* 93 * Port here is PHY, not port! 94 */ 95 reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(phy + 1)); 96 97 mii = device_get_softc(sc->sc_phys.miibus[phy]); 98 99 /* 100 * Compare the current link status to the previous link 101 * status. We may need to clear ATU / change phy config. 102 */ 103 if (((reg & AR40XX_PORT_STATUS_LINK_UP) != 0) && 104 (mii->mii_media_status & IFM_ACTIVE) == 0) { 105 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, 106 "%s: PHY %d: down -> up\n", __func__, phy); 107 ar40xx_hw_port_link_up(sc, phy + 1); 108 ar40xx_hw_atu_flush_port(sc, phy + 1); 109 } 110 if (((reg & AR40XX_PORT_STATUS_LINK_UP) == 0) && 111 (mii->mii_media_status & IFM_ACTIVE) != 0) { 112 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, 113 "%s: PHY %d: up -> down\n", __func__, phy); 114 ar40xx_hw_port_link_down(sc, phy + 1); 115 ar40xx_hw_atu_flush_port(sc, phy + 1); 116 } 117 118 mii_tick(mii); 119 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 120 if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 121 miisc->mii_inst) 122 continue; 123 ukphy_status(miisc); 124 mii_phy_update(miisc, MII_POLLSTAT); 125 } 126 } 127 128 return (0); 129 } 130 131 static inline int 132 ar40xx_portforphy(int phy) 133 { 134 135 return (phy+1); 136 } 137 138 struct mii_data * 139 ar40xx_phy_miiforport(struct ar40xx_softc *sc, int port) 140 { 141 int phy; 142 143 phy = port-1; 144 145 if (phy < 0 || phy >= AR40XX_NUM_PHYS) 146 return (NULL); 147 return (device_get_softc(sc->sc_phys.miibus[phy])); 148 } 149 150 if_t 151 ar40xx_phy_ifpforport(struct ar40xx_softc *sc, int port) 152 { 153 int phy; 154 155 phy = port-1; 156 if (phy < 0 || phy >= AR40XX_NUM_PHYS) 157 return (NULL); 158 return (sc->sc_phys.ifp[phy]); 159 } 160 161 static int 162 ar40xx_ifmedia_upd(if_t ifp) 163 { 164 struct ar40xx_softc *sc = if_getsoftc(ifp); 165 struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp)); 166 167 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n", 168 __func__, if_getdunit(ifp)); 169 170 if (mii == NULL) 171 return (ENXIO); 172 mii_mediachg(mii); 173 return (0); 174 } 175 176 static void 177 ar40xx_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr) 178 { 179 struct ar40xx_softc *sc = if_getsoftc(ifp); 180 struct mii_data *mii = ar40xx_phy_miiforport(sc, if_getdunit(ifp)); 181 182 AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s: called, PHY %d\n", 183 __func__, if_getdunit(ifp)); 184 185 if (mii == NULL) 186 return; 187 mii_pollstat(mii); 188 189 ifmr->ifm_active = mii->mii_media_active; 190 ifmr->ifm_status = mii->mii_media_status; 191 } 192 193 int 194 ar40xx_attach_phys(struct ar40xx_softc *sc) 195 { 196 int phy, err = 0; 197 char name[IFNAMSIZ]; 198 199 /* PHYs need an interface, so we generate a dummy one */ 200 snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 201 for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { 202 sc->sc_phys.ifp[phy] = if_alloc(IFT_ETHER); 203 if (sc->sc_phys.ifp[phy] == NULL) { 204 device_printf(sc->sc_dev, 205 "PHY %d: couldn't allocate ifnet structure\n", 206 phy); 207 err = ENOMEM; 208 break; 209 } 210 211 sc->sc_phys.ifp[phy]->if_softc = sc; 212 sc->sc_phys.ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST | 213 IFF_DRV_RUNNING | IFF_SIMPLEX; 214 sc->sc_phys.ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, 215 M_WAITOK); 216 bcopy(name, sc->sc_phys.ifname[phy], strlen(name)+1); 217 if_initname(sc->sc_phys.ifp[phy], sc->sc_phys.ifname[phy], 218 ar40xx_portforphy(phy)); 219 err = mii_attach(sc->sc_dev, &sc->sc_phys.miibus[phy], 220 sc->sc_phys.ifp[phy], ar40xx_ifmedia_upd, 221 ar40xx_ifmedia_sts, BMSR_DEFCAPMASK, 222 phy, MII_OFFSET_ANY, 0); 223 device_printf(sc->sc_dev, 224 "%s attached to pseudo interface %s\n", 225 device_get_nameunit(sc->sc_phys.miibus[phy]), 226 sc->sc_phys.ifp[phy]->if_xname); 227 if (err != 0) { 228 device_printf(sc->sc_dev, 229 "attaching PHY %d failed\n", 230 phy); 231 return (err); 232 } 233 } 234 return (0); 235 } 236 237 int 238 ar40xx_hw_phy_get_ids(struct ar40xx_softc *sc) 239 { 240 int phy; 241 uint32_t id1, id2; 242 243 for (phy = 0; phy < AR40XX_NUM_PHYS; phy++) { 244 id1 = MDIO_READREG(sc->sc_mdio_dev, phy, 2); 245 id2 = MDIO_READREG(sc->sc_mdio_dev, phy, 3); 246 device_printf(sc->sc_dev, 247 "%s: PHY %d: ID1=0x%04x, ID2=0x%04x\n", 248 __func__, phy, id1, id2); 249 } 250 251 return (0); 252 } 253