17b796c40SAdrian Chadd /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 47b796c40SAdrian Chadd * Copyright (c) 2013 Luiz Otavio O Souza. 57b796c40SAdrian Chadd * Copyright (c) 2011-2012 Stefan Bethke. 67b796c40SAdrian Chadd * Copyright (c) 2012 Adrian Chadd. 77b796c40SAdrian Chadd * All rights reserved. 87b796c40SAdrian Chadd * 97b796c40SAdrian Chadd * Redistribution and use in source and binary forms, with or without 107b796c40SAdrian Chadd * modification, are permitted provided that the following conditions 117b796c40SAdrian Chadd * are met: 127b796c40SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 137b796c40SAdrian Chadd * notice, this list of conditions and the following disclaimer. 147b796c40SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 157b796c40SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 167b796c40SAdrian Chadd * documentation and/or other materials provided with the distribution. 177b796c40SAdrian Chadd * 187b796c40SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 197b796c40SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 207b796c40SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 217b796c40SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 227b796c40SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 237b796c40SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 247b796c40SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 257b796c40SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 267b796c40SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 277b796c40SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 287b796c40SAdrian Chadd * SUCH DAMAGE. 297b796c40SAdrian Chadd */ 307b796c40SAdrian Chadd 317b796c40SAdrian Chadd #include <sys/param.h> 327b796c40SAdrian Chadd #include <sys/bus.h> 337b796c40SAdrian Chadd #include <sys/errno.h> 347b796c40SAdrian Chadd #include <sys/kernel.h> 3566e01d73SGleb Smirnoff #include <sys/lock.h> 3666e01d73SGleb Smirnoff #include <sys/malloc.h> 377b796c40SAdrian Chadd #include <sys/module.h> 3866e01d73SGleb Smirnoff #include <sys/mutex.h> 397b796c40SAdrian Chadd #include <sys/socket.h> 407b796c40SAdrian Chadd #include <sys/sockio.h> 417b796c40SAdrian Chadd #include <sys/sysctl.h> 427b796c40SAdrian Chadd #include <sys/systm.h> 437b796c40SAdrian Chadd 447b796c40SAdrian Chadd #include <net/if.h> 4566e01d73SGleb Smirnoff #include <net/if_var.h> 467b796c40SAdrian Chadd #include <net/ethernet.h> 477b796c40SAdrian Chadd #include <net/if_media.h> 487b796c40SAdrian Chadd #include <net/if_types.h> 497b796c40SAdrian Chadd 507b796c40SAdrian Chadd #include <machine/bus.h> 517b796c40SAdrian Chadd #include <dev/mii/mii.h> 527b796c40SAdrian Chadd #include <dev/mii/miivar.h> 5371e8eac4SAdrian Chadd #include <dev/mdio/mdio.h> 547b796c40SAdrian Chadd 557b796c40SAdrian Chadd #include <dev/etherswitch/etherswitch.h> 567b796c40SAdrian Chadd 577b796c40SAdrian Chadd #include "mdio_if.h" 587b796c40SAdrian Chadd #include "miibus_if.h" 597b796c40SAdrian Chadd #include "etherswitch_if.h" 607b796c40SAdrian Chadd 617b796c40SAdrian Chadd MALLOC_DECLARE(M_UKSWITCH); 627b796c40SAdrian Chadd MALLOC_DEFINE(M_UKSWITCH, "ukswitch", "ukswitch data structures"); 637b796c40SAdrian Chadd 647b796c40SAdrian Chadd struct ukswitch_softc { 657b796c40SAdrian Chadd struct mtx sc_mtx; /* serialize access to softc */ 667b796c40SAdrian Chadd device_t sc_dev; 677b796c40SAdrian Chadd int media; /* cpu port media */ 687b796c40SAdrian Chadd int cpuport; /* which PHY is connected to the CPU */ 697b796c40SAdrian Chadd int phymask; /* PHYs we manage */ 70b15317c4SMichael Zhilin int phyoffset; /* PHYs register offset */ 717b796c40SAdrian Chadd int numports; /* number of ports */ 727b796c40SAdrian Chadd int ifpport[MII_NPHY]; 737b796c40SAdrian Chadd int *portphy; 747b796c40SAdrian Chadd char **ifname; 757b796c40SAdrian Chadd device_t **miibus; 762e6a8c1aSJustin Hibbits if_t *ifp; 777b796c40SAdrian Chadd struct callout callout_tick; 787b796c40SAdrian Chadd etherswitch_info_t info; 797b796c40SAdrian Chadd }; 807b796c40SAdrian Chadd 817b796c40SAdrian Chadd #define UKSWITCH_LOCK(_sc) \ 827b796c40SAdrian Chadd mtx_lock(&(_sc)->sc_mtx) 837b796c40SAdrian Chadd #define UKSWITCH_UNLOCK(_sc) \ 847b796c40SAdrian Chadd mtx_unlock(&(_sc)->sc_mtx) 857b796c40SAdrian Chadd #define UKSWITCH_LOCK_ASSERT(_sc, _what) \ 867b796c40SAdrian Chadd mtx_assert(&(_sc)->sc_mtx, (_what)) 877b796c40SAdrian Chadd #define UKSWITCH_TRYLOCK(_sc) \ 887b796c40SAdrian Chadd mtx_trylock(&(_sc)->sc_mtx) 897b796c40SAdrian Chadd 907b796c40SAdrian Chadd #if defined(DEBUG) 917b796c40SAdrian Chadd #define DPRINTF(dev, args...) device_printf(dev, args) 927b796c40SAdrian Chadd #else 937b796c40SAdrian Chadd #define DPRINTF(dev, args...) 947b796c40SAdrian Chadd #endif 957b796c40SAdrian Chadd 967b796c40SAdrian Chadd static inline int ukswitch_portforphy(struct ukswitch_softc *, int); 977b796c40SAdrian Chadd static void ukswitch_tick(void *); 982e6a8c1aSJustin Hibbits static int ukswitch_ifmedia_upd(if_t); 992e6a8c1aSJustin Hibbits static void ukswitch_ifmedia_sts(if_t, struct ifmediareq *); 1007b796c40SAdrian Chadd 1017b796c40SAdrian Chadd static int 1027b796c40SAdrian Chadd ukswitch_probe(device_t dev) 1037b796c40SAdrian Chadd { 1047b796c40SAdrian Chadd struct ukswitch_softc *sc; 1057b796c40SAdrian Chadd 1067b796c40SAdrian Chadd sc = device_get_softc(dev); 1077b796c40SAdrian Chadd bzero(sc, sizeof(*sc)); 1087b796c40SAdrian Chadd 10954482989SMark Johnston device_set_desc(dev, "Generic MDIO switch driver"); 1107b796c40SAdrian Chadd return (BUS_PROBE_DEFAULT); 1117b796c40SAdrian Chadd } 1127b796c40SAdrian Chadd 1137b796c40SAdrian Chadd static int 1147b796c40SAdrian Chadd ukswitch_attach_phys(struct ukswitch_softc *sc) 1157b796c40SAdrian Chadd { 1167b796c40SAdrian Chadd int phy, port = 0, err = 0; 1177b796c40SAdrian Chadd char name[IFNAMSIZ]; 1187b796c40SAdrian Chadd 1197b796c40SAdrian Chadd /* PHYs need an interface, so we generate a dummy one */ 1207b796c40SAdrian Chadd snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 1217b796c40SAdrian Chadd for (phy = 0; phy < MII_NPHY; phy++) { 1227b796c40SAdrian Chadd if (((1 << phy) & sc->phymask) == 0) 1237b796c40SAdrian Chadd continue; 1247b796c40SAdrian Chadd sc->ifpport[phy] = port; 1257b796c40SAdrian Chadd sc->portphy[port] = phy; 1267b796c40SAdrian Chadd sc->ifp[port] = if_alloc(IFT_ETHER); 1272e6a8c1aSJustin Hibbits if_setsoftc(sc->ifp[port], sc); 1282e6a8c1aSJustin Hibbits if_setflags(sc->ifp[port], IFF_UP | IFF_BROADCAST | 1292e6a8c1aSJustin Hibbits IFF_DRV_RUNNING | IFF_SIMPLEX); 1307b796c40SAdrian Chadd sc->ifname[port] = malloc(strlen(name)+1, M_UKSWITCH, M_WAITOK); 1317b796c40SAdrian Chadd bcopy(name, sc->ifname[port], strlen(name)+1); 1327b796c40SAdrian Chadd if_initname(sc->ifp[port], sc->ifname[port], port); 1337b796c40SAdrian Chadd sc->miibus[port] = malloc(sizeof(device_t), M_UKSWITCH, 1347b796c40SAdrian Chadd M_WAITOK | M_ZERO); 1357b796c40SAdrian Chadd err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port], 1367b796c40SAdrian Chadd ukswitch_ifmedia_upd, ukswitch_ifmedia_sts, \ 137b15317c4SMichael Zhilin BMSR_DEFCAPMASK, phy + sc->phyoffset, MII_OFFSET_ANY, 0); 1387b796c40SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 1397b796c40SAdrian Chadd device_get_nameunit(*sc->miibus[port]), 1402e6a8c1aSJustin Hibbits if_name(sc->ifp[port])); 1417b796c40SAdrian Chadd if (err != 0) { 1427b796c40SAdrian Chadd device_printf(sc->sc_dev, 1437b796c40SAdrian Chadd "attaching PHY %d failed\n", 1447b796c40SAdrian Chadd phy); 1457b796c40SAdrian Chadd break; 1467b796c40SAdrian Chadd } 1477b796c40SAdrian Chadd sc->info.es_nports = port + 1; 1487b796c40SAdrian Chadd if (++port >= sc->numports) 1497b796c40SAdrian Chadd break; 1507b796c40SAdrian Chadd } 1517b796c40SAdrian Chadd return (err); 1527b796c40SAdrian Chadd } 1537b796c40SAdrian Chadd 1547b796c40SAdrian Chadd static int 1557b796c40SAdrian Chadd ukswitch_attach(device_t dev) 1567b796c40SAdrian Chadd { 1577b796c40SAdrian Chadd struct ukswitch_softc *sc; 1587b796c40SAdrian Chadd int err = 0; 1597b796c40SAdrian Chadd 1607b796c40SAdrian Chadd sc = device_get_softc(dev); 1617b796c40SAdrian Chadd 1627b796c40SAdrian Chadd sc->sc_dev = dev; 1637b796c40SAdrian Chadd mtx_init(&sc->sc_mtx, "ukswitch", NULL, MTX_DEF); 1647b796c40SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev), 1657b796c40SAdrian Chadd sizeof(sc->info.es_name)); 1667b796c40SAdrian Chadd 1677b796c40SAdrian Chadd /* XXX Defaults */ 1687b796c40SAdrian Chadd sc->numports = 6; 1697b796c40SAdrian Chadd sc->phymask = 0x0f; 170b15317c4SMichael Zhilin sc->phyoffset = 0; 1711ead288fSAdrian Chadd sc->cpuport = -1; 1727b796c40SAdrian Chadd sc->media = 100; 1737b796c40SAdrian Chadd 1747b796c40SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 1757b796c40SAdrian Chadd "numports", &sc->numports); 1767b796c40SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 1777b796c40SAdrian Chadd "phymask", &sc->phymask); 1787b796c40SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 179b15317c4SMichael Zhilin "phyoffset", &sc->phyoffset); 180b15317c4SMichael Zhilin (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 1817b796c40SAdrian Chadd "cpuport", &sc->cpuport); 1827b796c40SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 1837b796c40SAdrian Chadd "media", &sc->media); 1847b796c40SAdrian Chadd 1857b796c40SAdrian Chadd /* Support only fast and giga ethernet. */ 1867b796c40SAdrian Chadd if (sc->media != 100 && sc->media != 1000) 1877b796c40SAdrian Chadd sc->media = 100; 1887b796c40SAdrian Chadd 1891ead288fSAdrian Chadd if (sc->cpuport != -1) 1907b796c40SAdrian Chadd /* Always attach the cpu port. */ 1917b796c40SAdrian Chadd sc->phymask |= (1 << sc->cpuport); 1927b796c40SAdrian Chadd 1937b796c40SAdrian Chadd /* We do not support any vlan groups. */ 1947b796c40SAdrian Chadd sc->info.es_nvlangroups = 0; 1957b796c40SAdrian Chadd 1962e6a8c1aSJustin Hibbits sc->ifp = malloc(sizeof(if_t) * sc->numports, M_UKSWITCH, 1977b796c40SAdrian Chadd M_WAITOK | M_ZERO); 1987b796c40SAdrian Chadd sc->ifname = malloc(sizeof(char *) * sc->numports, M_UKSWITCH, 1997b796c40SAdrian Chadd M_WAITOK | M_ZERO); 2007b796c40SAdrian Chadd sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_UKSWITCH, 2017b796c40SAdrian Chadd M_WAITOK | M_ZERO); 2027b796c40SAdrian Chadd sc->portphy = malloc(sizeof(int) * sc->numports, M_UKSWITCH, 2037b796c40SAdrian Chadd M_WAITOK | M_ZERO); 2047b796c40SAdrian Chadd 2057b796c40SAdrian Chadd /* 2067b796c40SAdrian Chadd * Attach the PHYs and complete the bus enumeration. 2077b796c40SAdrian Chadd */ 2087b796c40SAdrian Chadd err = ukswitch_attach_phys(sc); 2097b796c40SAdrian Chadd if (err != 0) 2107b796c40SAdrian Chadd return (err); 2117b796c40SAdrian Chadd 212723da5d9SJohn Baldwin bus_identify_children(dev); 2137b796c40SAdrian Chadd bus_enumerate_hinted_children(dev); 21418250ec6SJohn Baldwin bus_attach_children(dev); 2157b796c40SAdrian Chadd 2167b796c40SAdrian Chadd callout_init(&sc->callout_tick, 0); 2177b796c40SAdrian Chadd 2187b796c40SAdrian Chadd ukswitch_tick(sc); 2197b796c40SAdrian Chadd 2207b796c40SAdrian Chadd return (err); 2217b796c40SAdrian Chadd } 2227b796c40SAdrian Chadd 2237b796c40SAdrian Chadd static int 2247b796c40SAdrian Chadd ukswitch_detach(device_t dev) 2257b796c40SAdrian Chadd { 2267b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 227*aa48c1aeSJohn Baldwin int error, i, port; 228*aa48c1aeSJohn Baldwin 229*aa48c1aeSJohn Baldwin error = bus_generic_detach(dev); 230*aa48c1aeSJohn Baldwin if (error != 0) 231*aa48c1aeSJohn Baldwin return (error); 2327b796c40SAdrian Chadd 2337b796c40SAdrian Chadd callout_drain(&sc->callout_tick); 2347b796c40SAdrian Chadd 2357b796c40SAdrian Chadd for (i=0; i < MII_NPHY; i++) { 2367b796c40SAdrian Chadd if (((1 << i) & sc->phymask) == 0) 2377b796c40SAdrian Chadd continue; 2387b796c40SAdrian Chadd port = ukswitch_portforphy(sc, i); 2397b796c40SAdrian Chadd if (sc->ifp[port] != NULL) 2407b796c40SAdrian Chadd if_free(sc->ifp[port]); 2417b796c40SAdrian Chadd free(sc->ifname[port], M_UKSWITCH); 2427b796c40SAdrian Chadd free(sc->miibus[port], M_UKSWITCH); 2437b796c40SAdrian Chadd } 2447b796c40SAdrian Chadd 2457b796c40SAdrian Chadd free(sc->portphy, M_UKSWITCH); 2467b796c40SAdrian Chadd free(sc->miibus, M_UKSWITCH); 2477b796c40SAdrian Chadd free(sc->ifname, M_UKSWITCH); 2487b796c40SAdrian Chadd free(sc->ifp, M_UKSWITCH); 2497b796c40SAdrian Chadd 2507b796c40SAdrian Chadd mtx_destroy(&sc->sc_mtx); 2517b796c40SAdrian Chadd 2527b796c40SAdrian Chadd return (0); 2537b796c40SAdrian Chadd } 2547b796c40SAdrian Chadd 2557b796c40SAdrian Chadd /* 2567b796c40SAdrian Chadd * Convert PHY number to port number. 2577b796c40SAdrian Chadd */ 2587b796c40SAdrian Chadd static inline int 2597b796c40SAdrian Chadd ukswitch_portforphy(struct ukswitch_softc *sc, int phy) 2607b796c40SAdrian Chadd { 2617b796c40SAdrian Chadd 2627b796c40SAdrian Chadd return (sc->ifpport[phy]); 2637b796c40SAdrian Chadd } 2647b796c40SAdrian Chadd 2657b796c40SAdrian Chadd static inline struct mii_data * 2667b796c40SAdrian Chadd ukswitch_miiforport(struct ukswitch_softc *sc, int port) 2677b796c40SAdrian Chadd { 2687b796c40SAdrian Chadd 2697b796c40SAdrian Chadd if (port < 0 || port > sc->numports) 2707b796c40SAdrian Chadd return (NULL); 2717b796c40SAdrian Chadd return (device_get_softc(*sc->miibus[port])); 2727b796c40SAdrian Chadd } 2737b796c40SAdrian Chadd 2742e6a8c1aSJustin Hibbits static inline if_t 2757b796c40SAdrian Chadd ukswitch_ifpforport(struct ukswitch_softc *sc, int port) 2767b796c40SAdrian Chadd { 2777b796c40SAdrian Chadd 2787b796c40SAdrian Chadd if (port < 0 || port > sc->numports) 2797b796c40SAdrian Chadd return (NULL); 2807b796c40SAdrian Chadd return (sc->ifp[port]); 2817b796c40SAdrian Chadd } 2827b796c40SAdrian Chadd 2837b796c40SAdrian Chadd /* 2847b796c40SAdrian Chadd * Poll the status for all PHYs. 2857b796c40SAdrian Chadd */ 2867b796c40SAdrian Chadd static void 2877b796c40SAdrian Chadd ukswitch_miipollstat(struct ukswitch_softc *sc) 2887b796c40SAdrian Chadd { 2897b796c40SAdrian Chadd int i, port; 2907b796c40SAdrian Chadd struct mii_data *mii; 2917b796c40SAdrian Chadd struct mii_softc *miisc; 2927b796c40SAdrian Chadd 2937b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 2947b796c40SAdrian Chadd 2957b796c40SAdrian Chadd for (i = 0; i < MII_NPHY; i++) { 2967b796c40SAdrian Chadd if (((1 << i) & sc->phymask) == 0) 2977b796c40SAdrian Chadd continue; 2987b796c40SAdrian Chadd port = ukswitch_portforphy(sc, i); 2997b796c40SAdrian Chadd if ((*sc->miibus[port]) == NULL) 3007b796c40SAdrian Chadd continue; 3017b796c40SAdrian Chadd mii = device_get_softc(*sc->miibus[port]); 3027b796c40SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 3037b796c40SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 3047b796c40SAdrian Chadd miisc->mii_inst) 3057b796c40SAdrian Chadd continue; 3067b796c40SAdrian Chadd ukphy_status(miisc); 3077b796c40SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT); 3087b796c40SAdrian Chadd } 3097b796c40SAdrian Chadd } 3107b796c40SAdrian Chadd } 3117b796c40SAdrian Chadd 3127b796c40SAdrian Chadd static void 3137b796c40SAdrian Chadd ukswitch_tick(void *arg) 3147b796c40SAdrian Chadd { 3157b796c40SAdrian Chadd struct ukswitch_softc *sc = arg; 3167b796c40SAdrian Chadd 3177b796c40SAdrian Chadd ukswitch_miipollstat(sc); 3187b796c40SAdrian Chadd callout_reset(&sc->callout_tick, hz, ukswitch_tick, sc); 3197b796c40SAdrian Chadd } 3207b796c40SAdrian Chadd 3217b796c40SAdrian Chadd static void 3227b796c40SAdrian Chadd ukswitch_lock(device_t dev) 3237b796c40SAdrian Chadd { 3247b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3257b796c40SAdrian Chadd 3267b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 3277b796c40SAdrian Chadd UKSWITCH_LOCK(sc); 3287b796c40SAdrian Chadd } 3297b796c40SAdrian Chadd 3307b796c40SAdrian Chadd static void 3317b796c40SAdrian Chadd ukswitch_unlock(device_t dev) 3327b796c40SAdrian Chadd { 3337b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3347b796c40SAdrian Chadd 3357b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_OWNED); 3367b796c40SAdrian Chadd UKSWITCH_UNLOCK(sc); 3377b796c40SAdrian Chadd } 3387b796c40SAdrian Chadd 3397b796c40SAdrian Chadd static etherswitch_info_t * 3407b796c40SAdrian Chadd ukswitch_getinfo(device_t dev) 3417b796c40SAdrian Chadd { 3427b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3437b796c40SAdrian Chadd 3447b796c40SAdrian Chadd return (&sc->info); 3457b796c40SAdrian Chadd } 3467b796c40SAdrian Chadd 3477b796c40SAdrian Chadd static int 3487b796c40SAdrian Chadd ukswitch_getport(device_t dev, etherswitch_port_t *p) 3497b796c40SAdrian Chadd { 3507b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3517b796c40SAdrian Chadd struct mii_data *mii; 3527b796c40SAdrian Chadd struct ifmediareq *ifmr = &p->es_ifmr; 3531ead288fSAdrian Chadd int err, phy; 3547b796c40SAdrian Chadd 3557b796c40SAdrian Chadd if (p->es_port < 0 || p->es_port >= sc->numports) 3567b796c40SAdrian Chadd return (ENXIO); 3571ead288fSAdrian Chadd p->es_pvid = 0; 3587b796c40SAdrian Chadd 3591ead288fSAdrian Chadd phy = sc->portphy[p->es_port]; 3607b796c40SAdrian Chadd mii = ukswitch_miiforport(sc, p->es_port); 3611ead288fSAdrian Chadd if (sc->cpuport != -1 && phy == sc->cpuport) { 3627b796c40SAdrian Chadd /* fill in fixed values for CPU port */ 3631ead288fSAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 3647b796c40SAdrian Chadd ifmr->ifm_count = 0; 3657b796c40SAdrian Chadd if (sc->media == 100) 3667b796c40SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 3677b796c40SAdrian Chadd IFM_ETHER | IFM_100_TX | IFM_FDX; 3687b796c40SAdrian Chadd else 3697b796c40SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 3707b796c40SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 3717b796c40SAdrian Chadd ifmr->ifm_mask = 0; 3727b796c40SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 3737b796c40SAdrian Chadd } else if (mii != NULL) { 3747b796c40SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 3757b796c40SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 3767b796c40SAdrian Chadd if (err) 3777b796c40SAdrian Chadd return (err); 3787b796c40SAdrian Chadd } else { 3797b796c40SAdrian Chadd return (ENXIO); 3807b796c40SAdrian Chadd } 3817b796c40SAdrian Chadd return (0); 3827b796c40SAdrian Chadd } 3837b796c40SAdrian Chadd 3847b796c40SAdrian Chadd static int 3857b796c40SAdrian Chadd ukswitch_setport(device_t dev, etherswitch_port_t *p) 3867b796c40SAdrian Chadd { 3877b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3887b796c40SAdrian Chadd struct ifmedia *ifm; 3897b796c40SAdrian Chadd struct mii_data *mii; 3902e6a8c1aSJustin Hibbits if_t ifp; 3917b796c40SAdrian Chadd int err; 3927b796c40SAdrian Chadd 3937b796c40SAdrian Chadd if (p->es_port < 0 || p->es_port >= sc->numports) 3947b796c40SAdrian Chadd return (ENXIO); 3957b796c40SAdrian Chadd 3967b796c40SAdrian Chadd if (sc->portphy[p->es_port] == sc->cpuport) 3977b796c40SAdrian Chadd return (ENXIO); 3987b796c40SAdrian Chadd 3997b796c40SAdrian Chadd mii = ukswitch_miiforport(sc, p->es_port); 4007b796c40SAdrian Chadd if (mii == NULL) 4017b796c40SAdrian Chadd return (ENXIO); 4027b796c40SAdrian Chadd 4037b796c40SAdrian Chadd ifp = ukswitch_ifpforport(sc, p->es_port); 4047b796c40SAdrian Chadd 4057b796c40SAdrian Chadd ifm = &mii->mii_media; 4067b796c40SAdrian Chadd err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA); 4077b796c40SAdrian Chadd return (err); 4087b796c40SAdrian Chadd } 4097b796c40SAdrian Chadd 4107b796c40SAdrian Chadd static int 4117b796c40SAdrian Chadd ukswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 4127b796c40SAdrian Chadd { 4137b796c40SAdrian Chadd 4147b796c40SAdrian Chadd /* Not supported. */ 4157b796c40SAdrian Chadd vg->es_vid = 0; 4167b796c40SAdrian Chadd vg->es_member_ports = 0; 4177b796c40SAdrian Chadd vg->es_untagged_ports = 0; 4187b796c40SAdrian Chadd vg->es_fid = 0; 4197b796c40SAdrian Chadd return (0); 4207b796c40SAdrian Chadd } 4217b796c40SAdrian Chadd 4227b796c40SAdrian Chadd static int 4237b796c40SAdrian Chadd ukswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 4247b796c40SAdrian Chadd { 4257b796c40SAdrian Chadd 4267b796c40SAdrian Chadd /* Not supported. */ 4277b796c40SAdrian Chadd return (0); 4287b796c40SAdrian Chadd } 4297b796c40SAdrian Chadd 4307b796c40SAdrian Chadd static void 4317b796c40SAdrian Chadd ukswitch_statchg(device_t dev) 4327b796c40SAdrian Chadd { 4337b796c40SAdrian Chadd 4347b796c40SAdrian Chadd DPRINTF(dev, "%s\n", __func__); 4357b796c40SAdrian Chadd } 4367b796c40SAdrian Chadd 4377b796c40SAdrian Chadd static int 4382e6a8c1aSJustin Hibbits ukswitch_ifmedia_upd(if_t ifp) 4397b796c40SAdrian Chadd { 4402e6a8c1aSJustin Hibbits struct ukswitch_softc *sc = if_getsoftc(ifp); 4412e6a8c1aSJustin Hibbits struct mii_data *mii = ukswitch_miiforport(sc, if_getdunit(ifp)); 4427b796c40SAdrian Chadd 4437b796c40SAdrian Chadd DPRINTF(sc->sc_dev, "%s\n", __func__); 4447b796c40SAdrian Chadd if (mii == NULL) 4457b796c40SAdrian Chadd return (ENXIO); 4467b796c40SAdrian Chadd mii_mediachg(mii); 4477b796c40SAdrian Chadd return (0); 4487b796c40SAdrian Chadd } 4497b796c40SAdrian Chadd 4507b796c40SAdrian Chadd static void 4512e6a8c1aSJustin Hibbits ukswitch_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr) 4527b796c40SAdrian Chadd { 4532e6a8c1aSJustin Hibbits struct ukswitch_softc *sc = if_getsoftc(ifp); 4542e6a8c1aSJustin Hibbits struct mii_data *mii = ukswitch_miiforport(sc, if_getdunit(ifp)); 4557b796c40SAdrian Chadd 4567b796c40SAdrian Chadd DPRINTF(sc->sc_dev, "%s\n", __func__); 4577b796c40SAdrian Chadd 4587b796c40SAdrian Chadd if (mii == NULL) 4597b796c40SAdrian Chadd return; 4607b796c40SAdrian Chadd mii_pollstat(mii); 4617b796c40SAdrian Chadd ifmr->ifm_active = mii->mii_media_active; 4627b796c40SAdrian Chadd ifmr->ifm_status = mii->mii_media_status; 4637b796c40SAdrian Chadd } 4647b796c40SAdrian Chadd 4657b796c40SAdrian Chadd static int 4667b796c40SAdrian Chadd ukswitch_readphy(device_t dev, int phy, int reg) 4677b796c40SAdrian Chadd { 4687b796c40SAdrian Chadd struct ukswitch_softc *sc; 4697b796c40SAdrian Chadd int data; 4707b796c40SAdrian Chadd 4717b796c40SAdrian Chadd sc = device_get_softc(dev); 4727b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 4737b796c40SAdrian Chadd 4747b796c40SAdrian Chadd if (phy < 0 || phy >= 32) 4757b796c40SAdrian Chadd return (ENXIO); 4767b796c40SAdrian Chadd if (reg < 0 || reg >= 32) 4777b796c40SAdrian Chadd return (ENXIO); 4787b796c40SAdrian Chadd 4797b796c40SAdrian Chadd UKSWITCH_LOCK(sc); 4807b796c40SAdrian Chadd data = MDIO_READREG(device_get_parent(dev), phy, reg); 4817b796c40SAdrian Chadd UKSWITCH_UNLOCK(sc); 4827b796c40SAdrian Chadd 4837b796c40SAdrian Chadd return (data); 4847b796c40SAdrian Chadd } 4857b796c40SAdrian Chadd 4867b796c40SAdrian Chadd static int 4877b796c40SAdrian Chadd ukswitch_writephy(device_t dev, int phy, int reg, int data) 4887b796c40SAdrian Chadd { 4897b796c40SAdrian Chadd struct ukswitch_softc *sc; 4907b796c40SAdrian Chadd int err; 4917b796c40SAdrian Chadd 4927b796c40SAdrian Chadd sc = device_get_softc(dev); 4937b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 4947b796c40SAdrian Chadd 4957b796c40SAdrian Chadd if (phy < 0 || phy >= 32) 4967b796c40SAdrian Chadd return (ENXIO); 4977b796c40SAdrian Chadd if (reg < 0 || reg >= 32) 4987b796c40SAdrian Chadd return (ENXIO); 4997b796c40SAdrian Chadd 5007b796c40SAdrian Chadd UKSWITCH_LOCK(sc); 5017b796c40SAdrian Chadd err = MDIO_WRITEREG(device_get_parent(dev), phy, reg, data); 5027b796c40SAdrian Chadd UKSWITCH_UNLOCK(sc); 5037b796c40SAdrian Chadd 5047b796c40SAdrian Chadd return (err); 5057b796c40SAdrian Chadd } 5067b796c40SAdrian Chadd 5077b796c40SAdrian Chadd static int 5087b796c40SAdrian Chadd ukswitch_readreg(device_t dev, int addr) 5097b796c40SAdrian Chadd { 5102e0c027eSEd Maste struct ukswitch_softc *sc __diagused; 5117b796c40SAdrian Chadd 5127b796c40SAdrian Chadd sc = device_get_softc(dev); 5137b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_OWNED); 5147b796c40SAdrian Chadd 5157b796c40SAdrian Chadd /* Not supported. */ 5167b796c40SAdrian Chadd return (0); 5177b796c40SAdrian Chadd } 5187b796c40SAdrian Chadd 5197b796c40SAdrian Chadd static int 5207b796c40SAdrian Chadd ukswitch_writereg(device_t dev, int addr, int value) 5217b796c40SAdrian Chadd { 5222e0c027eSEd Maste struct ukswitch_softc *sc __diagused; 5237b796c40SAdrian Chadd 5247b796c40SAdrian Chadd sc = device_get_softc(dev); 5257b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_OWNED); 5267b796c40SAdrian Chadd 5277b796c40SAdrian Chadd /* Not supported. */ 5287b796c40SAdrian Chadd return (0); 5297b796c40SAdrian Chadd } 5307b796c40SAdrian Chadd 5317b796c40SAdrian Chadd static device_method_t ukswitch_methods[] = { 5327b796c40SAdrian Chadd /* Device interface */ 5337b796c40SAdrian Chadd DEVMETHOD(device_probe, ukswitch_probe), 5347b796c40SAdrian Chadd DEVMETHOD(device_attach, ukswitch_attach), 5357b796c40SAdrian Chadd DEVMETHOD(device_detach, ukswitch_detach), 5367b796c40SAdrian Chadd 5377b796c40SAdrian Chadd /* bus interface */ 5387b796c40SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 5397b796c40SAdrian Chadd 5407b796c40SAdrian Chadd /* MII interface */ 5417b796c40SAdrian Chadd DEVMETHOD(miibus_readreg, ukswitch_readphy), 5427b796c40SAdrian Chadd DEVMETHOD(miibus_writereg, ukswitch_writephy), 5437b796c40SAdrian Chadd DEVMETHOD(miibus_statchg, ukswitch_statchg), 5447b796c40SAdrian Chadd 5457b796c40SAdrian Chadd /* MDIO interface */ 5467b796c40SAdrian Chadd DEVMETHOD(mdio_readreg, ukswitch_readphy), 5477b796c40SAdrian Chadd DEVMETHOD(mdio_writereg, ukswitch_writephy), 5487b796c40SAdrian Chadd 5497b796c40SAdrian Chadd /* etherswitch interface */ 5507b796c40SAdrian Chadd DEVMETHOD(etherswitch_lock, ukswitch_lock), 5517b796c40SAdrian Chadd DEVMETHOD(etherswitch_unlock, ukswitch_unlock), 5527b796c40SAdrian Chadd DEVMETHOD(etherswitch_getinfo, ukswitch_getinfo), 5537b796c40SAdrian Chadd DEVMETHOD(etherswitch_readreg, ukswitch_readreg), 5547b796c40SAdrian Chadd DEVMETHOD(etherswitch_writereg, ukswitch_writereg), 5557b796c40SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, ukswitch_readphy), 5567b796c40SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, ukswitch_writephy), 5577b796c40SAdrian Chadd DEVMETHOD(etherswitch_getport, ukswitch_getport), 5587b796c40SAdrian Chadd DEVMETHOD(etherswitch_setport, ukswitch_setport), 5597b796c40SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, ukswitch_getvgroup), 5607b796c40SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, ukswitch_setvgroup), 5617b796c40SAdrian Chadd 5627b796c40SAdrian Chadd DEVMETHOD_END 5637b796c40SAdrian Chadd }; 5647b796c40SAdrian Chadd 5657b796c40SAdrian Chadd DEFINE_CLASS_0(ukswitch, ukswitch_driver, ukswitch_methods, 5667b796c40SAdrian Chadd sizeof(struct ukswitch_softc)); 5677b796c40SAdrian Chadd 56842726c2fSJohn Baldwin DRIVER_MODULE(ukswitch, mdio, ukswitch_driver, 0, 0); 5693e38757dSJohn Baldwin DRIVER_MODULE(miibus, ukswitch, miibus_driver, 0, 0); 5708933f7d6SJohn Baldwin DRIVER_MODULE(mdio, ukswitch, mdio_driver, 0, 0); 571829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, ukswitch, etherswitch_driver, 0, 0); 5727b796c40SAdrian Chadd MODULE_VERSION(ukswitch, 1); 5737b796c40SAdrian Chadd MODULE_DEPEND(ukswitch, miibus, 1, 1, 1); /* XXX which versions? */ 5747b796c40SAdrian Chadd MODULE_DEPEND(ukswitch, etherswitch, 1, 1, 1); /* XXX which versions? */ 575