xref: /freebsd/sys/dev/etherswitch/e6000sw/e6060sw.c (revision 95ee2897)
14677ca5bSMichael Zhilin /*-
2bf73b5a2SMichael Zhilin  * Copyright (c) 2016-2017 Hiroki Mori
34677ca5bSMichael Zhilin  * Copyright (c) 2013 Luiz Otavio O Souza.
44677ca5bSMichael Zhilin  * Copyright (c) 2011-2012 Stefan Bethke.
54677ca5bSMichael Zhilin  * Copyright (c) 2012 Adrian Chadd.
64677ca5bSMichael Zhilin  * All rights reserved.
74677ca5bSMichael Zhilin  *
84677ca5bSMichael Zhilin  * Redistribution and use in source and binary forms, with or without
94677ca5bSMichael Zhilin  * modification, are permitted provided that the following conditions
104677ca5bSMichael Zhilin  * are met:
114677ca5bSMichael Zhilin  * 1. Redistributions of source code must retain the above copyright
124677ca5bSMichael Zhilin  *    notice, this list of conditions and the following disclaimer.
134677ca5bSMichael Zhilin  * 2. Redistributions in binary form must reproduce the above copyright
144677ca5bSMichael Zhilin  *    notice, this list of conditions and the following disclaimer in the
154677ca5bSMichael Zhilin  *    documentation and/or other materials provided with the distribution.
164677ca5bSMichael Zhilin  *
174677ca5bSMichael Zhilin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
184677ca5bSMichael Zhilin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
194677ca5bSMichael Zhilin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
204677ca5bSMichael Zhilin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
214677ca5bSMichael Zhilin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
224677ca5bSMichael Zhilin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
234677ca5bSMichael Zhilin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
244677ca5bSMichael Zhilin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
254677ca5bSMichael Zhilin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
264677ca5bSMichael Zhilin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
274677ca5bSMichael Zhilin  * SUCH DAMAGE.
284677ca5bSMichael Zhilin  */
294677ca5bSMichael Zhilin 
304677ca5bSMichael Zhilin /*
314677ca5bSMichael Zhilin  * This code is Marvell 88E6060 ethernet switch support code on etherswitch
324677ca5bSMichael Zhilin  * framework.
33bf73b5a2SMichael Zhilin  * 88E6060 support is only port vlan support. Not support ingress/egress
34bf73b5a2SMichael Zhilin  * trailer.
35bf73b5a2SMichael Zhilin  * 88E6065 support is port and dot1q vlan. Also group base tag support.
364677ca5bSMichael Zhilin  */
374677ca5bSMichael Zhilin 
384677ca5bSMichael Zhilin #include <sys/param.h>
394677ca5bSMichael Zhilin #include <sys/bus.h>
404677ca5bSMichael Zhilin #include <sys/errno.h>
414677ca5bSMichael Zhilin #include <sys/kernel.h>
424677ca5bSMichael Zhilin #include <sys/lock.h>
434677ca5bSMichael Zhilin #include <sys/malloc.h>
444677ca5bSMichael Zhilin #include <sys/module.h>
454677ca5bSMichael Zhilin #include <sys/mutex.h>
464677ca5bSMichael Zhilin #include <sys/socket.h>
474677ca5bSMichael Zhilin #include <sys/sockio.h>
484677ca5bSMichael Zhilin #include <sys/sysctl.h>
494677ca5bSMichael Zhilin #include <sys/systm.h>
504677ca5bSMichael Zhilin 
514677ca5bSMichael Zhilin #include <net/if.h>
524677ca5bSMichael Zhilin #include <net/if_var.h>
534677ca5bSMichael Zhilin #include <net/ethernet.h>
544677ca5bSMichael Zhilin #include <net/if_media.h>
554677ca5bSMichael Zhilin #include <net/if_types.h>
564677ca5bSMichael Zhilin 
574677ca5bSMichael Zhilin #include <machine/bus.h>
584677ca5bSMichael Zhilin #include <dev/mii/mii.h>
594677ca5bSMichael Zhilin #include <dev/mii/miivar.h>
604677ca5bSMichael Zhilin #include <dev/mdio/mdio.h>
614677ca5bSMichael Zhilin 
624677ca5bSMichael Zhilin #include <dev/etherswitch/etherswitch.h>
634677ca5bSMichael Zhilin 
644677ca5bSMichael Zhilin #include "mdio_if.h"
654677ca5bSMichael Zhilin #include "miibus_if.h"
664677ca5bSMichael Zhilin #include "etherswitch_if.h"
674677ca5bSMichael Zhilin 
68bf73b5a2SMichael Zhilin #define	CORE_REGISTER	0x8
694677ca5bSMichael Zhilin #define	SWITCH_ID	3
70bf73b5a2SMichael Zhilin 
71bf73b5a2SMichael Zhilin #define	PORT_CONTROL	4
72bf73b5a2SMichael Zhilin #define	ENGRESSFSHIFT	2
73bf73b5a2SMichael Zhilin #define	ENGRESSFMASK	3
74bf73b5a2SMichael Zhilin #define	ENGRESSTAGSHIFT	12
75bf73b5a2SMichael Zhilin #define	ENGRESSTAGMASK	3
76bf73b5a2SMichael Zhilin 
774677ca5bSMichael Zhilin #define	PORT_VLAN_MAP	6
78bf73b5a2SMichael Zhilin #define	FORCEMAPSHIFT	8
79bf73b5a2SMichael Zhilin #define	FORCEMAPMASK	1
80bf73b5a2SMichael Zhilin 
81bf73b5a2SMichael Zhilin #define	PORT_DEFVLAN	7
82bf73b5a2SMichael Zhilin #define	DEFVIDMASK	0xfff
83bf73b5a2SMichael Zhilin #define	DEFPRIMASK	7
84bf73b5a2SMichael Zhilin 
85bf73b5a2SMichael Zhilin #define	PORT_CONTROL2	8
86bf73b5a2SMichael Zhilin #define	DOT1QMODESHIFT	10
87bf73b5a2SMichael Zhilin #define	DOT1QMODEMASK	3
88bf73b5a2SMichael Zhilin #define	DOT1QNONE	0
89bf73b5a2SMichael Zhilin #define	DOT1QFALLBACK	1
90bf73b5a2SMichael Zhilin #define	DOT1QCHECK	2
91bf73b5a2SMichael Zhilin #define	DOT1QSECURE	3
92bf73b5a2SMichael Zhilin 
93bf73b5a2SMichael Zhilin #define	GLOBAL_REGISTER	0xf
94bf73b5a2SMichael Zhilin 
95bf73b5a2SMichael Zhilin #define	VTU_OPERATION	5
96bf73b5a2SMichael Zhilin #define	VTU_VID_REG	6
97bf73b5a2SMichael Zhilin #define	VTU_DATA1_REG	7
98bf73b5a2SMichael Zhilin #define	VTU_DATA2_REG	8
99bf73b5a2SMichael Zhilin #define	VTU_DATA3_REG	9
100bf73b5a2SMichael Zhilin #define	VTU_BUSY	0x8000
101bf73b5a2SMichael Zhilin #define	VTU_FLASH	1
102bf73b5a2SMichael Zhilin #define	VTU_LOAD_PURGE	3
103bf73b5a2SMichael Zhilin #define	VTU_GET_NEXT	4
104bf73b5a2SMichael Zhilin #define	VTU_VIOLATION	7
1054677ca5bSMichael Zhilin 
1064677ca5bSMichael Zhilin MALLOC_DECLARE(M_E6060SW);
1074677ca5bSMichael Zhilin MALLOC_DEFINE(M_E6060SW, "e6060sw", "e6060sw data structures");
1084677ca5bSMichael Zhilin 
1094677ca5bSMichael Zhilin struct e6060sw_softc {
1104677ca5bSMichael Zhilin 	struct mtx	sc_mtx;		/* serialize access to softc */
1114677ca5bSMichael Zhilin 	device_t	sc_dev;
1124677ca5bSMichael Zhilin 	int		vlan_mode;
1134677ca5bSMichael Zhilin 	int		media;		/* cpu port media */
1144677ca5bSMichael Zhilin 	int		cpuport;	/* which PHY is connected to the CPU */
1154677ca5bSMichael Zhilin 	int		phymask;	/* PHYs we manage */
1164677ca5bSMichael Zhilin 	int		numports;	/* number of ports */
1174677ca5bSMichael Zhilin 	int		ifpport[MII_NPHY];
1184677ca5bSMichael Zhilin 	int		*portphy;
1194677ca5bSMichael Zhilin 	char		**ifname;
1204677ca5bSMichael Zhilin 	device_t	**miibus;
1212e6a8c1aSJustin Hibbits 	if_t *ifp;
1224677ca5bSMichael Zhilin 	struct callout	callout_tick;
1234677ca5bSMichael Zhilin 	etherswitch_info_t	info;
124bf73b5a2SMichael Zhilin 	int		smi_offset;
125bf73b5a2SMichael Zhilin 	int		sw_model;
1264677ca5bSMichael Zhilin };
1274677ca5bSMichael Zhilin 
128bf73b5a2SMichael Zhilin /* Switch Identifier DeviceID */
129bf73b5a2SMichael Zhilin 
130bf73b5a2SMichael Zhilin #define	E6060		0x60
131bf73b5a2SMichael Zhilin #define	E6063		0x63
132bf73b5a2SMichael Zhilin #define	E6065		0x65
133bf73b5a2SMichael Zhilin 
1344677ca5bSMichael Zhilin #define	E6060SW_LOCK(_sc)			\
1354677ca5bSMichael Zhilin 	    mtx_lock(&(_sc)->sc_mtx)
1364677ca5bSMichael Zhilin #define	E6060SW_UNLOCK(_sc)			\
1374677ca5bSMichael Zhilin 	    mtx_unlock(&(_sc)->sc_mtx)
1384677ca5bSMichael Zhilin #define	E6060SW_LOCK_ASSERT(_sc, _what)	\
1394677ca5bSMichael Zhilin 	    mtx_assert(&(_sc)->sc_mtx, (_what))
1404677ca5bSMichael Zhilin #define	E6060SW_TRYLOCK(_sc)			\
1414677ca5bSMichael Zhilin 	    mtx_trylock(&(_sc)->sc_mtx)
1424677ca5bSMichael Zhilin 
1434677ca5bSMichael Zhilin #if defined(DEBUG)
1444677ca5bSMichael Zhilin #define	DPRINTF(dev, args...) device_printf(dev, args)
1454677ca5bSMichael Zhilin #else
1464677ca5bSMichael Zhilin #define	DPRINTF(dev, args...)
1474677ca5bSMichael Zhilin #endif
1484677ca5bSMichael Zhilin 
1494677ca5bSMichael Zhilin static inline int e6060sw_portforphy(struct e6060sw_softc *, int);
1504677ca5bSMichael Zhilin static void e6060sw_tick(void *);
1512e6a8c1aSJustin Hibbits static int e6060sw_ifmedia_upd(if_t);
1522e6a8c1aSJustin Hibbits static void e6060sw_ifmedia_sts(if_t, struct ifmediareq *);
1534677ca5bSMichael Zhilin 
154bf73b5a2SMichael Zhilin static void e6060sw_setup(device_t dev);
155bf73b5a2SMichael Zhilin static int e6060sw_read_vtu(device_t dev, int num, int *data1, int *data2);
156bf73b5a2SMichael Zhilin static void e6060sw_set_vtu(device_t dev, int num, int data1, int data2);
157bf73b5a2SMichael Zhilin 
1584677ca5bSMichael Zhilin static int
e6060sw_probe(device_t dev)1594677ca5bSMichael Zhilin e6060sw_probe(device_t dev)
1604677ca5bSMichael Zhilin {
1614677ca5bSMichael Zhilin 	int data;
1624677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
163bf73b5a2SMichael Zhilin 	int devid, i;
164bf73b5a2SMichael Zhilin 	char *devname;
165bf73b5a2SMichael Zhilin 	char desc[80];
1664677ca5bSMichael Zhilin 
1674677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
1684677ca5bSMichael Zhilin 	bzero(sc, sizeof(*sc));
1694677ca5bSMichael Zhilin 
170034aec85SAdrian Chadd 	devid = 0;
171bf73b5a2SMichael Zhilin 	for (i = 0; i < 2; ++i) {
172bf73b5a2SMichael Zhilin 		data = MDIO_READREG(device_get_parent(dev),
173bf73b5a2SMichael Zhilin 		    CORE_REGISTER + i * 0x10, SWITCH_ID);
1744677ca5bSMichael Zhilin 		if (bootverbose)
175bf73b5a2SMichael Zhilin 			device_printf(dev,"Switch Identifier Register %x\n",
176bf73b5a2SMichael Zhilin 			    data);
1774677ca5bSMichael Zhilin 
178bf73b5a2SMichael Zhilin 		devid = data >> 4;
179bf73b5a2SMichael Zhilin 		if (devid == E6060 ||
180bf73b5a2SMichael Zhilin 		    devid == E6063 || devid == E6065) {
181bf73b5a2SMichael Zhilin 			sc->sw_model = devid;
182bf73b5a2SMichael Zhilin 			sc->smi_offset = i * 0x10;
183bf73b5a2SMichael Zhilin 			break;
1844677ca5bSMichael Zhilin 		}
185bf73b5a2SMichael Zhilin 	}
1864677ca5bSMichael Zhilin 
187bf73b5a2SMichael Zhilin 	if (devid == E6060)
188bf73b5a2SMichael Zhilin 		devname = "88E6060";
189bf73b5a2SMichael Zhilin 	else if (devid == E6063)
190bf73b5a2SMichael Zhilin 		devname = "88E6063";
191bf73b5a2SMichael Zhilin 	else if (devid == E6065)
192bf73b5a2SMichael Zhilin 		devname = "88E6065";
193034aec85SAdrian Chadd 	else
194034aec85SAdrian Chadd 		return (ENXIO);
195034aec85SAdrian Chadd 
196bf73b5a2SMichael Zhilin 	sprintf(desc, "Marvell %s MDIO switch driver at 0x%02x",
197bf73b5a2SMichael Zhilin 	    devname, sc->smi_offset);
198bf73b5a2SMichael Zhilin 	device_set_desc_copy(dev, desc);
199bf73b5a2SMichael Zhilin 
2004677ca5bSMichael Zhilin 	return (BUS_PROBE_DEFAULT);
2014677ca5bSMichael Zhilin }
2024677ca5bSMichael Zhilin 
2034677ca5bSMichael Zhilin static int
e6060sw_attach_phys(struct e6060sw_softc * sc)2044677ca5bSMichael Zhilin e6060sw_attach_phys(struct e6060sw_softc *sc)
2054677ca5bSMichael Zhilin {
2064677ca5bSMichael Zhilin 	int phy, port, err;
2074677ca5bSMichael Zhilin 	char name[IFNAMSIZ];
2084677ca5bSMichael Zhilin 
2094677ca5bSMichael Zhilin 	port = 0;
2104677ca5bSMichael Zhilin 	err = 0;
2114677ca5bSMichael Zhilin 	/* PHYs need an interface, so we generate a dummy one */
2124677ca5bSMichael Zhilin 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
2134677ca5bSMichael Zhilin 	for (phy = 0; phy < sc->numports; phy++) {
2144677ca5bSMichael Zhilin 		if (((1 << phy) & sc->phymask) == 0)
2154677ca5bSMichael Zhilin 			continue;
2164677ca5bSMichael Zhilin 		sc->ifpport[phy] = port;
2174677ca5bSMichael Zhilin 		sc->portphy[port] = phy;
2184677ca5bSMichael Zhilin 		sc->ifp[port] = if_alloc(IFT_ETHER);
2190774131eSMichael Zhilin 		if (sc->ifp[port] == NULL) {
2200774131eSMichael Zhilin 			device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n");
2210774131eSMichael Zhilin 			err = ENOMEM;
2220774131eSMichael Zhilin 			break;
2230774131eSMichael Zhilin 		}
2240774131eSMichael Zhilin 
2254677ca5bSMichael Zhilin 		sc->ifp[port]->if_softc = sc;
2264677ca5bSMichael Zhilin 		sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
2274677ca5bSMichael Zhilin 		    IFF_DRV_RUNNING | IFF_SIMPLEX;
2284677ca5bSMichael Zhilin 		if_initname(sc->ifp[port], name, port);
2294677ca5bSMichael Zhilin 		sc->miibus[port] = malloc(sizeof(device_t), M_E6060SW,
2304677ca5bSMichael Zhilin 		    M_WAITOK | M_ZERO);
2314677ca5bSMichael Zhilin 		err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
2324677ca5bSMichael Zhilin 		    e6060sw_ifmedia_upd, e6060sw_ifmedia_sts, \
233bf73b5a2SMichael Zhilin 		    BMSR_DEFCAPMASK, phy + sc->smi_offset, MII_OFFSET_ANY, 0);
2344677ca5bSMichael Zhilin 		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
2354677ca5bSMichael Zhilin 		    device_get_nameunit(*sc->miibus[port]),
2364677ca5bSMichael Zhilin 		    sc->ifp[port]->if_xname);
2374677ca5bSMichael Zhilin 		if (err != 0) {
2384677ca5bSMichael Zhilin 			device_printf(sc->sc_dev,
2394677ca5bSMichael Zhilin 			    "attaching PHY %d failed\n",
2404677ca5bSMichael Zhilin 			    phy);
2414677ca5bSMichael Zhilin 			break;
2424677ca5bSMichael Zhilin 		}
2434677ca5bSMichael Zhilin 		++port;
2444677ca5bSMichael Zhilin 	}
2454677ca5bSMichael Zhilin 	sc->info.es_nports = port;
2464677ca5bSMichael Zhilin 	if (sc->cpuport != -1) {
2474677ca5bSMichael Zhilin 		/* assume cpuport is last one */
2484677ca5bSMichael Zhilin 		sc->ifpport[sc->cpuport] = port;
2494677ca5bSMichael Zhilin 		sc->portphy[port] = sc->cpuport;
2504677ca5bSMichael Zhilin 		++sc->info.es_nports;
2514677ca5bSMichael Zhilin 	}
2524677ca5bSMichael Zhilin 	return (err);
2534677ca5bSMichael Zhilin }
2544677ca5bSMichael Zhilin 
2554677ca5bSMichael Zhilin static int
e6060sw_attach(device_t dev)2564677ca5bSMichael Zhilin e6060sw_attach(device_t dev)
2574677ca5bSMichael Zhilin {
2584677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
2594677ca5bSMichael Zhilin 	int err;
2604677ca5bSMichael Zhilin 
2614677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
2624677ca5bSMichael Zhilin 	err = 0;
2634677ca5bSMichael Zhilin 
2644677ca5bSMichael Zhilin 	sc->sc_dev = dev;
2654677ca5bSMichael Zhilin 	mtx_init(&sc->sc_mtx, "e6060sw", NULL, MTX_DEF);
2664677ca5bSMichael Zhilin 	strlcpy(sc->info.es_name, device_get_desc(dev),
2674677ca5bSMichael Zhilin 	    sizeof(sc->info.es_name));
2684677ca5bSMichael Zhilin 
2694677ca5bSMichael Zhilin 	/* XXX Defaults */
270bf73b5a2SMichael Zhilin 	if (sc->sw_model == E6063) {
271bf73b5a2SMichael Zhilin 		sc->numports = 3;
272bf73b5a2SMichael Zhilin 		sc->phymask = 0x07;
273bf73b5a2SMichael Zhilin 		sc->cpuport = 2;
274bf73b5a2SMichael Zhilin 	} else {
2754677ca5bSMichael Zhilin 		sc->numports = 6;
2764677ca5bSMichael Zhilin 		sc->phymask = 0x1f;
2774677ca5bSMichael Zhilin 		sc->cpuport = 5;
278bf73b5a2SMichael Zhilin 	}
2794677ca5bSMichael Zhilin 	sc->media = 100;
2804677ca5bSMichael Zhilin 
2814677ca5bSMichael Zhilin 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
2824677ca5bSMichael Zhilin 	    "numports", &sc->numports);
2834677ca5bSMichael Zhilin 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
2844677ca5bSMichael Zhilin 	    "phymask", &sc->phymask);
2854677ca5bSMichael Zhilin 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
2864677ca5bSMichael Zhilin 	    "cpuport", &sc->cpuport);
2874677ca5bSMichael Zhilin 	(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
2884677ca5bSMichael Zhilin 	    "media", &sc->media);
2894677ca5bSMichael Zhilin 
290bf73b5a2SMichael Zhilin 	if (sc->sw_model == E6060) {
2914677ca5bSMichael Zhilin 		sc->info.es_nvlangroups = sc->numports;
2924677ca5bSMichael Zhilin 		sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT;
293bf73b5a2SMichael Zhilin 	} else {
294bf73b5a2SMichael Zhilin 		sc->info.es_nvlangroups = 64;
295bf73b5a2SMichael Zhilin 		sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT |
296bf73b5a2SMichael Zhilin 		    ETHERSWITCH_VLAN_DOT1Q;
297bf73b5a2SMichael Zhilin 	}
298bf73b5a2SMichael Zhilin 
299bf73b5a2SMichael Zhilin 	e6060sw_setup(dev);
3004677ca5bSMichael Zhilin 
3012e6a8c1aSJustin Hibbits 	sc->ifp = malloc(sizeof(if_t) * sc->numports, M_E6060SW,
3024677ca5bSMichael Zhilin 	    M_WAITOK | M_ZERO);
3034677ca5bSMichael Zhilin 	sc->ifname = malloc(sizeof(char *) * sc->numports, M_E6060SW,
3044677ca5bSMichael Zhilin 	    M_WAITOK | M_ZERO);
3054677ca5bSMichael Zhilin 	sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_E6060SW,
3064677ca5bSMichael Zhilin 	    M_WAITOK | M_ZERO);
3074677ca5bSMichael Zhilin 	sc->portphy = malloc(sizeof(int) * sc->numports, M_E6060SW,
3084677ca5bSMichael Zhilin 	    M_WAITOK | M_ZERO);
3094677ca5bSMichael Zhilin 
3104677ca5bSMichael Zhilin 	/*
3114677ca5bSMichael Zhilin 	 * Attach the PHYs and complete the bus enumeration.
3124677ca5bSMichael Zhilin 	 */
3134677ca5bSMichael Zhilin 	err = e6060sw_attach_phys(sc);
3144677ca5bSMichael Zhilin 	if (err != 0)
3154677ca5bSMichael Zhilin 		return (err);
3164677ca5bSMichael Zhilin 
3174677ca5bSMichael Zhilin 	bus_generic_probe(dev);
3184677ca5bSMichael Zhilin 	bus_enumerate_hinted_children(dev);
3194677ca5bSMichael Zhilin 	err = bus_generic_attach(dev);
3204677ca5bSMichael Zhilin 	if (err != 0)
3214677ca5bSMichael Zhilin 		return (err);
3224677ca5bSMichael Zhilin 
3234677ca5bSMichael Zhilin 	callout_init(&sc->callout_tick, 0);
3244677ca5bSMichael Zhilin 
3254677ca5bSMichael Zhilin 	e6060sw_tick(sc);
3264677ca5bSMichael Zhilin 
3274677ca5bSMichael Zhilin 	return (err);
3284677ca5bSMichael Zhilin }
3294677ca5bSMichael Zhilin 
3304677ca5bSMichael Zhilin static int
e6060sw_detach(device_t dev)3314677ca5bSMichael Zhilin e6060sw_detach(device_t dev)
3324677ca5bSMichael Zhilin {
3334677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
3344677ca5bSMichael Zhilin 	int i, port;
3354677ca5bSMichael Zhilin 
3364677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
3374677ca5bSMichael Zhilin 
3384677ca5bSMichael Zhilin 	callout_drain(&sc->callout_tick);
3394677ca5bSMichael Zhilin 
3404677ca5bSMichael Zhilin 	for (i = 0; i < MII_NPHY; i++) {
3414677ca5bSMichael Zhilin 		if (((1 << i) & sc->phymask) == 0)
3424677ca5bSMichael Zhilin 			continue;
3434677ca5bSMichael Zhilin 		port = e6060sw_portforphy(sc, i);
3444677ca5bSMichael Zhilin 		if (sc->miibus[port] != NULL)
3454677ca5bSMichael Zhilin 			device_delete_child(dev, (*sc->miibus[port]));
3464677ca5bSMichael Zhilin 		if (sc->ifp[port] != NULL)
3474677ca5bSMichael Zhilin 			if_free(sc->ifp[port]);
3484677ca5bSMichael Zhilin 		free(sc->ifname[port], M_E6060SW);
3494677ca5bSMichael Zhilin 		free(sc->miibus[port], M_E6060SW);
3504677ca5bSMichael Zhilin 	}
3514677ca5bSMichael Zhilin 
3524677ca5bSMichael Zhilin 	free(sc->portphy, M_E6060SW);
3534677ca5bSMichael Zhilin 	free(sc->miibus, M_E6060SW);
3544677ca5bSMichael Zhilin 	free(sc->ifname, M_E6060SW);
3554677ca5bSMichael Zhilin 	free(sc->ifp, M_E6060SW);
3564677ca5bSMichael Zhilin 
3574677ca5bSMichael Zhilin 	bus_generic_detach(dev);
3584677ca5bSMichael Zhilin 	mtx_destroy(&sc->sc_mtx);
3594677ca5bSMichael Zhilin 
3604677ca5bSMichael Zhilin 	return (0);
3614677ca5bSMichael Zhilin }
3624677ca5bSMichael Zhilin 
3634677ca5bSMichael Zhilin /*
3644677ca5bSMichael Zhilin  * Convert PHY number to port number.
3654677ca5bSMichael Zhilin  */
3664677ca5bSMichael Zhilin static inline int
e6060sw_portforphy(struct e6060sw_softc * sc,int phy)3674677ca5bSMichael Zhilin e6060sw_portforphy(struct e6060sw_softc *sc, int phy)
3684677ca5bSMichael Zhilin {
3694677ca5bSMichael Zhilin 
3704677ca5bSMichael Zhilin 	return (sc->ifpport[phy]);
3714677ca5bSMichael Zhilin }
3724677ca5bSMichael Zhilin 
3734677ca5bSMichael Zhilin static inline struct mii_data *
e6060sw_miiforport(struct e6060sw_softc * sc,int port)3744677ca5bSMichael Zhilin e6060sw_miiforport(struct e6060sw_softc *sc, int port)
3754677ca5bSMichael Zhilin {
3764677ca5bSMichael Zhilin 
3774677ca5bSMichael Zhilin 	if (port < 0 || port > sc->numports)
3784677ca5bSMichael Zhilin 		return (NULL);
3794677ca5bSMichael Zhilin 	if (port == sc->cpuport)
3804677ca5bSMichael Zhilin 		return (NULL);
3814677ca5bSMichael Zhilin 	return (device_get_softc(*sc->miibus[port]));
3824677ca5bSMichael Zhilin }
3834677ca5bSMichael Zhilin 
3842e6a8c1aSJustin Hibbits static inline if_t
e6060sw_ifpforport(struct e6060sw_softc * sc,int port)3854677ca5bSMichael Zhilin e6060sw_ifpforport(struct e6060sw_softc *sc, int port)
3864677ca5bSMichael Zhilin {
3874677ca5bSMichael Zhilin 
3884677ca5bSMichael Zhilin 	if (port < 0 || port > sc->numports)
3894677ca5bSMichael Zhilin 		return (NULL);
3904677ca5bSMichael Zhilin 	return (sc->ifp[port]);
3914677ca5bSMichael Zhilin }
3924677ca5bSMichael Zhilin 
3934677ca5bSMichael Zhilin /*
3944677ca5bSMichael Zhilin  * Poll the status for all PHYs.
3954677ca5bSMichael Zhilin  */
3964677ca5bSMichael Zhilin static void
e6060sw_miipollstat(struct e6060sw_softc * sc)3974677ca5bSMichael Zhilin e6060sw_miipollstat(struct e6060sw_softc *sc)
3984677ca5bSMichael Zhilin {
3994677ca5bSMichael Zhilin 	int i, port;
4004677ca5bSMichael Zhilin 	struct mii_data *mii;
4014677ca5bSMichael Zhilin 	struct mii_softc *miisc;
4024677ca5bSMichael Zhilin 
4034677ca5bSMichael Zhilin 	E6060SW_LOCK_ASSERT(sc, MA_NOTOWNED);
4044677ca5bSMichael Zhilin 
4054677ca5bSMichael Zhilin 	for (i = 0; i < MII_NPHY; i++) {
4064677ca5bSMichael Zhilin 		if (((1 << i) & sc->phymask) == 0)
4074677ca5bSMichael Zhilin 			continue;
4084677ca5bSMichael Zhilin 		port = e6060sw_portforphy(sc, i);
4094677ca5bSMichael Zhilin 		if ((*sc->miibus[port]) == NULL)
4104677ca5bSMichael Zhilin 			continue;
4114677ca5bSMichael Zhilin 		mii = device_get_softc(*sc->miibus[port]);
4124677ca5bSMichael Zhilin 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
4134677ca5bSMichael Zhilin 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
4144677ca5bSMichael Zhilin 			    miisc->mii_inst)
4154677ca5bSMichael Zhilin 				continue;
4164677ca5bSMichael Zhilin 			ukphy_status(miisc);
4174677ca5bSMichael Zhilin 			mii_phy_update(miisc, MII_POLLSTAT);
4184677ca5bSMichael Zhilin 		}
4194677ca5bSMichael Zhilin 	}
4204677ca5bSMichael Zhilin }
4214677ca5bSMichael Zhilin 
4224677ca5bSMichael Zhilin static void
e6060sw_tick(void * arg)4234677ca5bSMichael Zhilin e6060sw_tick(void *arg)
4244677ca5bSMichael Zhilin {
4254677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
4264677ca5bSMichael Zhilin 
4274677ca5bSMichael Zhilin 	sc = arg;
4284677ca5bSMichael Zhilin 
4294677ca5bSMichael Zhilin 	e6060sw_miipollstat(sc);
4304677ca5bSMichael Zhilin 	callout_reset(&sc->callout_tick, hz, e6060sw_tick, sc);
4314677ca5bSMichael Zhilin }
4324677ca5bSMichael Zhilin 
4334677ca5bSMichael Zhilin static void
e6060sw_lock(device_t dev)4344677ca5bSMichael Zhilin e6060sw_lock(device_t dev)
4354677ca5bSMichael Zhilin {
4364677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
4374677ca5bSMichael Zhilin 
4384677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
4394677ca5bSMichael Zhilin 
4404677ca5bSMichael Zhilin 	E6060SW_LOCK_ASSERT(sc, MA_NOTOWNED);
4414677ca5bSMichael Zhilin 	E6060SW_LOCK(sc);
4424677ca5bSMichael Zhilin }
4434677ca5bSMichael Zhilin 
4444677ca5bSMichael Zhilin static void
e6060sw_unlock(device_t dev)4454677ca5bSMichael Zhilin e6060sw_unlock(device_t dev)
4464677ca5bSMichael Zhilin {
4474677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
4484677ca5bSMichael Zhilin 
4494677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
4504677ca5bSMichael Zhilin 
4514677ca5bSMichael Zhilin 	E6060SW_LOCK_ASSERT(sc, MA_OWNED);
4524677ca5bSMichael Zhilin 	E6060SW_UNLOCK(sc);
4534677ca5bSMichael Zhilin }
4544677ca5bSMichael Zhilin 
4554677ca5bSMichael Zhilin static etherswitch_info_t *
e6060sw_getinfo(device_t dev)4564677ca5bSMichael Zhilin e6060sw_getinfo(device_t dev)
4574677ca5bSMichael Zhilin {
4584677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
4594677ca5bSMichael Zhilin 
4604677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
4614677ca5bSMichael Zhilin 
4624677ca5bSMichael Zhilin 	return (&sc->info);
4634677ca5bSMichael Zhilin }
4644677ca5bSMichael Zhilin 
4654677ca5bSMichael Zhilin static int
e6060sw_getport(device_t dev,etherswitch_port_t * p)4664677ca5bSMichael Zhilin e6060sw_getport(device_t dev, etherswitch_port_t *p)
4674677ca5bSMichael Zhilin {
4684677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
4694677ca5bSMichael Zhilin 	struct mii_data *mii;
4704677ca5bSMichael Zhilin 	struct ifmediareq *ifmr;
4714677ca5bSMichael Zhilin 	int err, phy;
4724677ca5bSMichael Zhilin 
4734677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
4744677ca5bSMichael Zhilin 	ifmr = &p->es_ifmr;
4754677ca5bSMichael Zhilin 
4764677ca5bSMichael Zhilin 	if (p->es_port < 0 || p->es_port >= sc->numports)
4774677ca5bSMichael Zhilin 		return (ENXIO);
478bf73b5a2SMichael Zhilin 
4794677ca5bSMichael Zhilin 	p->es_pvid = 0;
480bf73b5a2SMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
481bf73b5a2SMichael Zhilin 		p->es_pvid = MDIO_READREG(device_get_parent(dev),
482bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + p->es_port,
483bf73b5a2SMichael Zhilin 		    PORT_DEFVLAN) & 0xfff;
484bf73b5a2SMichael Zhilin 	}
4854677ca5bSMichael Zhilin 
4864677ca5bSMichael Zhilin 	phy = sc->portphy[p->es_port];
4874677ca5bSMichael Zhilin 	mii = e6060sw_miiforport(sc, p->es_port);
4884677ca5bSMichael Zhilin 	if (sc->cpuport != -1 && phy == sc->cpuport) {
4894677ca5bSMichael Zhilin 		/* fill in fixed values for CPU port */
4904677ca5bSMichael Zhilin 		p->es_flags |= ETHERSWITCH_PORT_CPU;
4914677ca5bSMichael Zhilin 		ifmr->ifm_count = 0;
4924677ca5bSMichael Zhilin 		if (sc->media == 100)
4934677ca5bSMichael Zhilin 			ifmr->ifm_current = ifmr->ifm_active =
4944677ca5bSMichael Zhilin 			    IFM_ETHER | IFM_100_TX | IFM_FDX;
4954677ca5bSMichael Zhilin 		else
4964677ca5bSMichael Zhilin 			ifmr->ifm_current = ifmr->ifm_active =
4974677ca5bSMichael Zhilin 			    IFM_ETHER | IFM_1000_T | IFM_FDX;
4984677ca5bSMichael Zhilin 		ifmr->ifm_mask = 0;
4994677ca5bSMichael Zhilin 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
5004677ca5bSMichael Zhilin 	} else if (mii != NULL) {
5014677ca5bSMichael Zhilin 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
5024677ca5bSMichael Zhilin 		    &mii->mii_media, SIOCGIFMEDIA);
5034677ca5bSMichael Zhilin 		if (err)
5044677ca5bSMichael Zhilin 			return (err);
5054677ca5bSMichael Zhilin 	} else {
5064677ca5bSMichael Zhilin 		return (ENXIO);
5074677ca5bSMichael Zhilin 	}
5084677ca5bSMichael Zhilin 	return (0);
5094677ca5bSMichael Zhilin }
5104677ca5bSMichael Zhilin 
5114677ca5bSMichael Zhilin static int
e6060sw_setport(device_t dev,etherswitch_port_t * p)5124677ca5bSMichael Zhilin e6060sw_setport(device_t dev, etherswitch_port_t *p)
5134677ca5bSMichael Zhilin {
5144677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
5154677ca5bSMichael Zhilin 	struct ifmedia *ifm;
5164677ca5bSMichael Zhilin 	struct mii_data *mii;
5172e6a8c1aSJustin Hibbits 	if_t ifp;
5184677ca5bSMichael Zhilin 	int err;
519bf73b5a2SMichael Zhilin 	int data;
5204677ca5bSMichael Zhilin 
5214677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
5224677ca5bSMichael Zhilin 
5234677ca5bSMichael Zhilin 	if (p->es_port < 0 || p->es_port >= sc->numports)
5244677ca5bSMichael Zhilin 		return (ENXIO);
5254677ca5bSMichael Zhilin 
526bf73b5a2SMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
527bf73b5a2SMichael Zhilin 		data = MDIO_READREG(device_get_parent(dev),
528bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + p->es_port,
529bf73b5a2SMichael Zhilin 		    PORT_DEFVLAN);
530bf73b5a2SMichael Zhilin 		data &= ~0xfff;
531bf73b5a2SMichael Zhilin 		data |= p->es_pvid;
532bf73b5a2SMichael Zhilin 		data |= 1 << 12;
533bf73b5a2SMichael Zhilin 		MDIO_WRITEREG(device_get_parent(dev),
534bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + p->es_port,
535bf73b5a2SMichael Zhilin 		    PORT_DEFVLAN, data);
536bf73b5a2SMichael Zhilin 	}
537bf73b5a2SMichael Zhilin 
5384677ca5bSMichael Zhilin 	if (sc->portphy[p->es_port] == sc->cpuport)
539bf73b5a2SMichael Zhilin 		return(0);
5404677ca5bSMichael Zhilin 
5414677ca5bSMichael Zhilin 	mii = e6060sw_miiforport(sc, p->es_port);
5424677ca5bSMichael Zhilin 	if (mii == NULL)
5434677ca5bSMichael Zhilin 		return (ENXIO);
5444677ca5bSMichael Zhilin 
5454677ca5bSMichael Zhilin 	ifp = e6060sw_ifpforport(sc, p->es_port);
5464677ca5bSMichael Zhilin 
5474677ca5bSMichael Zhilin 	ifm = &mii->mii_media;
5484677ca5bSMichael Zhilin 	err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
5494677ca5bSMichael Zhilin 	return (err);
5504677ca5bSMichael Zhilin }
5514677ca5bSMichael Zhilin 
5524677ca5bSMichael Zhilin static int
e6060sw_getvgroup(device_t dev,etherswitch_vlangroup_t * vg)5534677ca5bSMichael Zhilin e6060sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
5544677ca5bSMichael Zhilin {
5554677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
556bf73b5a2SMichael Zhilin 	int data1, data2;
557bf73b5a2SMichael Zhilin 	int vid;
558bf73b5a2SMichael Zhilin 	int i, tag;
5594677ca5bSMichael Zhilin 
5604677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
5614677ca5bSMichael Zhilin 
5624677ca5bSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
5634677ca5bSMichael Zhilin 		vg->es_vid = ETHERSWITCH_VID_VALID;
5644677ca5bSMichael Zhilin 		vg->es_vid |= vg->es_vlangroup;
565bf73b5a2SMichael Zhilin 		data1 = MDIO_READREG(device_get_parent(dev),
566bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + vg->es_vlangroup,
567bf73b5a2SMichael Zhilin 		    PORT_VLAN_MAP);
568bf73b5a2SMichael Zhilin 		vg->es_member_ports = data1 & 0x3f;
5694677ca5bSMichael Zhilin 		vg->es_untagged_ports = vg->es_member_ports;
5704677ca5bSMichael Zhilin 		vg->es_fid = 0;
571bf73b5a2SMichael Zhilin 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
572bf73b5a2SMichael Zhilin 		if (vg->es_vlangroup == 0)
573bf73b5a2SMichael Zhilin 			return (0);
574bf73b5a2SMichael Zhilin 		vid = e6060sw_read_vtu(dev, vg->es_vlangroup, &data1, &data2);
575bf73b5a2SMichael Zhilin 		if (vid > 0) {
576bf73b5a2SMichael Zhilin 			vg->es_vid = ETHERSWITCH_VID_VALID;
577bf73b5a2SMichael Zhilin 			vg->es_vid |= vid;
578bf73b5a2SMichael Zhilin 			vg->es_member_ports = 0;
579bf73b5a2SMichael Zhilin 			vg->es_untagged_ports = 0;
580bf73b5a2SMichael Zhilin 			for (i = 0; i < 4; ++i) {
581bf73b5a2SMichael Zhilin 				tag = data1 >> (i * 4) & 3;
582bf73b5a2SMichael Zhilin 				if (tag == 0 || tag == 1) {
583bf73b5a2SMichael Zhilin 					vg->es_member_ports |= 1 << i;
584bf73b5a2SMichael Zhilin 					vg->es_untagged_ports |= 1 << i;
585bf73b5a2SMichael Zhilin 				} else if (tag == 2) {
586bf73b5a2SMichael Zhilin 					vg->es_member_ports |= 1 << i;
587bf73b5a2SMichael Zhilin 				}
588bf73b5a2SMichael Zhilin 			}
589bf73b5a2SMichael Zhilin 			for (i = 0; i < 2; ++i) {
590bf73b5a2SMichael Zhilin 				tag = data2 >> (i * 4) & 3;
591bf73b5a2SMichael Zhilin 				if (tag == 0 || tag == 1) {
592bf73b5a2SMichael Zhilin 					vg->es_member_ports |= 1 << (i + 4);
593bf73b5a2SMichael Zhilin 					vg->es_untagged_ports |= 1 << (i + 4);
594bf73b5a2SMichael Zhilin 				} else if (tag == 2) {
595bf73b5a2SMichael Zhilin 					vg->es_member_ports |= 1 << (i + 4);
596bf73b5a2SMichael Zhilin 				}
597bf73b5a2SMichael Zhilin 			}
598bf73b5a2SMichael Zhilin 
599bf73b5a2SMichael Zhilin 		}
6004677ca5bSMichael Zhilin 	} else {
6014677ca5bSMichael Zhilin 		vg->es_vid = 0;
6024677ca5bSMichael Zhilin 	}
6034677ca5bSMichael Zhilin 	return (0);
6044677ca5bSMichael Zhilin }
6054677ca5bSMichael Zhilin 
6064677ca5bSMichael Zhilin static int
e6060sw_setvgroup(device_t dev,etherswitch_vlangroup_t * vg)6074677ca5bSMichael Zhilin e6060sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
6084677ca5bSMichael Zhilin {
6094677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
610bf73b5a2SMichael Zhilin 	int data1, data2;
611bf73b5a2SMichael Zhilin 	int i;
6124677ca5bSMichael Zhilin 
6134677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
6144677ca5bSMichael Zhilin 
6154677ca5bSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
616bf73b5a2SMichael Zhilin 		data1 = MDIO_READREG(device_get_parent(dev),
617bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + vg->es_vlangroup,
618bf73b5a2SMichael Zhilin 		    PORT_VLAN_MAP);
619bf73b5a2SMichael Zhilin 		data1 &= ~0x3f;
620bf73b5a2SMichael Zhilin 		data1 |= vg->es_member_ports;
621bf73b5a2SMichael Zhilin 		MDIO_WRITEREG(device_get_parent(dev),
622bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + vg->es_vlangroup,
623bf73b5a2SMichael Zhilin 		    PORT_VLAN_MAP, data1);
624bf73b5a2SMichael Zhilin 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
625bf73b5a2SMichael Zhilin 		if (vg->es_vlangroup == 0)
626bf73b5a2SMichael Zhilin 			return (0);
627bf73b5a2SMichael Zhilin 		data1 = 0;
628bf73b5a2SMichael Zhilin 		data2 = 0;
629bf73b5a2SMichael Zhilin 		for (i = 0; i < 6; ++i) {
630bf73b5a2SMichael Zhilin 			if (vg->es_member_ports &
631bf73b5a2SMichael Zhilin 			    vg->es_untagged_ports & (1 << i)) {
632bf73b5a2SMichael Zhilin 				if (i < 4) {
633bf73b5a2SMichael Zhilin 					data1 |= (0xd << i * 4);
634bf73b5a2SMichael Zhilin 				} else {
635bf73b5a2SMichael Zhilin 					data2 |= (0xd << (i - 4) * 4);
6364677ca5bSMichael Zhilin 				}
637bf73b5a2SMichael Zhilin 			} else if (vg->es_member_ports & (1 << i)) {
638bf73b5a2SMichael Zhilin 				if (i < 4) {
639bf73b5a2SMichael Zhilin 					data1 |= (0xe << i * 4);
640bf73b5a2SMichael Zhilin 				} else {
641bf73b5a2SMichael Zhilin 					data2 |= (0xe << (i - 4) * 4);
642bf73b5a2SMichael Zhilin 				}
643bf73b5a2SMichael Zhilin 			} else {
644bf73b5a2SMichael Zhilin 				if (i < 4) {
645bf73b5a2SMichael Zhilin 					data1 |= (0x3 << i * 4);
646bf73b5a2SMichael Zhilin 				} else {
647bf73b5a2SMichael Zhilin 					data2 |= (0x3 << (i - 4) * 4);
648bf73b5a2SMichael Zhilin 				}
649bf73b5a2SMichael Zhilin 			}
650bf73b5a2SMichael Zhilin 		}
651bf73b5a2SMichael Zhilin 		e6060sw_set_vtu(dev, vg->es_vlangroup, data1, data2);
652bf73b5a2SMichael Zhilin 	}
6534677ca5bSMichael Zhilin 	return (0);
6544677ca5bSMichael Zhilin }
6554677ca5bSMichael Zhilin 
6564677ca5bSMichael Zhilin static void
e6060sw_reset_vlans(device_t dev)6574677ca5bSMichael Zhilin e6060sw_reset_vlans(device_t dev)
6584677ca5bSMichael Zhilin {
6594677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
6604677ca5bSMichael Zhilin 	uint32_t ports;
6614677ca5bSMichael Zhilin 	int i;
6624677ca5bSMichael Zhilin 	int data;
6634677ca5bSMichael Zhilin 
6644677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
6654677ca5bSMichael Zhilin 
6664677ca5bSMichael Zhilin 	for (i = 0; i <= sc->numports; i++) {
6674677ca5bSMichael Zhilin 		ports = (1 << (sc->numports + 1)) - 1;
6684677ca5bSMichael Zhilin 		ports &= ~(1 << i);
6694677ca5bSMichael Zhilin 		if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
6704677ca5bSMichael Zhilin 			data = i << 12;
671bf73b5a2SMichael Zhilin 		} else if (sc->vlan_mode == 0) {
672bf73b5a2SMichael Zhilin 			data = 1 << 8;
6734677ca5bSMichael Zhilin 		} else {
6744677ca5bSMichael Zhilin 			data = 0;
6754677ca5bSMichael Zhilin 		}
6764677ca5bSMichael Zhilin 		data |= ports;
677bf73b5a2SMichael Zhilin 		MDIO_WRITEREG(device_get_parent(dev),
678bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + i, PORT_VLAN_MAP, data);
679bf73b5a2SMichael Zhilin 	}
680bf73b5a2SMichael Zhilin }
681bf73b5a2SMichael Zhilin 
682bf73b5a2SMichael Zhilin static void
e6060sw_setup(device_t dev)683bf73b5a2SMichael Zhilin e6060sw_setup(device_t dev)
684bf73b5a2SMichael Zhilin {
685bf73b5a2SMichael Zhilin 	struct e6060sw_softc *sc;
686bf73b5a2SMichael Zhilin 	int i;
687bf73b5a2SMichael Zhilin 	int data;
688bf73b5a2SMichael Zhilin 
689bf73b5a2SMichael Zhilin 	sc = device_get_softc(dev);
690bf73b5a2SMichael Zhilin 
691bf73b5a2SMichael Zhilin 	for (i = 0; i <= sc->numports; i++) {
692bf73b5a2SMichael Zhilin 		if (sc->sw_model == E6063 || sc->sw_model == E6065) {
693bf73b5a2SMichael Zhilin 			data = MDIO_READREG(device_get_parent(dev),
694bf73b5a2SMichael Zhilin 			    CORE_REGISTER + sc->smi_offset + i, PORT_VLAN_MAP);
695bf73b5a2SMichael Zhilin 			data &= ~(FORCEMAPMASK << FORCEMAPSHIFT);
696bf73b5a2SMichael Zhilin 			MDIO_WRITEREG(device_get_parent(dev),
697bf73b5a2SMichael Zhilin 			    CORE_REGISTER + sc->smi_offset + i,
698bf73b5a2SMichael Zhilin 			    PORT_VLAN_MAP, data);
699bf73b5a2SMichael Zhilin 
700bf73b5a2SMichael Zhilin 			data = MDIO_READREG(device_get_parent(dev),
701bf73b5a2SMichael Zhilin 			    CORE_REGISTER + sc->smi_offset + i, PORT_CONTROL);
702bf73b5a2SMichael Zhilin 			data |= 3 << ENGRESSFSHIFT;
703bf73b5a2SMichael Zhilin 			MDIO_WRITEREG(device_get_parent(dev),
704bf73b5a2SMichael Zhilin 			    CORE_REGISTER + sc->smi_offset + i,
705bf73b5a2SMichael Zhilin 			    PORT_CONTROL, data);
706bf73b5a2SMichael Zhilin 		}
707bf73b5a2SMichael Zhilin 	}
708bf73b5a2SMichael Zhilin }
709bf73b5a2SMichael Zhilin 
710bf73b5a2SMichael Zhilin static void
e6060sw_dot1q_mode(device_t dev,int mode)711bf73b5a2SMichael Zhilin e6060sw_dot1q_mode(device_t dev, int mode)
712bf73b5a2SMichael Zhilin {
713bf73b5a2SMichael Zhilin 	struct e6060sw_softc *sc;
714bf73b5a2SMichael Zhilin 	int i;
715bf73b5a2SMichael Zhilin 	int data;
716bf73b5a2SMichael Zhilin 
717bf73b5a2SMichael Zhilin 	sc = device_get_softc(dev);
718bf73b5a2SMichael Zhilin 
719bf73b5a2SMichael Zhilin 	for (i = 0; i <= sc->numports; i++) {
720bf73b5a2SMichael Zhilin 		data = MDIO_READREG(device_get_parent(dev),
721bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + i, PORT_CONTROL2);
722bf73b5a2SMichael Zhilin 		data &= ~(DOT1QMODEMASK << DOT1QMODESHIFT);
723bf73b5a2SMichael Zhilin 		data |= mode << DOT1QMODESHIFT;
724bf73b5a2SMichael Zhilin 		MDIO_WRITEREG(device_get_parent(dev),
725bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + i, PORT_CONTROL2, data);
726bf73b5a2SMichael Zhilin 
727bf73b5a2SMichael Zhilin 		data = MDIO_READREG(device_get_parent(dev),
728bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + i,
729bf73b5a2SMichael Zhilin 		    PORT_DEFVLAN);
730bf73b5a2SMichael Zhilin 		data &= ~0xfff;
731bf73b5a2SMichael Zhilin 		data |= 1;
732bf73b5a2SMichael Zhilin 		MDIO_WRITEREG(device_get_parent(dev),
733bf73b5a2SMichael Zhilin 		    CORE_REGISTER + sc->smi_offset + i,
734bf73b5a2SMichael Zhilin 		    PORT_DEFVLAN, data);
7354677ca5bSMichael Zhilin 	}
7364677ca5bSMichael Zhilin }
7374677ca5bSMichael Zhilin 
7384677ca5bSMichael Zhilin static int
e6060sw_getconf(device_t dev,etherswitch_conf_t * conf)7394677ca5bSMichael Zhilin e6060sw_getconf(device_t dev, etherswitch_conf_t *conf)
7404677ca5bSMichael Zhilin {
7414677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
7424677ca5bSMichael Zhilin 
7434677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
7444677ca5bSMichael Zhilin 
7454677ca5bSMichael Zhilin 	/* Return the VLAN mode. */
7464677ca5bSMichael Zhilin 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
7474677ca5bSMichael Zhilin 	conf->vlan_mode = sc->vlan_mode;
7484677ca5bSMichael Zhilin 
7494677ca5bSMichael Zhilin 	return (0);
7504677ca5bSMichael Zhilin }
7514677ca5bSMichael Zhilin 
752bf73b5a2SMichael Zhilin static void
e6060sw_init_vtu(device_t dev)753bf73b5a2SMichael Zhilin e6060sw_init_vtu(device_t dev)
754bf73b5a2SMichael Zhilin {
755bf73b5a2SMichael Zhilin 	struct e6060sw_softc *sc;
756bf73b5a2SMichael Zhilin 	int busy;
757bf73b5a2SMichael Zhilin 
758bf73b5a2SMichael Zhilin 	sc = device_get_softc(dev);
759bf73b5a2SMichael Zhilin 
760bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
761bf73b5a2SMichael Zhilin 	    VTU_OPERATION, VTU_BUSY | (VTU_FLASH << 12));
762bf73b5a2SMichael Zhilin 	while (1) {
763bf73b5a2SMichael Zhilin 		busy = MDIO_READREG(device_get_parent(dev),
764bf73b5a2SMichael Zhilin 		    GLOBAL_REGISTER + sc->smi_offset, VTU_OPERATION);
765bf73b5a2SMichael Zhilin 		if ((busy & VTU_BUSY) == 0)
766bf73b5a2SMichael Zhilin 			break;
767bf73b5a2SMichael Zhilin 	}
768bf73b5a2SMichael Zhilin 
769bf73b5a2SMichael Zhilin 	/* initial member set at vlan 1*/
770bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
771bf73b5a2SMichael Zhilin 	    VTU_DATA1_REG, 0xcccc);
772bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
773bf73b5a2SMichael Zhilin 	    VTU_DATA2_REG, 0x00cc);
774bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
775bf73b5a2SMichael Zhilin 	    VTU_VID_REG, 0x1000 | 1);
776bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
777bf73b5a2SMichael Zhilin 	    VTU_OPERATION, VTU_BUSY | (VTU_LOAD_PURGE << 12) | 1);
778bf73b5a2SMichael Zhilin 	while (1) {
779bf73b5a2SMichael Zhilin 		busy = MDIO_READREG(device_get_parent(dev),
780bf73b5a2SMichael Zhilin 		    GLOBAL_REGISTER + sc->smi_offset, VTU_OPERATION);
781bf73b5a2SMichael Zhilin 		if ((busy & VTU_BUSY) == 0)
782bf73b5a2SMichael Zhilin 			break;
783bf73b5a2SMichael Zhilin 	}
784bf73b5a2SMichael Zhilin }
785bf73b5a2SMichael Zhilin 
786bf73b5a2SMichael Zhilin static void
e6060sw_set_vtu(device_t dev,int num,int data1,int data2)787bf73b5a2SMichael Zhilin e6060sw_set_vtu(device_t dev, int num, int data1, int data2)
788bf73b5a2SMichael Zhilin {
789bf73b5a2SMichael Zhilin 	struct e6060sw_softc *sc;
790bf73b5a2SMichael Zhilin 	int busy;
791bf73b5a2SMichael Zhilin 
792bf73b5a2SMichael Zhilin 	sc = device_get_softc(dev);
793bf73b5a2SMichael Zhilin 
794bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
795bf73b5a2SMichael Zhilin 	    VTU_DATA1_REG, data1);
796bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
797bf73b5a2SMichael Zhilin 	    VTU_DATA2_REG, data2);
798bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
799bf73b5a2SMichael Zhilin 	    VTU_VID_REG, 0x1000 | num);
800bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
801bf73b5a2SMichael Zhilin 	    VTU_OPERATION, VTU_BUSY | (VTU_LOAD_PURGE << 12) | num);
802bf73b5a2SMichael Zhilin 	while (1) {
803bf73b5a2SMichael Zhilin 		busy = MDIO_READREG(device_get_parent(dev),
804bf73b5a2SMichael Zhilin 		    GLOBAL_REGISTER + sc->smi_offset, VTU_OPERATION);
805bf73b5a2SMichael Zhilin 		if ((busy & VTU_BUSY) == 0)
806bf73b5a2SMichael Zhilin 			break;
807bf73b5a2SMichael Zhilin 	}
808bf73b5a2SMichael Zhilin 
809bf73b5a2SMichael Zhilin }
810bf73b5a2SMichael Zhilin 
811bf73b5a2SMichael Zhilin static int
e6060sw_read_vtu(device_t dev,int num,int * data1,int * data2)812bf73b5a2SMichael Zhilin e6060sw_read_vtu(device_t dev, int num, int *data1, int *data2)
813bf73b5a2SMichael Zhilin {
814bf73b5a2SMichael Zhilin 	struct e6060sw_softc *sc;
815bf73b5a2SMichael Zhilin 	int busy;
816bf73b5a2SMichael Zhilin 
817bf73b5a2SMichael Zhilin 	sc = device_get_softc(dev);
818bf73b5a2SMichael Zhilin 
819bf73b5a2SMichael Zhilin 	num = num - 1;
820bf73b5a2SMichael Zhilin 
821bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
822bf73b5a2SMichael Zhilin 	    VTU_VID_REG, num & 0xfff);
823bf73b5a2SMichael Zhilin 	/* Get Next */
824bf73b5a2SMichael Zhilin 	MDIO_WRITEREG(device_get_parent(dev), GLOBAL_REGISTER + sc->smi_offset,
825bf73b5a2SMichael Zhilin 	    VTU_OPERATION, VTU_BUSY | (VTU_GET_NEXT << 12));
826bf73b5a2SMichael Zhilin 	while (1) {
827bf73b5a2SMichael Zhilin 		busy = MDIO_READREG(device_get_parent(dev),
828bf73b5a2SMichael Zhilin 		    GLOBAL_REGISTER + sc->smi_offset, VTU_OPERATION);
829bf73b5a2SMichael Zhilin 		if ((busy & VTU_BUSY) == 0)
830bf73b5a2SMichael Zhilin 			break;
831bf73b5a2SMichael Zhilin 	}
832bf73b5a2SMichael Zhilin 
833bf73b5a2SMichael Zhilin 	int vid = MDIO_READREG(device_get_parent(dev),
834bf73b5a2SMichael Zhilin 	    GLOBAL_REGISTER + sc->smi_offset, VTU_VID_REG);
835bf73b5a2SMichael Zhilin 	if (vid & 0x1000) {
836bf73b5a2SMichael Zhilin 		*data1 = MDIO_READREG(device_get_parent(dev),
837bf73b5a2SMichael Zhilin 		    GLOBAL_REGISTER + sc->smi_offset, VTU_DATA1_REG);
838bf73b5a2SMichael Zhilin 		*data2 = MDIO_READREG(device_get_parent(dev),
839bf73b5a2SMichael Zhilin 		    GLOBAL_REGISTER + sc->smi_offset, VTU_DATA2_REG);
840bf73b5a2SMichael Zhilin 
841bf73b5a2SMichael Zhilin 		return (vid & 0xfff);
842bf73b5a2SMichael Zhilin 	}
843bf73b5a2SMichael Zhilin 
844bf73b5a2SMichael Zhilin 	return (-1);
845bf73b5a2SMichael Zhilin }
846bf73b5a2SMichael Zhilin 
8474677ca5bSMichael Zhilin static int
e6060sw_setconf(device_t dev,etherswitch_conf_t * conf)8484677ca5bSMichael Zhilin e6060sw_setconf(device_t dev, etherswitch_conf_t *conf)
8494677ca5bSMichael Zhilin {
8504677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
8514677ca5bSMichael Zhilin 
8524677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
8534677ca5bSMichael Zhilin 
8544677ca5bSMichael Zhilin 	/* Set the VLAN mode. */
8554677ca5bSMichael Zhilin 	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
8564677ca5bSMichael Zhilin 		if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) {
8574677ca5bSMichael Zhilin 			sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
858bf73b5a2SMichael Zhilin 			e6060sw_dot1q_mode(dev, DOT1QNONE);
859bf73b5a2SMichael Zhilin 			e6060sw_reset_vlans(dev);
860bf73b5a2SMichael Zhilin 		} else if ((sc->sw_model == E6063 || sc->sw_model == E6065) &&
861bf73b5a2SMichael Zhilin 		    conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
862bf73b5a2SMichael Zhilin 			sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
863bf73b5a2SMichael Zhilin 			e6060sw_dot1q_mode(dev, DOT1QSECURE);
864bf73b5a2SMichael Zhilin 			e6060sw_init_vtu(dev);
8654677ca5bSMichael Zhilin 		} else {
8664677ca5bSMichael Zhilin 			sc->vlan_mode = 0;
8674677ca5bSMichael Zhilin 			/* Reset VLANs. */
868bf73b5a2SMichael Zhilin 			e6060sw_dot1q_mode(dev, DOT1QNONE);
8694677ca5bSMichael Zhilin 			e6060sw_reset_vlans(dev);
8704677ca5bSMichael Zhilin 		}
871bf73b5a2SMichael Zhilin 	}
8724677ca5bSMichael Zhilin 
8734677ca5bSMichael Zhilin 	return (0);
8744677ca5bSMichael Zhilin }
8754677ca5bSMichael Zhilin 
8764677ca5bSMichael Zhilin static void
e6060sw_statchg(device_t dev)8774677ca5bSMichael Zhilin e6060sw_statchg(device_t dev)
8784677ca5bSMichael Zhilin {
8794677ca5bSMichael Zhilin 
8804677ca5bSMichael Zhilin 	DPRINTF(dev, "%s\n", __func__);
8814677ca5bSMichael Zhilin }
8824677ca5bSMichael Zhilin 
8834677ca5bSMichael Zhilin static int
e6060sw_ifmedia_upd(if_t ifp)8842e6a8c1aSJustin Hibbits e6060sw_ifmedia_upd(if_t ifp)
8854677ca5bSMichael Zhilin {
8864677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
8874677ca5bSMichael Zhilin 	struct mii_data *mii;
8884677ca5bSMichael Zhilin 
8892e6a8c1aSJustin Hibbits 	sc = if_getsoftc(ifp);
890b29549c7SJustin Hibbits 	mii = e6060sw_miiforport(sc, if_getdunit(ifp));
8914677ca5bSMichael Zhilin 
8924677ca5bSMichael Zhilin 	DPRINTF(sc->sc_dev, "%s\n", __func__);
8934677ca5bSMichael Zhilin 	if (mii == NULL)
8944677ca5bSMichael Zhilin 		return (ENXIO);
8954677ca5bSMichael Zhilin 	mii_mediachg(mii);
8964677ca5bSMichael Zhilin 	return (0);
8974677ca5bSMichael Zhilin }
8984677ca5bSMichael Zhilin 
8994677ca5bSMichael Zhilin static void
e6060sw_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)9002e6a8c1aSJustin Hibbits e6060sw_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
9014677ca5bSMichael Zhilin {
9024677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
9034677ca5bSMichael Zhilin 	struct mii_data *mii;
9044677ca5bSMichael Zhilin 
9052e6a8c1aSJustin Hibbits 	sc = if_getsoftc(ifp);
906b29549c7SJustin Hibbits 	mii = e6060sw_miiforport(sc, if_getdunit(ifp));
9074677ca5bSMichael Zhilin 
9084677ca5bSMichael Zhilin 	DPRINTF(sc->sc_dev, "%s\n", __func__);
9094677ca5bSMichael Zhilin 
9104677ca5bSMichael Zhilin 	if (mii == NULL)
9114677ca5bSMichael Zhilin 		return;
9124677ca5bSMichael Zhilin 	mii_pollstat(mii);
9134677ca5bSMichael Zhilin 	ifmr->ifm_active = mii->mii_media_active;
9144677ca5bSMichael Zhilin 	ifmr->ifm_status = mii->mii_media_status;
9154677ca5bSMichael Zhilin }
9164677ca5bSMichael Zhilin 
9174677ca5bSMichael Zhilin static int
e6060sw_readphy(device_t dev,int phy,int reg)9184677ca5bSMichael Zhilin e6060sw_readphy(device_t dev, int phy, int reg)
9194677ca5bSMichael Zhilin {
9204677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
9214677ca5bSMichael Zhilin 	int data;
9224677ca5bSMichael Zhilin 
9234677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
9244677ca5bSMichael Zhilin 	E6060SW_LOCK_ASSERT(sc, MA_NOTOWNED);
9254677ca5bSMichael Zhilin 
9264677ca5bSMichael Zhilin 	if (phy < 0 || phy >= 32)
9274677ca5bSMichael Zhilin 		return (ENXIO);
9284677ca5bSMichael Zhilin 	if (reg < 0 || reg >= 32)
9294677ca5bSMichael Zhilin 		return (ENXIO);
9304677ca5bSMichael Zhilin 
9314677ca5bSMichael Zhilin 	E6060SW_LOCK(sc);
9324677ca5bSMichael Zhilin 	data = MDIO_READREG(device_get_parent(dev), phy, reg);
9334677ca5bSMichael Zhilin 	E6060SW_UNLOCK(sc);
9344677ca5bSMichael Zhilin 
9354677ca5bSMichael Zhilin 	return (data);
9364677ca5bSMichael Zhilin }
9374677ca5bSMichael Zhilin 
9384677ca5bSMichael Zhilin static int
e6060sw_writephy(device_t dev,int phy,int reg,int data)9394677ca5bSMichael Zhilin e6060sw_writephy(device_t dev, int phy, int reg, int data)
9404677ca5bSMichael Zhilin {
9414677ca5bSMichael Zhilin 	struct e6060sw_softc *sc;
9424677ca5bSMichael Zhilin 	int err;
9434677ca5bSMichael Zhilin 
9444677ca5bSMichael Zhilin 	sc = device_get_softc(dev);
9454677ca5bSMichael Zhilin 	E6060SW_LOCK_ASSERT(sc, MA_NOTOWNED);
9464677ca5bSMichael Zhilin 
9474677ca5bSMichael Zhilin 	if (phy < 0 || phy >= 32)
9484677ca5bSMichael Zhilin 		return (ENXIO);
9494677ca5bSMichael Zhilin 	if (reg < 0 || reg >= 32)
9504677ca5bSMichael Zhilin 		return (ENXIO);
9514677ca5bSMichael Zhilin 
9524677ca5bSMichael Zhilin 	E6060SW_LOCK(sc);
9534677ca5bSMichael Zhilin 	err = MDIO_WRITEREG(device_get_parent(dev), phy, reg, data);
9544677ca5bSMichael Zhilin 	E6060SW_UNLOCK(sc);
9554677ca5bSMichael Zhilin 
9564677ca5bSMichael Zhilin 	return (err);
9574677ca5bSMichael Zhilin }
9584677ca5bSMichael Zhilin 
9594677ca5bSMichael Zhilin /* addr is 5-8 bit is SMI Device Addres, 0-4 bit is SMI Register Address */
9604677ca5bSMichael Zhilin 
9614677ca5bSMichael Zhilin static int
e6060sw_readreg(device_t dev,int addr)9624677ca5bSMichael Zhilin e6060sw_readreg(device_t dev, int addr)
9634677ca5bSMichael Zhilin {
9644677ca5bSMichael Zhilin 	int devaddr, regaddr;
9654677ca5bSMichael Zhilin 
966bf73b5a2SMichael Zhilin 	devaddr = (addr >> 5) & 0x1f;
9674677ca5bSMichael Zhilin 	regaddr = addr & 0x1f;
9684677ca5bSMichael Zhilin 
969bf73b5a2SMichael Zhilin 	return MDIO_READREG(device_get_parent(dev), devaddr, regaddr);
9704677ca5bSMichael Zhilin }
9714677ca5bSMichael Zhilin 
9724677ca5bSMichael Zhilin /* addr is 5-8 bit is SMI Device Addres, 0-4 bit is SMI Register Address */
9734677ca5bSMichael Zhilin 
9744677ca5bSMichael Zhilin static int
e6060sw_writereg(device_t dev,int addr,int value)9754677ca5bSMichael Zhilin e6060sw_writereg(device_t dev, int addr, int value)
9764677ca5bSMichael Zhilin {
9774677ca5bSMichael Zhilin 	int devaddr, regaddr;
9784677ca5bSMichael Zhilin 
979bf73b5a2SMichael Zhilin 	devaddr = (addr >> 5) & 0x1f;
9804677ca5bSMichael Zhilin 	regaddr = addr & 0x1f;
9814677ca5bSMichael Zhilin 
982bf73b5a2SMichael Zhilin 	return (MDIO_WRITEREG(device_get_parent(dev), devaddr, regaddr, value));
9834677ca5bSMichael Zhilin }
9844677ca5bSMichael Zhilin 
9854677ca5bSMichael Zhilin static device_method_t e6060sw_methods[] = {
9864677ca5bSMichael Zhilin 	/* Device interface */
9874677ca5bSMichael Zhilin 	DEVMETHOD(device_probe,		e6060sw_probe),
9884677ca5bSMichael Zhilin 	DEVMETHOD(device_attach,	e6060sw_attach),
9894677ca5bSMichael Zhilin 	DEVMETHOD(device_detach,	e6060sw_detach),
9904677ca5bSMichael Zhilin 
9914677ca5bSMichael Zhilin 	/* bus interface */
9924677ca5bSMichael Zhilin 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
9934677ca5bSMichael Zhilin 
9944677ca5bSMichael Zhilin 	/* MII interface */
9954677ca5bSMichael Zhilin 	DEVMETHOD(miibus_readreg,	e6060sw_readphy),
9964677ca5bSMichael Zhilin 	DEVMETHOD(miibus_writereg,	e6060sw_writephy),
9974677ca5bSMichael Zhilin 	DEVMETHOD(miibus_statchg,	e6060sw_statchg),
9984677ca5bSMichael Zhilin 
9994677ca5bSMichael Zhilin 	/* MDIO interface */
10004677ca5bSMichael Zhilin 	DEVMETHOD(mdio_readreg,		e6060sw_readphy),
10014677ca5bSMichael Zhilin 	DEVMETHOD(mdio_writereg,	e6060sw_writephy),
10024677ca5bSMichael Zhilin 
10034677ca5bSMichael Zhilin 	/* etherswitch interface */
10044677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_lock,	e6060sw_lock),
10054677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_unlock,	e6060sw_unlock),
10064677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_getinfo,	e6060sw_getinfo),
10074677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_readreg,	e6060sw_readreg),
10084677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_writereg,	e6060sw_writereg),
10094677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_readphyreg,	e6060sw_readphy),
10104677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_writephyreg,	e6060sw_writephy),
10114677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_getport,	e6060sw_getport),
10124677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_setport,	e6060sw_setport),
10134677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_getvgroup,	e6060sw_getvgroup),
10144677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_setvgroup,	e6060sw_setvgroup),
10154677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_setconf,	e6060sw_setconf),
10164677ca5bSMichael Zhilin 	DEVMETHOD(etherswitch_getconf,	e6060sw_getconf),
10174677ca5bSMichael Zhilin 
10184677ca5bSMichael Zhilin 	DEVMETHOD_END
10194677ca5bSMichael Zhilin };
10204677ca5bSMichael Zhilin 
10214677ca5bSMichael Zhilin DEFINE_CLASS_0(e6060sw, e6060sw_driver, e6060sw_methods,
10224677ca5bSMichael Zhilin     sizeof(struct e6060sw_softc));
10234677ca5bSMichael Zhilin 
102442726c2fSJohn Baldwin DRIVER_MODULE(e6060sw, mdio, e6060sw_driver, 0, 0);
10253e38757dSJohn Baldwin DRIVER_MODULE(miibus, e6060sw, miibus_driver, 0, 0);
10268933f7d6SJohn Baldwin DRIVER_MODULE(mdio, e6060sw, mdio_driver, 0, 0);
1027829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, e6060sw, etherswitch_driver, 0, 0);
10284677ca5bSMichael Zhilin MODULE_VERSION(e6060sw, 1);
10294677ca5bSMichael Zhilin MODULE_DEPEND(e6060sw, miibus, 1, 1, 1); /* XXX which versions? */
10304677ca5bSMichael Zhilin MODULE_DEPEND(e6060sw, etherswitch, 1, 1, 1); /* XXX which versions? */
1031