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