xref: /freebsd/sys/dev/etherswitch/ukswitch/ukswitch.c (revision 3e38757d4c52f7c2b33e4ab667ebc55e334a6ca0)
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