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