xref: /freebsd/sys/dev/etherswitch/ukswitch/ukswitch.c (revision 66e01d73cda653a2fdb5e0fbb88904f7d7e718c1)
17b796c40SAdrian Chadd /*-
27b796c40SAdrian Chadd  * Copyright (c) 2013 Luiz Otavio O Souza.
37b796c40SAdrian Chadd  * Copyright (c) 2011-2012 Stefan Bethke.
47b796c40SAdrian Chadd  * Copyright (c) 2012 Adrian Chadd.
57b796c40SAdrian Chadd  * All rights reserved.
67b796c40SAdrian Chadd  *
77b796c40SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
87b796c40SAdrian Chadd  * modification, are permitted provided that the following conditions
97b796c40SAdrian Chadd  * are met:
107b796c40SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
117b796c40SAdrian Chadd  *    notice, this list of conditions and the following disclaimer.
127b796c40SAdrian Chadd  * 2. Redistributions in binary form must reproduce the above copyright
137b796c40SAdrian Chadd  *    notice, this list of conditions and the following disclaimer in the
147b796c40SAdrian Chadd  *    documentation and/or other materials provided with the distribution.
157b796c40SAdrian Chadd  *
167b796c40SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177b796c40SAdrian Chadd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187b796c40SAdrian Chadd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197b796c40SAdrian Chadd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207b796c40SAdrian Chadd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217b796c40SAdrian Chadd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227b796c40SAdrian Chadd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237b796c40SAdrian Chadd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247b796c40SAdrian Chadd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257b796c40SAdrian Chadd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267b796c40SAdrian Chadd  * SUCH DAMAGE.
277b796c40SAdrian Chadd  *
287b796c40SAdrian Chadd  * $FreeBSD$
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>
35*66e01d73SGleb Smirnoff #include <sys/lock.h>
36*66e01d73SGleb Smirnoff #include <sys/malloc.h>
377b796c40SAdrian Chadd #include <sys/module.h>
38*66e01d73SGleb 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>
45*66e01d73SGleb 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>
537b796c40SAdrian Chadd #include <dev/etherswitch/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 */
707b796c40SAdrian Chadd 	int		numports;	/* number of ports */
717b796c40SAdrian Chadd 	int		ifpport[MII_NPHY];
727b796c40SAdrian Chadd 	int		*portphy;
737b796c40SAdrian Chadd 	char		**ifname;
747b796c40SAdrian Chadd 	device_t	**miibus;
757b796c40SAdrian Chadd 	struct ifnet	**ifp;
767b796c40SAdrian Chadd 	struct callout	callout_tick;
777b796c40SAdrian Chadd 	etherswitch_info_t	info;
787b796c40SAdrian Chadd };
797b796c40SAdrian Chadd 
807b796c40SAdrian Chadd #define UKSWITCH_LOCK(_sc)			\
817b796c40SAdrian Chadd 	    mtx_lock(&(_sc)->sc_mtx)
827b796c40SAdrian Chadd #define UKSWITCH_UNLOCK(_sc)			\
837b796c40SAdrian Chadd 	    mtx_unlock(&(_sc)->sc_mtx)
847b796c40SAdrian Chadd #define UKSWITCH_LOCK_ASSERT(_sc, _what)	\
857b796c40SAdrian Chadd 	    mtx_assert(&(_sc)->sc_mtx, (_what))
867b796c40SAdrian Chadd #define UKSWITCH_TRYLOCK(_sc)			\
877b796c40SAdrian Chadd 	    mtx_trylock(&(_sc)->sc_mtx)
887b796c40SAdrian Chadd 
897b796c40SAdrian Chadd #if defined(DEBUG)
907b796c40SAdrian Chadd #define	DPRINTF(dev, args...) device_printf(dev, args)
917b796c40SAdrian Chadd #else
927b796c40SAdrian Chadd #define	DPRINTF(dev, args...)
937b796c40SAdrian Chadd #endif
947b796c40SAdrian Chadd 
957b796c40SAdrian Chadd static inline int ukswitch_portforphy(struct ukswitch_softc *, int);
967b796c40SAdrian Chadd static void ukswitch_tick(void *);
977b796c40SAdrian Chadd static int ukswitch_ifmedia_upd(struct ifnet *);
987b796c40SAdrian Chadd static void ukswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *);
997b796c40SAdrian Chadd 
1007b796c40SAdrian Chadd static int
1017b796c40SAdrian Chadd ukswitch_probe(device_t dev)
1027b796c40SAdrian Chadd {
1037b796c40SAdrian Chadd 	struct ukswitch_softc *sc;
1047b796c40SAdrian Chadd 
1057b796c40SAdrian Chadd 	sc = device_get_softc(dev);
1067b796c40SAdrian Chadd 	bzero(sc, sizeof(*sc));
1077b796c40SAdrian Chadd 
1087b796c40SAdrian Chadd 	device_set_desc_copy(dev, "Generic MDIO switch driver");
1097b796c40SAdrian Chadd 	return (BUS_PROBE_DEFAULT);
1107b796c40SAdrian Chadd }
1117b796c40SAdrian Chadd 
1127b796c40SAdrian Chadd static int
1137b796c40SAdrian Chadd ukswitch_attach_phys(struct ukswitch_softc *sc)
1147b796c40SAdrian Chadd {
1157b796c40SAdrian Chadd 	int phy, port = 0, err = 0;
1167b796c40SAdrian Chadd 	char name[IFNAMSIZ];
1177b796c40SAdrian Chadd 
1187b796c40SAdrian Chadd 	/* PHYs need an interface, so we generate a dummy one */
1197b796c40SAdrian Chadd 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
1207b796c40SAdrian Chadd 	for (phy = 0; phy < MII_NPHY; phy++) {
1217b796c40SAdrian Chadd 		if (((1 << phy) & sc->phymask) == 0)
1227b796c40SAdrian Chadd 			continue;
1237b796c40SAdrian Chadd 		sc->ifpport[phy] = port;
1247b796c40SAdrian Chadd 		sc->portphy[port] = phy;
1257b796c40SAdrian Chadd 		sc->ifp[port] = if_alloc(IFT_ETHER);
1267b796c40SAdrian Chadd 		sc->ifp[port]->if_softc = sc;
1277b796c40SAdrian Chadd 		sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
1287b796c40SAdrian Chadd 		    IFF_DRV_RUNNING | IFF_SIMPLEX;
1297b796c40SAdrian Chadd 		sc->ifname[port] = malloc(strlen(name)+1, M_UKSWITCH, M_WAITOK);
1307b796c40SAdrian Chadd 		bcopy(name, sc->ifname[port], strlen(name)+1);
1317b796c40SAdrian Chadd 		if_initname(sc->ifp[port], sc->ifname[port], port);
1327b796c40SAdrian Chadd 		sc->miibus[port] = malloc(sizeof(device_t), M_UKSWITCH,
1337b796c40SAdrian Chadd 		    M_WAITOK | M_ZERO);
1347b796c40SAdrian Chadd 		err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
1357b796c40SAdrian Chadd 		    ukswitch_ifmedia_upd, ukswitch_ifmedia_sts, \
1367b796c40SAdrian Chadd 		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
1377b796c40SAdrian Chadd 		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
1387b796c40SAdrian Chadd 		    device_get_nameunit(*sc->miibus[port]),
1397b796c40SAdrian Chadd 		    sc->ifp[port]->if_xname);
1407b796c40SAdrian Chadd 		if (err != 0) {
1417b796c40SAdrian Chadd 			device_printf(sc->sc_dev,
1427b796c40SAdrian Chadd 			    "attaching PHY %d failed\n",
1437b796c40SAdrian Chadd 			    phy);
1447b796c40SAdrian Chadd 			break;
1457b796c40SAdrian Chadd 		}
1467b796c40SAdrian Chadd 		sc->info.es_nports = port + 1;
1477b796c40SAdrian Chadd 		if (++port >= sc->numports)
1487b796c40SAdrian Chadd 			break;
1497b796c40SAdrian Chadd 	}
1507b796c40SAdrian Chadd 	return (err);
1517b796c40SAdrian Chadd }
1527b796c40SAdrian Chadd 
1537b796c40SAdrian Chadd static int
1547b796c40SAdrian Chadd ukswitch_attach(device_t dev)
1557b796c40SAdrian Chadd {
1567b796c40SAdrian Chadd 	struct ukswitch_softc *sc;
1577b796c40SAdrian Chadd 	int err = 0;
1587b796c40SAdrian Chadd 
1597b796c40SAdrian Chadd 	sc = device_get_softc(dev);
1607b796c40SAdrian Chadd 
1617b796c40SAdrian Chadd 	sc->sc_dev = dev;
1627b796c40SAdrian Chadd 	mtx_init(&sc->sc_mtx, "ukswitch", NULL, MTX_DEF);
1637b796c40SAdrian Chadd 	strlcpy(sc->info.es_name, device_get_desc(dev),
1647b796c40SAdrian Chadd 	    sizeof(sc->info.es_name));
1657b796c40SAdrian Chadd 
1667b796c40SAdrian Chadd 	/* XXX Defaults */
1677b796c40SAdrian Chadd 	sc->numports = 6;
1687b796c40SAdrian Chadd 	sc->phymask = 0x0f;
1691ead288fSAdrian Chadd 	sc->cpuport = -1;
1707b796c40SAdrian Chadd 	sc->media = 100;
1717b796c40SAdrian Chadd 
1727b796c40SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
1737b796c40SAdrian Chadd 	    "numports", &sc->numports);
1747b796c40SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
1757b796c40SAdrian Chadd 	    "phymask", &sc->phymask);
1767b796c40SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
1777b796c40SAdrian Chadd 	    "cpuport", &sc->cpuport);
1787b796c40SAdrian Chadd 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
1797b796c40SAdrian Chadd 	    "media", &sc->media);
1807b796c40SAdrian Chadd 
1817b796c40SAdrian Chadd 	/* Support only fast and giga ethernet. */
1827b796c40SAdrian Chadd 	if (sc->media != 100 && sc->media != 1000)
1837b796c40SAdrian Chadd 		sc->media = 100;
1847b796c40SAdrian Chadd 
1851ead288fSAdrian Chadd 	if (sc->cpuport != -1)
1867b796c40SAdrian Chadd 		/* Always attach the cpu port. */
1877b796c40SAdrian Chadd 		sc->phymask |= (1 << sc->cpuport);
1887b796c40SAdrian Chadd 
1897b796c40SAdrian Chadd 	/* We do not support any vlan groups. */
1907b796c40SAdrian Chadd 	sc->info.es_nvlangroups = 0;
1917b796c40SAdrian Chadd 
1927b796c40SAdrian Chadd 	sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_UKSWITCH,
1937b796c40SAdrian Chadd 	    M_WAITOK | M_ZERO);
1947b796c40SAdrian Chadd 	sc->ifname = malloc(sizeof(char *) * sc->numports, M_UKSWITCH,
1957b796c40SAdrian Chadd 	    M_WAITOK | M_ZERO);
1967b796c40SAdrian Chadd 	sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_UKSWITCH,
1977b796c40SAdrian Chadd 	    M_WAITOK | M_ZERO);
1987b796c40SAdrian Chadd 	sc->portphy = malloc(sizeof(int) * sc->numports, M_UKSWITCH,
1997b796c40SAdrian Chadd 	    M_WAITOK | M_ZERO);
2007b796c40SAdrian Chadd 
2017b796c40SAdrian Chadd 	/*
2027b796c40SAdrian Chadd 	 * Attach the PHYs and complete the bus enumeration.
2037b796c40SAdrian Chadd 	 */
2047b796c40SAdrian Chadd 	err = ukswitch_attach_phys(sc);
2057b796c40SAdrian Chadd 	if (err != 0)
2067b796c40SAdrian Chadd 		return (err);
2077b796c40SAdrian Chadd 
2087b796c40SAdrian Chadd 	bus_generic_probe(dev);
2097b796c40SAdrian Chadd 	bus_enumerate_hinted_children(dev);
2107b796c40SAdrian Chadd 	err = bus_generic_attach(dev);
2117b796c40SAdrian Chadd 	if (err != 0)
2127b796c40SAdrian Chadd 		return (err);
2137b796c40SAdrian Chadd 
2147b796c40SAdrian Chadd 	callout_init(&sc->callout_tick, 0);
2157b796c40SAdrian Chadd 
2167b796c40SAdrian Chadd 	ukswitch_tick(sc);
2177b796c40SAdrian Chadd 
2187b796c40SAdrian Chadd 	return (err);
2197b796c40SAdrian Chadd }
2207b796c40SAdrian Chadd 
2217b796c40SAdrian Chadd static int
2227b796c40SAdrian Chadd ukswitch_detach(device_t dev)
2237b796c40SAdrian Chadd {
2247b796c40SAdrian Chadd 	struct ukswitch_softc *sc = device_get_softc(dev);
2257b796c40SAdrian Chadd 	int i, port;
2267b796c40SAdrian Chadd 
2277b796c40SAdrian Chadd 	callout_drain(&sc->callout_tick);
2287b796c40SAdrian Chadd 
2297b796c40SAdrian Chadd 	for (i=0; i < MII_NPHY; i++) {
2307b796c40SAdrian Chadd 		if (((1 << i) & sc->phymask) == 0)
2317b796c40SAdrian Chadd 			continue;
2327b796c40SAdrian Chadd 		port = ukswitch_portforphy(sc, i);
2337b796c40SAdrian Chadd 		if (sc->miibus[port] != NULL)
2347b796c40SAdrian Chadd 			device_delete_child(dev, (*sc->miibus[port]));
2357b796c40SAdrian Chadd 		if (sc->ifp[port] != NULL)
2367b796c40SAdrian Chadd 			if_free(sc->ifp[port]);
2377b796c40SAdrian Chadd 		free(sc->ifname[port], M_UKSWITCH);
2387b796c40SAdrian Chadd 		free(sc->miibus[port], M_UKSWITCH);
2397b796c40SAdrian Chadd 	}
2407b796c40SAdrian Chadd 
2417b796c40SAdrian Chadd 	free(sc->portphy, M_UKSWITCH);
2427b796c40SAdrian Chadd 	free(sc->miibus, M_UKSWITCH);
2437b796c40SAdrian Chadd 	free(sc->ifname, M_UKSWITCH);
2447b796c40SAdrian Chadd 	free(sc->ifp, M_UKSWITCH);
2457b796c40SAdrian Chadd 
2467b796c40SAdrian Chadd 	bus_generic_detach(dev);
2477b796c40SAdrian Chadd 	mtx_destroy(&sc->sc_mtx);
2487b796c40SAdrian Chadd 
2497b796c40SAdrian Chadd 	return (0);
2507b796c40SAdrian Chadd }
2517b796c40SAdrian Chadd 
2527b796c40SAdrian Chadd /*
2537b796c40SAdrian Chadd  * Convert PHY number to port number.
2547b796c40SAdrian Chadd  */
2557b796c40SAdrian Chadd static inline int
2567b796c40SAdrian Chadd ukswitch_portforphy(struct ukswitch_softc *sc, int phy)
2577b796c40SAdrian Chadd {
2587b796c40SAdrian Chadd 
2597b796c40SAdrian Chadd 	return (sc->ifpport[phy]);
2607b796c40SAdrian Chadd }
2617b796c40SAdrian Chadd 
2627b796c40SAdrian Chadd static inline struct mii_data *
2637b796c40SAdrian Chadd ukswitch_miiforport(struct ukswitch_softc *sc, int port)
2647b796c40SAdrian Chadd {
2657b796c40SAdrian Chadd 
2667b796c40SAdrian Chadd 	if (port < 0 || port > sc->numports)
2677b796c40SAdrian Chadd 		return (NULL);
2687b796c40SAdrian Chadd 	return (device_get_softc(*sc->miibus[port]));
2697b796c40SAdrian Chadd }
2707b796c40SAdrian Chadd 
2717b796c40SAdrian Chadd static inline struct ifnet *
2727b796c40SAdrian Chadd ukswitch_ifpforport(struct ukswitch_softc *sc, int port)
2737b796c40SAdrian Chadd {
2747b796c40SAdrian Chadd 
2757b796c40SAdrian Chadd 	if (port < 0 || port > sc->numports)
2767b796c40SAdrian Chadd 		return (NULL);
2777b796c40SAdrian Chadd 	return (sc->ifp[port]);
2787b796c40SAdrian Chadd }
2797b796c40SAdrian Chadd 
2807b796c40SAdrian Chadd /*
2817b796c40SAdrian Chadd  * Poll the status for all PHYs.
2827b796c40SAdrian Chadd  */
2837b796c40SAdrian Chadd static void
2847b796c40SAdrian Chadd ukswitch_miipollstat(struct ukswitch_softc *sc)
2857b796c40SAdrian Chadd {
2867b796c40SAdrian Chadd 	int i, port;
2877b796c40SAdrian Chadd 	struct mii_data *mii;
2887b796c40SAdrian Chadd 	struct mii_softc *miisc;
2897b796c40SAdrian Chadd 
2907b796c40SAdrian Chadd 	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
2917b796c40SAdrian Chadd 
2927b796c40SAdrian Chadd 	for (i = 0; i < MII_NPHY; i++) {
2937b796c40SAdrian Chadd 		if (((1 << i) & sc->phymask) == 0)
2947b796c40SAdrian Chadd 			continue;
2957b796c40SAdrian Chadd 		port = ukswitch_portforphy(sc, i);
2967b796c40SAdrian Chadd 		if ((*sc->miibus[port]) == NULL)
2977b796c40SAdrian Chadd 			continue;
2987b796c40SAdrian Chadd 		mii = device_get_softc(*sc->miibus[port]);
2997b796c40SAdrian Chadd 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
3007b796c40SAdrian Chadd 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
3017b796c40SAdrian Chadd 			    miisc->mii_inst)
3027b796c40SAdrian Chadd 				continue;
3037b796c40SAdrian Chadd 			ukphy_status(miisc);
3047b796c40SAdrian Chadd 			mii_phy_update(miisc, MII_POLLSTAT);
3057b796c40SAdrian Chadd 		}
3067b796c40SAdrian Chadd 	}
3077b796c40SAdrian Chadd }
3087b796c40SAdrian Chadd 
3097b796c40SAdrian Chadd static void
3107b796c40SAdrian Chadd ukswitch_tick(void *arg)
3117b796c40SAdrian Chadd {
3127b796c40SAdrian Chadd 	struct ukswitch_softc *sc = arg;
3137b796c40SAdrian Chadd 
3147b796c40SAdrian Chadd 	ukswitch_miipollstat(sc);
3157b796c40SAdrian Chadd 	callout_reset(&sc->callout_tick, hz, ukswitch_tick, sc);
3167b796c40SAdrian Chadd }
3177b796c40SAdrian Chadd 
3187b796c40SAdrian Chadd static void
3197b796c40SAdrian Chadd ukswitch_lock(device_t dev)
3207b796c40SAdrian Chadd {
3217b796c40SAdrian Chadd 	struct ukswitch_softc *sc = device_get_softc(dev);
3227b796c40SAdrian Chadd 
3237b796c40SAdrian Chadd 	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
3247b796c40SAdrian Chadd 	UKSWITCH_LOCK(sc);
3257b796c40SAdrian Chadd }
3267b796c40SAdrian Chadd 
3277b796c40SAdrian Chadd static void
3287b796c40SAdrian Chadd ukswitch_unlock(device_t dev)
3297b796c40SAdrian Chadd {
3307b796c40SAdrian Chadd 	struct ukswitch_softc *sc = device_get_softc(dev);
3317b796c40SAdrian Chadd 
3327b796c40SAdrian Chadd 	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
3337b796c40SAdrian Chadd 	UKSWITCH_UNLOCK(sc);
3347b796c40SAdrian Chadd }
3357b796c40SAdrian Chadd 
3367b796c40SAdrian Chadd static etherswitch_info_t *
3377b796c40SAdrian Chadd ukswitch_getinfo(device_t dev)
3387b796c40SAdrian Chadd {
3397b796c40SAdrian Chadd 	struct ukswitch_softc *sc = device_get_softc(dev);
3407b796c40SAdrian Chadd 
3417b796c40SAdrian Chadd 	return (&sc->info);
3427b796c40SAdrian Chadd }
3437b796c40SAdrian Chadd 
3447b796c40SAdrian Chadd static int
3457b796c40SAdrian Chadd ukswitch_getport(device_t dev, etherswitch_port_t *p)
3467b796c40SAdrian Chadd {
3477b796c40SAdrian Chadd 	struct ukswitch_softc *sc = device_get_softc(dev);
3487b796c40SAdrian Chadd 	struct mii_data *mii;
3497b796c40SAdrian Chadd 	struct ifmediareq *ifmr = &p->es_ifmr;
3501ead288fSAdrian Chadd 	int err, phy;
3517b796c40SAdrian Chadd 
3527b796c40SAdrian Chadd 	if (p->es_port < 0 || p->es_port >= sc->numports)
3537b796c40SAdrian Chadd 		return (ENXIO);
3541ead288fSAdrian Chadd 	p->es_pvid = 0;
3557b796c40SAdrian Chadd 
3561ead288fSAdrian Chadd 	phy = sc->portphy[p->es_port];
3577b796c40SAdrian Chadd 	mii = ukswitch_miiforport(sc, p->es_port);
3581ead288fSAdrian Chadd 	if (sc->cpuport != -1 && phy == sc->cpuport) {
3597b796c40SAdrian Chadd 		/* fill in fixed values for CPU port */
3601ead288fSAdrian Chadd 		p->es_flags |= ETHERSWITCH_PORT_CPU;
3617b796c40SAdrian Chadd 		ifmr->ifm_count = 0;
3627b796c40SAdrian Chadd 		if (sc->media == 100)
3637b796c40SAdrian Chadd 			ifmr->ifm_current = ifmr->ifm_active =
3647b796c40SAdrian Chadd 			    IFM_ETHER | IFM_100_TX | IFM_FDX;
3657b796c40SAdrian Chadd 		else
3667b796c40SAdrian Chadd 			ifmr->ifm_current = ifmr->ifm_active =
3677b796c40SAdrian Chadd 			    IFM_ETHER | IFM_1000_T | IFM_FDX;
3687b796c40SAdrian Chadd 		ifmr->ifm_mask = 0;
3697b796c40SAdrian Chadd 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
3707b796c40SAdrian Chadd 	} else if (mii != NULL) {
3717b796c40SAdrian Chadd 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
3727b796c40SAdrian Chadd 		    &mii->mii_media, SIOCGIFMEDIA);
3737b796c40SAdrian Chadd 		if (err)
3747b796c40SAdrian Chadd 			return (err);
3757b796c40SAdrian Chadd 	} else {
3767b796c40SAdrian Chadd 		return (ENXIO);
3777b796c40SAdrian Chadd 	}
3787b796c40SAdrian Chadd 	return (0);
3797b796c40SAdrian Chadd }
3807b796c40SAdrian Chadd 
3817b796c40SAdrian Chadd static int
3827b796c40SAdrian Chadd ukswitch_setport(device_t dev, etherswitch_port_t *p)
3837b796c40SAdrian Chadd {
3847b796c40SAdrian Chadd 	struct ukswitch_softc *sc = device_get_softc(dev);
3857b796c40SAdrian Chadd 	struct ifmedia *ifm;
3867b796c40SAdrian Chadd 	struct mii_data *mii;
3877b796c40SAdrian Chadd 	struct ifnet *ifp;
3887b796c40SAdrian Chadd 	int err;
3897b796c40SAdrian Chadd 
3907b796c40SAdrian Chadd 	if (p->es_port < 0 || p->es_port >= sc->numports)
3917b796c40SAdrian Chadd 		return (ENXIO);
3927b796c40SAdrian Chadd 
3937b796c40SAdrian Chadd 	if (sc->portphy[p->es_port] == sc->cpuport)
3947b796c40SAdrian Chadd 		return (ENXIO);
3957b796c40SAdrian Chadd 
3967b796c40SAdrian Chadd 	mii = ukswitch_miiforport(sc, p->es_port);
3977b796c40SAdrian Chadd 	if (mii == NULL)
3987b796c40SAdrian Chadd 		return (ENXIO);
3997b796c40SAdrian Chadd 
4007b796c40SAdrian Chadd 	ifp = ukswitch_ifpforport(sc, p->es_port);
4017b796c40SAdrian Chadd 
4027b796c40SAdrian Chadd 	ifm = &mii->mii_media;
4037b796c40SAdrian Chadd 	err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
4047b796c40SAdrian Chadd 	return (err);
4057b796c40SAdrian Chadd }
4067b796c40SAdrian Chadd 
4077b796c40SAdrian Chadd static int
4087b796c40SAdrian Chadd ukswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
4097b796c40SAdrian Chadd {
4107b796c40SAdrian Chadd 
4117b796c40SAdrian Chadd 	/* Not supported. */
4127b796c40SAdrian Chadd 	vg->es_vid = 0;
4137b796c40SAdrian Chadd 	vg->es_member_ports = 0;
4147b796c40SAdrian Chadd 	vg->es_untagged_ports = 0;
4157b796c40SAdrian Chadd 	vg->es_fid = 0;
4167b796c40SAdrian Chadd 	return (0);
4177b796c40SAdrian Chadd }
4187b796c40SAdrian Chadd 
4197b796c40SAdrian Chadd static int
4207b796c40SAdrian Chadd ukswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
4217b796c40SAdrian Chadd {
4227b796c40SAdrian Chadd 
4237b796c40SAdrian Chadd 	/* Not supported. */
4247b796c40SAdrian Chadd 	return (0);
4257b796c40SAdrian Chadd }
4267b796c40SAdrian Chadd 
4277b796c40SAdrian Chadd static void
4287b796c40SAdrian Chadd ukswitch_statchg(device_t dev)
4297b796c40SAdrian Chadd {
4307b796c40SAdrian Chadd 
4317b796c40SAdrian Chadd 	DPRINTF(dev, "%s\n", __func__);
4327b796c40SAdrian Chadd }
4337b796c40SAdrian Chadd 
4347b796c40SAdrian Chadd static int
4357b796c40SAdrian Chadd ukswitch_ifmedia_upd(struct ifnet *ifp)
4367b796c40SAdrian Chadd {
4377b796c40SAdrian Chadd 	struct ukswitch_softc *sc = ifp->if_softc;
4387b796c40SAdrian Chadd 	struct mii_data *mii = ukswitch_miiforport(sc, ifp->if_dunit);
4397b796c40SAdrian Chadd 
4407b796c40SAdrian Chadd 	DPRINTF(sc->sc_dev, "%s\n", __func__);
4417b796c40SAdrian Chadd 	if (mii == NULL)
4427b796c40SAdrian Chadd 		return (ENXIO);
4437b796c40SAdrian Chadd 	mii_mediachg(mii);
4447b796c40SAdrian Chadd 	return (0);
4457b796c40SAdrian Chadd }
4467b796c40SAdrian Chadd 
4477b796c40SAdrian Chadd static void
4487b796c40SAdrian Chadd ukswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
4497b796c40SAdrian Chadd {
4507b796c40SAdrian Chadd 	struct ukswitch_softc *sc = ifp->if_softc;
4517b796c40SAdrian Chadd 	struct mii_data *mii = ukswitch_miiforport(sc, ifp->if_dunit);
4527b796c40SAdrian Chadd 
4537b796c40SAdrian Chadd 	DPRINTF(sc->sc_dev, "%s\n", __func__);
4547b796c40SAdrian Chadd 
4557b796c40SAdrian Chadd 	if (mii == NULL)
4567b796c40SAdrian Chadd 		return;
4577b796c40SAdrian Chadd 	mii_pollstat(mii);
4587b796c40SAdrian Chadd 	ifmr->ifm_active = mii->mii_media_active;
4597b796c40SAdrian Chadd 	ifmr->ifm_status = mii->mii_media_status;
4607b796c40SAdrian Chadd }
4617b796c40SAdrian Chadd 
4627b796c40SAdrian Chadd static int
4637b796c40SAdrian Chadd ukswitch_readphy(device_t dev, int phy, int reg)
4647b796c40SAdrian Chadd {
4657b796c40SAdrian Chadd 	struct ukswitch_softc *sc;
4667b796c40SAdrian Chadd 	int data;
4677b796c40SAdrian Chadd 
4687b796c40SAdrian Chadd 	sc = device_get_softc(dev);
4697b796c40SAdrian Chadd 	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
4707b796c40SAdrian Chadd 
4717b796c40SAdrian Chadd 	if (phy < 0 || phy >= 32)
4727b796c40SAdrian Chadd 		return (ENXIO);
4737b796c40SAdrian Chadd 	if (reg < 0 || reg >= 32)
4747b796c40SAdrian Chadd 		return (ENXIO);
4757b796c40SAdrian Chadd 
4767b796c40SAdrian Chadd 	UKSWITCH_LOCK(sc);
4777b796c40SAdrian Chadd 	data = MDIO_READREG(device_get_parent(dev), phy, reg);
4787b796c40SAdrian Chadd 	UKSWITCH_UNLOCK(sc);
4797b796c40SAdrian Chadd 
4807b796c40SAdrian Chadd 	return (data);
4817b796c40SAdrian Chadd }
4827b796c40SAdrian Chadd 
4837b796c40SAdrian Chadd static int
4847b796c40SAdrian Chadd ukswitch_writephy(device_t dev, int phy, int reg, int data)
4857b796c40SAdrian Chadd {
4867b796c40SAdrian Chadd 	struct ukswitch_softc *sc;
4877b796c40SAdrian Chadd 	int err;
4887b796c40SAdrian Chadd 
4897b796c40SAdrian Chadd 	sc = device_get_softc(dev);
4907b796c40SAdrian Chadd 	UKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
4917b796c40SAdrian Chadd 
4927b796c40SAdrian Chadd 	if (phy < 0 || phy >= 32)
4937b796c40SAdrian Chadd 		return (ENXIO);
4947b796c40SAdrian Chadd 	if (reg < 0 || reg >= 32)
4957b796c40SAdrian Chadd 		return (ENXIO);
4967b796c40SAdrian Chadd 
4977b796c40SAdrian Chadd 	UKSWITCH_LOCK(sc);
4987b796c40SAdrian Chadd 	err = MDIO_WRITEREG(device_get_parent(dev), phy, reg, data);
4997b796c40SAdrian Chadd 	UKSWITCH_UNLOCK(sc);
5007b796c40SAdrian Chadd 
5017b796c40SAdrian Chadd 	return (err);
5027b796c40SAdrian Chadd }
5037b796c40SAdrian Chadd 
5047b796c40SAdrian Chadd static int
5057b796c40SAdrian Chadd ukswitch_readreg(device_t dev, int addr)
5067b796c40SAdrian Chadd {
5077b796c40SAdrian Chadd 	struct ukswitch_softc *sc;
5087b796c40SAdrian Chadd 
5097b796c40SAdrian Chadd 	sc = device_get_softc(dev);
5107b796c40SAdrian Chadd 	UKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
5117b796c40SAdrian Chadd 
5127b796c40SAdrian Chadd 	/* Not supported. */
5137b796c40SAdrian Chadd 	return (0);
5147b796c40SAdrian Chadd }
5157b796c40SAdrian Chadd 
5167b796c40SAdrian Chadd static int
5177b796c40SAdrian Chadd ukswitch_writereg(device_t dev, int addr, int value)
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 device_method_t ukswitch_methods[] = {
5297b796c40SAdrian Chadd 	/* Device interface */
5307b796c40SAdrian Chadd 	DEVMETHOD(device_probe,		ukswitch_probe),
5317b796c40SAdrian Chadd 	DEVMETHOD(device_attach,	ukswitch_attach),
5327b796c40SAdrian Chadd 	DEVMETHOD(device_detach,	ukswitch_detach),
5337b796c40SAdrian Chadd 
5347b796c40SAdrian Chadd 	/* bus interface */
5357b796c40SAdrian Chadd 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
5367b796c40SAdrian Chadd 
5377b796c40SAdrian Chadd 	/* MII interface */
5387b796c40SAdrian Chadd 	DEVMETHOD(miibus_readreg,	ukswitch_readphy),
5397b796c40SAdrian Chadd 	DEVMETHOD(miibus_writereg,	ukswitch_writephy),
5407b796c40SAdrian Chadd 	DEVMETHOD(miibus_statchg,	ukswitch_statchg),
5417b796c40SAdrian Chadd 
5427b796c40SAdrian Chadd 	/* MDIO interface */
5437b796c40SAdrian Chadd 	DEVMETHOD(mdio_readreg,		ukswitch_readphy),
5447b796c40SAdrian Chadd 	DEVMETHOD(mdio_writereg,	ukswitch_writephy),
5457b796c40SAdrian Chadd 
5467b796c40SAdrian Chadd 	/* etherswitch interface */
5477b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_lock,	ukswitch_lock),
5487b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_unlock,	ukswitch_unlock),
5497b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_getinfo,	ukswitch_getinfo),
5507b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_readreg,	ukswitch_readreg),
5517b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_writereg,	ukswitch_writereg),
5527b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_readphyreg,	ukswitch_readphy),
5537b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_writephyreg,	ukswitch_writephy),
5547b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_getport,	ukswitch_getport),
5557b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_setport,	ukswitch_setport),
5567b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_getvgroup,	ukswitch_getvgroup),
5577b796c40SAdrian Chadd 	DEVMETHOD(etherswitch_setvgroup,	ukswitch_setvgroup),
5587b796c40SAdrian Chadd 
5597b796c40SAdrian Chadd 	DEVMETHOD_END
5607b796c40SAdrian Chadd };
5617b796c40SAdrian Chadd 
5627b796c40SAdrian Chadd DEFINE_CLASS_0(ukswitch, ukswitch_driver, ukswitch_methods,
5637b796c40SAdrian Chadd     sizeof(struct ukswitch_softc));
5647b796c40SAdrian Chadd static devclass_t ukswitch_devclass;
5657b796c40SAdrian Chadd 
5667b796c40SAdrian Chadd DRIVER_MODULE(ukswitch, mdio, ukswitch_driver, ukswitch_devclass, 0, 0);
5677b796c40SAdrian Chadd DRIVER_MODULE(miibus, ukswitch, miibus_driver, miibus_devclass, 0, 0);
5687b796c40SAdrian Chadd DRIVER_MODULE(mdio, ukswitch, mdio_driver, mdio_devclass, 0, 0);
5697b796c40SAdrian Chadd DRIVER_MODULE(etherswitch, ukswitch, etherswitch_driver, etherswitch_devclass, 0, 0);
5707b796c40SAdrian Chadd MODULE_VERSION(ukswitch, 1);
5717b796c40SAdrian Chadd MODULE_DEPEND(ukswitch, miibus, 1, 1, 1); /* XXX which versions? */
5727b796c40SAdrian Chadd MODULE_DEPEND(ukswitch, etherswitch, 1, 1, 1); /* XXX which versions? */
573