xref: /freebsd/sys/dev/etherswitch/ukswitch/ukswitch.c (revision aa48c1ae0831789c6aa34bf3a85b9a2289d425e2)
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
ukswitch_probe(device_t dev)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
ukswitch_attach_phys(struct ukswitch_softc * sc)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
ukswitch_attach(device_t dev)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
ukswitch_detach(device_t dev)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
ukswitch_portforphy(struct ukswitch_softc * sc,int phy)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 *
ukswitch_miiforport(struct ukswitch_softc * sc,int port)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
ukswitch_ifpforport(struct ukswitch_softc * sc,int port)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
ukswitch_miipollstat(struct ukswitch_softc * sc)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
ukswitch_tick(void * arg)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
ukswitch_lock(device_t dev)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
ukswitch_unlock(device_t dev)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 *
ukswitch_getinfo(device_t dev)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
ukswitch_getport(device_t dev,etherswitch_port_t * p)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
ukswitch_setport(device_t dev,etherswitch_port_t * p)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
ukswitch_getvgroup(device_t dev,etherswitch_vlangroup_t * vg)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
ukswitch_setvgroup(device_t dev,etherswitch_vlangroup_t * vg)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
ukswitch_statchg(device_t dev)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
ukswitch_ifmedia_upd(if_t ifp)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
ukswitch_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)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
ukswitch_readphy(device_t dev,int phy,int reg)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
ukswitch_writephy(device_t dev,int phy,int reg,int data)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
ukswitch_readreg(device_t dev,int addr)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
ukswitch_writereg(device_t dev,int addr,int value)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