17b796c40SAdrian Chadd /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * $FreeBSD$ 317b796c40SAdrian Chadd */ 327b796c40SAdrian Chadd 337b796c40SAdrian Chadd #include <sys/param.h> 347b796c40SAdrian Chadd #include <sys/bus.h> 357b796c40SAdrian Chadd #include <sys/errno.h> 367b796c40SAdrian Chadd #include <sys/kernel.h> 3766e01d73SGleb Smirnoff #include <sys/lock.h> 3866e01d73SGleb Smirnoff #include <sys/malloc.h> 397b796c40SAdrian Chadd #include <sys/module.h> 4066e01d73SGleb Smirnoff #include <sys/mutex.h> 417b796c40SAdrian Chadd #include <sys/socket.h> 427b796c40SAdrian Chadd #include <sys/sockio.h> 437b796c40SAdrian Chadd #include <sys/sysctl.h> 447b796c40SAdrian Chadd #include <sys/systm.h> 457b796c40SAdrian Chadd 467b796c40SAdrian Chadd #include <net/if.h> 4766e01d73SGleb Smirnoff #include <net/if_var.h> 487b796c40SAdrian Chadd #include <net/ethernet.h> 497b796c40SAdrian Chadd #include <net/if_media.h> 507b796c40SAdrian Chadd #include <net/if_types.h> 517b796c40SAdrian Chadd 527b796c40SAdrian Chadd #include <machine/bus.h> 537b796c40SAdrian Chadd #include <dev/mii/mii.h> 547b796c40SAdrian Chadd #include <dev/mii/miivar.h> 5571e8eac4SAdrian Chadd #include <dev/mdio/mdio.h> 567b796c40SAdrian Chadd 577b796c40SAdrian Chadd #include <dev/etherswitch/etherswitch.h> 587b796c40SAdrian Chadd 597b796c40SAdrian Chadd #include "mdio_if.h" 607b796c40SAdrian Chadd #include "miibus_if.h" 617b796c40SAdrian Chadd #include "etherswitch_if.h" 627b796c40SAdrian Chadd 637b796c40SAdrian Chadd MALLOC_DECLARE(M_UKSWITCH); 647b796c40SAdrian Chadd MALLOC_DEFINE(M_UKSWITCH, "ukswitch", "ukswitch data structures"); 657b796c40SAdrian Chadd 667b796c40SAdrian Chadd struct ukswitch_softc { 677b796c40SAdrian Chadd struct mtx sc_mtx; /* serialize access to softc */ 687b796c40SAdrian Chadd device_t sc_dev; 697b796c40SAdrian Chadd int media; /* cpu port media */ 707b796c40SAdrian Chadd int cpuport; /* which PHY is connected to the CPU */ 717b796c40SAdrian Chadd int phymask; /* PHYs we manage */ 72b15317c4SMichael Zhilin int phyoffset; /* PHYs register offset */ 737b796c40SAdrian Chadd int numports; /* number of ports */ 747b796c40SAdrian Chadd int ifpport[MII_NPHY]; 757b796c40SAdrian Chadd int *portphy; 767b796c40SAdrian Chadd char **ifname; 777b796c40SAdrian Chadd device_t **miibus; 787b796c40SAdrian Chadd struct ifnet **ifp; 797b796c40SAdrian Chadd struct callout callout_tick; 807b796c40SAdrian Chadd etherswitch_info_t info; 817b796c40SAdrian Chadd }; 827b796c40SAdrian Chadd 837b796c40SAdrian Chadd #define UKSWITCH_LOCK(_sc) \ 847b796c40SAdrian Chadd mtx_lock(&(_sc)->sc_mtx) 857b796c40SAdrian Chadd #define UKSWITCH_UNLOCK(_sc) \ 867b796c40SAdrian Chadd mtx_unlock(&(_sc)->sc_mtx) 877b796c40SAdrian Chadd #define UKSWITCH_LOCK_ASSERT(_sc, _what) \ 887b796c40SAdrian Chadd mtx_assert(&(_sc)->sc_mtx, (_what)) 897b796c40SAdrian Chadd #define UKSWITCH_TRYLOCK(_sc) \ 907b796c40SAdrian Chadd mtx_trylock(&(_sc)->sc_mtx) 917b796c40SAdrian Chadd 927b796c40SAdrian Chadd #if defined(DEBUG) 937b796c40SAdrian Chadd #define DPRINTF(dev, args...) device_printf(dev, args) 947b796c40SAdrian Chadd #else 957b796c40SAdrian Chadd #define DPRINTF(dev, args...) 967b796c40SAdrian Chadd #endif 977b796c40SAdrian Chadd 987b796c40SAdrian Chadd static inline int ukswitch_portforphy(struct ukswitch_softc *, int); 997b796c40SAdrian Chadd static void ukswitch_tick(void *); 1007b796c40SAdrian Chadd static int ukswitch_ifmedia_upd(struct ifnet *); 1017b796c40SAdrian Chadd static void ukswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *); 1027b796c40SAdrian Chadd 1037b796c40SAdrian Chadd static int 1047b796c40SAdrian Chadd ukswitch_probe(device_t dev) 1057b796c40SAdrian Chadd { 1067b796c40SAdrian Chadd struct ukswitch_softc *sc; 1077b796c40SAdrian Chadd 1087b796c40SAdrian Chadd sc = device_get_softc(dev); 1097b796c40SAdrian Chadd bzero(sc, sizeof(*sc)); 1107b796c40SAdrian Chadd 1117b796c40SAdrian Chadd device_set_desc_copy(dev, "Generic MDIO switch driver"); 1127b796c40SAdrian Chadd return (BUS_PROBE_DEFAULT); 1137b796c40SAdrian Chadd } 1147b796c40SAdrian Chadd 1157b796c40SAdrian Chadd static int 1167b796c40SAdrian Chadd ukswitch_attach_phys(struct ukswitch_softc *sc) 1177b796c40SAdrian Chadd { 1187b796c40SAdrian Chadd int phy, port = 0, err = 0; 1197b796c40SAdrian Chadd char name[IFNAMSIZ]; 1207b796c40SAdrian Chadd 1217b796c40SAdrian Chadd /* PHYs need an interface, so we generate a dummy one */ 1227b796c40SAdrian Chadd snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 1237b796c40SAdrian Chadd for (phy = 0; phy < MII_NPHY; phy++) { 1247b796c40SAdrian Chadd if (((1 << phy) & sc->phymask) == 0) 1257b796c40SAdrian Chadd continue; 1267b796c40SAdrian Chadd sc->ifpport[phy] = port; 1277b796c40SAdrian Chadd sc->portphy[port] = phy; 1287b796c40SAdrian Chadd sc->ifp[port] = if_alloc(IFT_ETHER); 1290774131eSMichael Zhilin if (sc->ifp[port] == NULL) { 1300774131eSMichael Zhilin device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n"); 1310774131eSMichael Zhilin err = ENOMEM; 1320774131eSMichael Zhilin break; 1330774131eSMichael Zhilin } 1340774131eSMichael Zhilin 1357b796c40SAdrian Chadd sc->ifp[port]->if_softc = sc; 1367b796c40SAdrian Chadd sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | 1377b796c40SAdrian Chadd IFF_DRV_RUNNING | IFF_SIMPLEX; 1387b796c40SAdrian Chadd sc->ifname[port] = malloc(strlen(name)+1, M_UKSWITCH, M_WAITOK); 1397b796c40SAdrian Chadd bcopy(name, sc->ifname[port], strlen(name)+1); 1407b796c40SAdrian Chadd if_initname(sc->ifp[port], sc->ifname[port], port); 1417b796c40SAdrian Chadd sc->miibus[port] = malloc(sizeof(device_t), M_UKSWITCH, 1427b796c40SAdrian Chadd M_WAITOK | M_ZERO); 1437b796c40SAdrian Chadd err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port], 1447b796c40SAdrian Chadd ukswitch_ifmedia_upd, ukswitch_ifmedia_sts, \ 145b15317c4SMichael Zhilin BMSR_DEFCAPMASK, phy + sc->phyoffset, MII_OFFSET_ANY, 0); 1467b796c40SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 1477b796c40SAdrian Chadd device_get_nameunit(*sc->miibus[port]), 1487b796c40SAdrian Chadd sc->ifp[port]->if_xname); 1497b796c40SAdrian Chadd if (err != 0) { 1507b796c40SAdrian Chadd device_printf(sc->sc_dev, 1517b796c40SAdrian Chadd "attaching PHY %d failed\n", 1527b796c40SAdrian Chadd phy); 1537b796c40SAdrian Chadd break; 1547b796c40SAdrian Chadd } 1557b796c40SAdrian Chadd sc->info.es_nports = port + 1; 1567b796c40SAdrian Chadd if (++port >= sc->numports) 1577b796c40SAdrian Chadd break; 1587b796c40SAdrian Chadd } 1597b796c40SAdrian Chadd return (err); 1607b796c40SAdrian Chadd } 1617b796c40SAdrian Chadd 1627b796c40SAdrian Chadd static int 1637b796c40SAdrian Chadd ukswitch_attach(device_t dev) 1647b796c40SAdrian Chadd { 1657b796c40SAdrian Chadd struct ukswitch_softc *sc; 1667b796c40SAdrian Chadd int err = 0; 1677b796c40SAdrian Chadd 1687b796c40SAdrian Chadd sc = device_get_softc(dev); 1697b796c40SAdrian Chadd 1707b796c40SAdrian Chadd sc->sc_dev = dev; 1717b796c40SAdrian Chadd mtx_init(&sc->sc_mtx, "ukswitch", NULL, MTX_DEF); 1727b796c40SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev), 1737b796c40SAdrian Chadd sizeof(sc->info.es_name)); 1747b796c40SAdrian Chadd 1757b796c40SAdrian Chadd /* XXX Defaults */ 1767b796c40SAdrian Chadd sc->numports = 6; 1777b796c40SAdrian Chadd sc->phymask = 0x0f; 178b15317c4SMichael Zhilin sc->phyoffset = 0; 1791ead288fSAdrian Chadd sc->cpuport = -1; 1807b796c40SAdrian Chadd sc->media = 100; 1817b796c40SAdrian Chadd 1827b796c40SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 1837b796c40SAdrian Chadd "numports", &sc->numports); 1847b796c40SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 1857b796c40SAdrian Chadd "phymask", &sc->phymask); 1867b796c40SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 187b15317c4SMichael Zhilin "phyoffset", &sc->phyoffset); 188b15317c4SMichael Zhilin (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 1897b796c40SAdrian Chadd "cpuport", &sc->cpuport); 1907b796c40SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 1917b796c40SAdrian Chadd "media", &sc->media); 1927b796c40SAdrian Chadd 1937b796c40SAdrian Chadd /* Support only fast and giga ethernet. */ 1947b796c40SAdrian Chadd if (sc->media != 100 && sc->media != 1000) 1957b796c40SAdrian Chadd sc->media = 100; 1967b796c40SAdrian Chadd 1971ead288fSAdrian Chadd if (sc->cpuport != -1) 1987b796c40SAdrian Chadd /* Always attach the cpu port. */ 1997b796c40SAdrian Chadd sc->phymask |= (1 << sc->cpuport); 2007b796c40SAdrian Chadd 2017b796c40SAdrian Chadd /* We do not support any vlan groups. */ 2027b796c40SAdrian Chadd sc->info.es_nvlangroups = 0; 2037b796c40SAdrian Chadd 2047b796c40SAdrian Chadd sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_UKSWITCH, 2057b796c40SAdrian Chadd M_WAITOK | M_ZERO); 2067b796c40SAdrian Chadd sc->ifname = malloc(sizeof(char *) * sc->numports, M_UKSWITCH, 2077b796c40SAdrian Chadd M_WAITOK | M_ZERO); 2087b796c40SAdrian Chadd sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_UKSWITCH, 2097b796c40SAdrian Chadd M_WAITOK | M_ZERO); 2107b796c40SAdrian Chadd sc->portphy = malloc(sizeof(int) * sc->numports, M_UKSWITCH, 2117b796c40SAdrian Chadd M_WAITOK | M_ZERO); 2127b796c40SAdrian Chadd 2137b796c40SAdrian Chadd /* 2147b796c40SAdrian Chadd * Attach the PHYs and complete the bus enumeration. 2157b796c40SAdrian Chadd */ 2167b796c40SAdrian Chadd err = ukswitch_attach_phys(sc); 2177b796c40SAdrian Chadd if (err != 0) 2187b796c40SAdrian Chadd return (err); 2197b796c40SAdrian Chadd 2207b796c40SAdrian Chadd bus_generic_probe(dev); 2217b796c40SAdrian Chadd bus_enumerate_hinted_children(dev); 2227b796c40SAdrian Chadd err = bus_generic_attach(dev); 2237b796c40SAdrian Chadd if (err != 0) 2247b796c40SAdrian Chadd return (err); 2257b796c40SAdrian Chadd 2267b796c40SAdrian Chadd callout_init(&sc->callout_tick, 0); 2277b796c40SAdrian Chadd 2287b796c40SAdrian Chadd ukswitch_tick(sc); 2297b796c40SAdrian Chadd 2307b796c40SAdrian Chadd return (err); 2317b796c40SAdrian Chadd } 2327b796c40SAdrian Chadd 2337b796c40SAdrian Chadd static int 2347b796c40SAdrian Chadd ukswitch_detach(device_t dev) 2357b796c40SAdrian Chadd { 2367b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 2377b796c40SAdrian Chadd int i, port; 2387b796c40SAdrian Chadd 2397b796c40SAdrian Chadd callout_drain(&sc->callout_tick); 2407b796c40SAdrian Chadd 2417b796c40SAdrian Chadd for (i=0; i < MII_NPHY; i++) { 2427b796c40SAdrian Chadd if (((1 << i) & sc->phymask) == 0) 2437b796c40SAdrian Chadd continue; 2447b796c40SAdrian Chadd port = ukswitch_portforphy(sc, i); 2457b796c40SAdrian Chadd if (sc->miibus[port] != NULL) 2467b796c40SAdrian Chadd device_delete_child(dev, (*sc->miibus[port])); 2477b796c40SAdrian Chadd if (sc->ifp[port] != NULL) 2487b796c40SAdrian Chadd if_free(sc->ifp[port]); 2497b796c40SAdrian Chadd free(sc->ifname[port], M_UKSWITCH); 2507b796c40SAdrian Chadd free(sc->miibus[port], M_UKSWITCH); 2517b796c40SAdrian Chadd } 2527b796c40SAdrian Chadd 2537b796c40SAdrian Chadd free(sc->portphy, M_UKSWITCH); 2547b796c40SAdrian Chadd free(sc->miibus, M_UKSWITCH); 2557b796c40SAdrian Chadd free(sc->ifname, M_UKSWITCH); 2567b796c40SAdrian Chadd free(sc->ifp, M_UKSWITCH); 2577b796c40SAdrian Chadd 2587b796c40SAdrian Chadd bus_generic_detach(dev); 2597b796c40SAdrian Chadd mtx_destroy(&sc->sc_mtx); 2607b796c40SAdrian Chadd 2617b796c40SAdrian Chadd return (0); 2627b796c40SAdrian Chadd } 2637b796c40SAdrian Chadd 2647b796c40SAdrian Chadd /* 2657b796c40SAdrian Chadd * Convert PHY number to port number. 2667b796c40SAdrian Chadd */ 2677b796c40SAdrian Chadd static inline int 2687b796c40SAdrian Chadd ukswitch_portforphy(struct ukswitch_softc *sc, int phy) 2697b796c40SAdrian Chadd { 2707b796c40SAdrian Chadd 2717b796c40SAdrian Chadd return (sc->ifpport[phy]); 2727b796c40SAdrian Chadd } 2737b796c40SAdrian Chadd 2747b796c40SAdrian Chadd static inline struct mii_data * 2757b796c40SAdrian Chadd ukswitch_miiforport(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 (device_get_softc(*sc->miibus[port])); 2817b796c40SAdrian Chadd } 2827b796c40SAdrian Chadd 2837b796c40SAdrian Chadd static inline struct ifnet * 2847b796c40SAdrian Chadd ukswitch_ifpforport(struct ukswitch_softc *sc, int port) 2857b796c40SAdrian Chadd { 2867b796c40SAdrian Chadd 2877b796c40SAdrian Chadd if (port < 0 || port > sc->numports) 2887b796c40SAdrian Chadd return (NULL); 2897b796c40SAdrian Chadd return (sc->ifp[port]); 2907b796c40SAdrian Chadd } 2917b796c40SAdrian Chadd 2927b796c40SAdrian Chadd /* 2937b796c40SAdrian Chadd * Poll the status for all PHYs. 2947b796c40SAdrian Chadd */ 2957b796c40SAdrian Chadd static void 2967b796c40SAdrian Chadd ukswitch_miipollstat(struct ukswitch_softc *sc) 2977b796c40SAdrian Chadd { 2987b796c40SAdrian Chadd int i, port; 2997b796c40SAdrian Chadd struct mii_data *mii; 3007b796c40SAdrian Chadd struct mii_softc *miisc; 3017b796c40SAdrian Chadd 3027b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 3037b796c40SAdrian Chadd 3047b796c40SAdrian Chadd for (i = 0; i < MII_NPHY; i++) { 3057b796c40SAdrian Chadd if (((1 << i) & sc->phymask) == 0) 3067b796c40SAdrian Chadd continue; 3077b796c40SAdrian Chadd port = ukswitch_portforphy(sc, i); 3087b796c40SAdrian Chadd if ((*sc->miibus[port]) == NULL) 3097b796c40SAdrian Chadd continue; 3107b796c40SAdrian Chadd mii = device_get_softc(*sc->miibus[port]); 3117b796c40SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 3127b796c40SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 3137b796c40SAdrian Chadd miisc->mii_inst) 3147b796c40SAdrian Chadd continue; 3157b796c40SAdrian Chadd ukphy_status(miisc); 3167b796c40SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT); 3177b796c40SAdrian Chadd } 3187b796c40SAdrian Chadd } 3197b796c40SAdrian Chadd } 3207b796c40SAdrian Chadd 3217b796c40SAdrian Chadd static void 3227b796c40SAdrian Chadd ukswitch_tick(void *arg) 3237b796c40SAdrian Chadd { 3247b796c40SAdrian Chadd struct ukswitch_softc *sc = arg; 3257b796c40SAdrian Chadd 3267b796c40SAdrian Chadd ukswitch_miipollstat(sc); 3277b796c40SAdrian Chadd callout_reset(&sc->callout_tick, hz, ukswitch_tick, sc); 3287b796c40SAdrian Chadd } 3297b796c40SAdrian Chadd 3307b796c40SAdrian Chadd static void 3317b796c40SAdrian Chadd ukswitch_lock(device_t dev) 3327b796c40SAdrian Chadd { 3337b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3347b796c40SAdrian Chadd 3357b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 3367b796c40SAdrian Chadd UKSWITCH_LOCK(sc); 3377b796c40SAdrian Chadd } 3387b796c40SAdrian Chadd 3397b796c40SAdrian Chadd static void 3407b796c40SAdrian Chadd ukswitch_unlock(device_t dev) 3417b796c40SAdrian Chadd { 3427b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3437b796c40SAdrian Chadd 3447b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_OWNED); 3457b796c40SAdrian Chadd UKSWITCH_UNLOCK(sc); 3467b796c40SAdrian Chadd } 3477b796c40SAdrian Chadd 3487b796c40SAdrian Chadd static etherswitch_info_t * 3497b796c40SAdrian Chadd ukswitch_getinfo(device_t dev) 3507b796c40SAdrian Chadd { 3517b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3527b796c40SAdrian Chadd 3537b796c40SAdrian Chadd return (&sc->info); 3547b796c40SAdrian Chadd } 3557b796c40SAdrian Chadd 3567b796c40SAdrian Chadd static int 3577b796c40SAdrian Chadd ukswitch_getport(device_t dev, etherswitch_port_t *p) 3587b796c40SAdrian Chadd { 3597b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3607b796c40SAdrian Chadd struct mii_data *mii; 3617b796c40SAdrian Chadd struct ifmediareq *ifmr = &p->es_ifmr; 3621ead288fSAdrian Chadd int err, phy; 3637b796c40SAdrian Chadd 3647b796c40SAdrian Chadd if (p->es_port < 0 || p->es_port >= sc->numports) 3657b796c40SAdrian Chadd return (ENXIO); 3661ead288fSAdrian Chadd p->es_pvid = 0; 3677b796c40SAdrian Chadd 3681ead288fSAdrian Chadd phy = sc->portphy[p->es_port]; 3697b796c40SAdrian Chadd mii = ukswitch_miiforport(sc, p->es_port); 3701ead288fSAdrian Chadd if (sc->cpuport != -1 && phy == sc->cpuport) { 3717b796c40SAdrian Chadd /* fill in fixed values for CPU port */ 3721ead288fSAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 3737b796c40SAdrian Chadd ifmr->ifm_count = 0; 3747b796c40SAdrian Chadd if (sc->media == 100) 3757b796c40SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 3767b796c40SAdrian Chadd IFM_ETHER | IFM_100_TX | IFM_FDX; 3777b796c40SAdrian Chadd else 3787b796c40SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 3797b796c40SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 3807b796c40SAdrian Chadd ifmr->ifm_mask = 0; 3817b796c40SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 3827b796c40SAdrian Chadd } else if (mii != NULL) { 3837b796c40SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 3847b796c40SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 3857b796c40SAdrian Chadd if (err) 3867b796c40SAdrian Chadd return (err); 3877b796c40SAdrian Chadd } else { 3887b796c40SAdrian Chadd return (ENXIO); 3897b796c40SAdrian Chadd } 3907b796c40SAdrian Chadd return (0); 3917b796c40SAdrian Chadd } 3927b796c40SAdrian Chadd 3937b796c40SAdrian Chadd static int 3947b796c40SAdrian Chadd ukswitch_setport(device_t dev, etherswitch_port_t *p) 3957b796c40SAdrian Chadd { 3967b796c40SAdrian Chadd struct ukswitch_softc *sc = device_get_softc(dev); 3977b796c40SAdrian Chadd struct ifmedia *ifm; 3987b796c40SAdrian Chadd struct mii_data *mii; 3997b796c40SAdrian Chadd struct ifnet *ifp; 4007b796c40SAdrian Chadd int err; 4017b796c40SAdrian Chadd 4027b796c40SAdrian Chadd if (p->es_port < 0 || p->es_port >= sc->numports) 4037b796c40SAdrian Chadd return (ENXIO); 4047b796c40SAdrian Chadd 4057b796c40SAdrian Chadd if (sc->portphy[p->es_port] == sc->cpuport) 4067b796c40SAdrian Chadd return (ENXIO); 4077b796c40SAdrian Chadd 4087b796c40SAdrian Chadd mii = ukswitch_miiforport(sc, p->es_port); 4097b796c40SAdrian Chadd if (mii == NULL) 4107b796c40SAdrian Chadd return (ENXIO); 4117b796c40SAdrian Chadd 4127b796c40SAdrian Chadd ifp = ukswitch_ifpforport(sc, p->es_port); 4137b796c40SAdrian Chadd 4147b796c40SAdrian Chadd ifm = &mii->mii_media; 4157b796c40SAdrian Chadd err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA); 4167b796c40SAdrian Chadd return (err); 4177b796c40SAdrian Chadd } 4187b796c40SAdrian Chadd 4197b796c40SAdrian Chadd static int 4207b796c40SAdrian Chadd ukswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 4217b796c40SAdrian Chadd { 4227b796c40SAdrian Chadd 4237b796c40SAdrian Chadd /* Not supported. */ 4247b796c40SAdrian Chadd vg->es_vid = 0; 4257b796c40SAdrian Chadd vg->es_member_ports = 0; 4267b796c40SAdrian Chadd vg->es_untagged_ports = 0; 4277b796c40SAdrian Chadd vg->es_fid = 0; 4287b796c40SAdrian Chadd return (0); 4297b796c40SAdrian Chadd } 4307b796c40SAdrian Chadd 4317b796c40SAdrian Chadd static int 4327b796c40SAdrian Chadd ukswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 4337b796c40SAdrian Chadd { 4347b796c40SAdrian Chadd 4357b796c40SAdrian Chadd /* Not supported. */ 4367b796c40SAdrian Chadd return (0); 4377b796c40SAdrian Chadd } 4387b796c40SAdrian Chadd 4397b796c40SAdrian Chadd static void 4407b796c40SAdrian Chadd ukswitch_statchg(device_t dev) 4417b796c40SAdrian Chadd { 4427b796c40SAdrian Chadd 4437b796c40SAdrian Chadd DPRINTF(dev, "%s\n", __func__); 4447b796c40SAdrian Chadd } 4457b796c40SAdrian Chadd 4467b796c40SAdrian Chadd static int 4477b796c40SAdrian Chadd ukswitch_ifmedia_upd(struct ifnet *ifp) 4487b796c40SAdrian Chadd { 4497b796c40SAdrian Chadd struct ukswitch_softc *sc = ifp->if_softc; 4507b796c40SAdrian Chadd struct mii_data *mii = ukswitch_miiforport(sc, ifp->if_dunit); 4517b796c40SAdrian Chadd 4527b796c40SAdrian Chadd DPRINTF(sc->sc_dev, "%s\n", __func__); 4537b796c40SAdrian Chadd if (mii == NULL) 4547b796c40SAdrian Chadd return (ENXIO); 4557b796c40SAdrian Chadd mii_mediachg(mii); 4567b796c40SAdrian Chadd return (0); 4577b796c40SAdrian Chadd } 4587b796c40SAdrian Chadd 4597b796c40SAdrian Chadd static void 4607b796c40SAdrian Chadd ukswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 4617b796c40SAdrian Chadd { 4627b796c40SAdrian Chadd struct ukswitch_softc *sc = ifp->if_softc; 4637b796c40SAdrian Chadd struct mii_data *mii = ukswitch_miiforport(sc, ifp->if_dunit); 4647b796c40SAdrian Chadd 4657b796c40SAdrian Chadd DPRINTF(sc->sc_dev, "%s\n", __func__); 4667b796c40SAdrian Chadd 4677b796c40SAdrian Chadd if (mii == NULL) 4687b796c40SAdrian Chadd return; 4697b796c40SAdrian Chadd mii_pollstat(mii); 4707b796c40SAdrian Chadd ifmr->ifm_active = mii->mii_media_active; 4717b796c40SAdrian Chadd ifmr->ifm_status = mii->mii_media_status; 4727b796c40SAdrian Chadd } 4737b796c40SAdrian Chadd 4747b796c40SAdrian Chadd static int 4757b796c40SAdrian Chadd ukswitch_readphy(device_t dev, int phy, int reg) 4767b796c40SAdrian Chadd { 4777b796c40SAdrian Chadd struct ukswitch_softc *sc; 4787b796c40SAdrian Chadd int data; 4797b796c40SAdrian Chadd 4807b796c40SAdrian Chadd sc = device_get_softc(dev); 4817b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 4827b796c40SAdrian Chadd 4837b796c40SAdrian Chadd if (phy < 0 || phy >= 32) 4847b796c40SAdrian Chadd return (ENXIO); 4857b796c40SAdrian Chadd if (reg < 0 || reg >= 32) 4867b796c40SAdrian Chadd return (ENXIO); 4877b796c40SAdrian Chadd 4887b796c40SAdrian Chadd UKSWITCH_LOCK(sc); 4897b796c40SAdrian Chadd data = MDIO_READREG(device_get_parent(dev), phy, reg); 4907b796c40SAdrian Chadd UKSWITCH_UNLOCK(sc); 4917b796c40SAdrian Chadd 4927b796c40SAdrian Chadd return (data); 4937b796c40SAdrian Chadd } 4947b796c40SAdrian Chadd 4957b796c40SAdrian Chadd static int 4967b796c40SAdrian Chadd ukswitch_writephy(device_t dev, int phy, int reg, int data) 4977b796c40SAdrian Chadd { 4987b796c40SAdrian Chadd struct ukswitch_softc *sc; 4997b796c40SAdrian Chadd int err; 5007b796c40SAdrian Chadd 5017b796c40SAdrian Chadd sc = device_get_softc(dev); 5027b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 5037b796c40SAdrian Chadd 5047b796c40SAdrian Chadd if (phy < 0 || phy >= 32) 5057b796c40SAdrian Chadd return (ENXIO); 5067b796c40SAdrian Chadd if (reg < 0 || reg >= 32) 5077b796c40SAdrian Chadd return (ENXIO); 5087b796c40SAdrian Chadd 5097b796c40SAdrian Chadd UKSWITCH_LOCK(sc); 5107b796c40SAdrian Chadd err = MDIO_WRITEREG(device_get_parent(dev), phy, reg, data); 5117b796c40SAdrian Chadd UKSWITCH_UNLOCK(sc); 5127b796c40SAdrian Chadd 5137b796c40SAdrian Chadd return (err); 5147b796c40SAdrian Chadd } 5157b796c40SAdrian Chadd 5167b796c40SAdrian Chadd static int 5177b796c40SAdrian Chadd ukswitch_readreg(device_t dev, int addr) 5187b796c40SAdrian Chadd { 5197b796c40SAdrian Chadd struct ukswitch_softc *sc; 5207b796c40SAdrian Chadd 5217b796c40SAdrian Chadd sc = device_get_softc(dev); 5227b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_OWNED); 5237b796c40SAdrian Chadd 5247b796c40SAdrian Chadd /* Not supported. */ 5257b796c40SAdrian Chadd return (0); 5267b796c40SAdrian Chadd } 5277b796c40SAdrian Chadd 5287b796c40SAdrian Chadd static int 5297b796c40SAdrian Chadd ukswitch_writereg(device_t dev, int addr, int value) 5307b796c40SAdrian Chadd { 5317b796c40SAdrian Chadd struct ukswitch_softc *sc; 5327b796c40SAdrian Chadd 5337b796c40SAdrian Chadd sc = device_get_softc(dev); 5347b796c40SAdrian Chadd UKSWITCH_LOCK_ASSERT(sc, MA_OWNED); 5357b796c40SAdrian Chadd 5367b796c40SAdrian Chadd /* Not supported. */ 5377b796c40SAdrian Chadd return (0); 5387b796c40SAdrian Chadd } 5397b796c40SAdrian Chadd 5407b796c40SAdrian Chadd static device_method_t ukswitch_methods[] = { 5417b796c40SAdrian Chadd /* Device interface */ 5427b796c40SAdrian Chadd DEVMETHOD(device_probe, ukswitch_probe), 5437b796c40SAdrian Chadd DEVMETHOD(device_attach, ukswitch_attach), 5447b796c40SAdrian Chadd DEVMETHOD(device_detach, ukswitch_detach), 5457b796c40SAdrian Chadd 5467b796c40SAdrian Chadd /* bus interface */ 5477b796c40SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 5487b796c40SAdrian Chadd 5497b796c40SAdrian Chadd /* MII interface */ 5507b796c40SAdrian Chadd DEVMETHOD(miibus_readreg, ukswitch_readphy), 5517b796c40SAdrian Chadd DEVMETHOD(miibus_writereg, ukswitch_writephy), 5527b796c40SAdrian Chadd DEVMETHOD(miibus_statchg, ukswitch_statchg), 5537b796c40SAdrian Chadd 5547b796c40SAdrian Chadd /* MDIO interface */ 5557b796c40SAdrian Chadd DEVMETHOD(mdio_readreg, ukswitch_readphy), 5567b796c40SAdrian Chadd DEVMETHOD(mdio_writereg, ukswitch_writephy), 5577b796c40SAdrian Chadd 5587b796c40SAdrian Chadd /* etherswitch interface */ 5597b796c40SAdrian Chadd DEVMETHOD(etherswitch_lock, ukswitch_lock), 5607b796c40SAdrian Chadd DEVMETHOD(etherswitch_unlock, ukswitch_unlock), 5617b796c40SAdrian Chadd DEVMETHOD(etherswitch_getinfo, ukswitch_getinfo), 5627b796c40SAdrian Chadd DEVMETHOD(etherswitch_readreg, ukswitch_readreg), 5637b796c40SAdrian Chadd DEVMETHOD(etherswitch_writereg, ukswitch_writereg), 5647b796c40SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, ukswitch_readphy), 5657b796c40SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, ukswitch_writephy), 5667b796c40SAdrian Chadd DEVMETHOD(etherswitch_getport, ukswitch_getport), 5677b796c40SAdrian Chadd DEVMETHOD(etherswitch_setport, ukswitch_setport), 5687b796c40SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, ukswitch_getvgroup), 5697b796c40SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, ukswitch_setvgroup), 5707b796c40SAdrian Chadd 5717b796c40SAdrian Chadd DEVMETHOD_END 5727b796c40SAdrian Chadd }; 5737b796c40SAdrian Chadd 5747b796c40SAdrian Chadd DEFINE_CLASS_0(ukswitch, ukswitch_driver, ukswitch_methods, 5757b796c40SAdrian Chadd sizeof(struct ukswitch_softc)); 5767b796c40SAdrian Chadd static devclass_t ukswitch_devclass; 5777b796c40SAdrian Chadd 5787b796c40SAdrian Chadd DRIVER_MODULE(ukswitch, mdio, ukswitch_driver, ukswitch_devclass, 0, 0); 579*3e38757dSJohn Baldwin DRIVER_MODULE(miibus, ukswitch, miibus_driver, 0, 0); 5807b796c40SAdrian Chadd DRIVER_MODULE(mdio, ukswitch, mdio_driver, mdio_devclass, 0, 0); 5817b796c40SAdrian Chadd DRIVER_MODULE(etherswitch, ukswitch, etherswitch_driver, etherswitch_devclass, 0, 0); 5827b796c40SAdrian Chadd MODULE_VERSION(ukswitch, 1); 5837b796c40SAdrian Chadd MODULE_DEPEND(ukswitch, miibus, 1, 1, 1); /* XXX which versions? */ 5847b796c40SAdrian Chadd MODULE_DEPEND(ukswitch, etherswitch, 1, 1, 1); /* XXX which versions? */ 585