xref: /freebsd/sys/dev/etherswitch/infineon/adm6996fc.c (revision c702a80e27130fed0646201d9d4af3f1770950a8)
1*c702a80eSMichael Zhilin /*-
2*c702a80eSMichael Zhilin  * Copyright (c) 2016 Hiroki Mori
3*c702a80eSMichael Zhilin  * Copyright (c) 2013 Luiz Otavio O Souza.
4*c702a80eSMichael Zhilin  * Copyright (c) 2011-2012 Stefan Bethke.
5*c702a80eSMichael Zhilin  * Copyright (c) 2012 Adrian Chadd.
6*c702a80eSMichael Zhilin  * All rights reserved.
7*c702a80eSMichael Zhilin  *
8*c702a80eSMichael Zhilin  * Redistribution and use in source and binary forms, with or without
9*c702a80eSMichael Zhilin  * modification, are permitted provided that the following conditions
10*c702a80eSMichael Zhilin  * are met:
11*c702a80eSMichael Zhilin  * 1. Redistributions of source code must retain the above copyright
12*c702a80eSMichael Zhilin  *    notice, this list of conditions and the following disclaimer.
13*c702a80eSMichael Zhilin  * 2. Redistributions in binary form must reproduce the above copyright
14*c702a80eSMichael Zhilin  *    notice, this list of conditions and the following disclaimer in the
15*c702a80eSMichael Zhilin  *    documentation and/or other materials provided with the distribution.
16*c702a80eSMichael Zhilin  *
17*c702a80eSMichael Zhilin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18*c702a80eSMichael Zhilin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*c702a80eSMichael Zhilin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*c702a80eSMichael Zhilin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21*c702a80eSMichael Zhilin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*c702a80eSMichael Zhilin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*c702a80eSMichael Zhilin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*c702a80eSMichael Zhilin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*c702a80eSMichael Zhilin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*c702a80eSMichael Zhilin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*c702a80eSMichael Zhilin  * SUCH DAMAGE.
28*c702a80eSMichael Zhilin  *
29*c702a80eSMichael Zhilin  * $FreeBSD$
30*c702a80eSMichael Zhilin  */
31*c702a80eSMichael Zhilin 
32*c702a80eSMichael Zhilin /*
33*c702a80eSMichael Zhilin  * This is Infineon ADM6996FC/M/MX driver code on etherswitch framework.
34*c702a80eSMichael Zhilin  * Support PORT and DOT1Q VLAN.
35*c702a80eSMichael Zhilin  * This code suppose ADM6996FC SDC/SDIO connect to SOC network interface
36*c702a80eSMichael Zhilin  * MDC/MDIO.
37*c702a80eSMichael Zhilin  * This code development on Netgear WGR614Cv7.
38*c702a80eSMichael Zhilin  */
39*c702a80eSMichael Zhilin 
40*c702a80eSMichael Zhilin #include <sys/param.h>
41*c702a80eSMichael Zhilin #include <sys/bus.h>
42*c702a80eSMichael Zhilin #include <sys/errno.h>
43*c702a80eSMichael Zhilin #include <sys/kernel.h>
44*c702a80eSMichael Zhilin #include <sys/lock.h>
45*c702a80eSMichael Zhilin #include <sys/malloc.h>
46*c702a80eSMichael Zhilin #include <sys/module.h>
47*c702a80eSMichael Zhilin #include <sys/mutex.h>
48*c702a80eSMichael Zhilin #include <sys/socket.h>
49*c702a80eSMichael Zhilin #include <sys/sockio.h>
50*c702a80eSMichael Zhilin #include <sys/sysctl.h>
51*c702a80eSMichael Zhilin #include <sys/systm.h>
52*c702a80eSMichael Zhilin 
53*c702a80eSMichael Zhilin #include <net/if.h>
54*c702a80eSMichael Zhilin #include <net/if_var.h>
55*c702a80eSMichael Zhilin #include <net/ethernet.h>
56*c702a80eSMichael Zhilin #include <net/if_media.h>
57*c702a80eSMichael Zhilin #include <net/if_types.h>
58*c702a80eSMichael Zhilin 
59*c702a80eSMichael Zhilin #include <machine/bus.h>
60*c702a80eSMichael Zhilin #include <dev/mii/mii.h>
61*c702a80eSMichael Zhilin #include <dev/mii/miivar.h>
62*c702a80eSMichael Zhilin #include <dev/mdio/mdio.h>
63*c702a80eSMichael Zhilin 
64*c702a80eSMichael Zhilin #include <dev/etherswitch/etherswitch.h>
65*c702a80eSMichael Zhilin 
66*c702a80eSMichael Zhilin #include "mdio_if.h"
67*c702a80eSMichael Zhilin #include "miibus_if.h"
68*c702a80eSMichael Zhilin #include "etherswitch_if.h"
69*c702a80eSMichael Zhilin 
70*c702a80eSMichael Zhilin #define	ADM6996FC_PRODUCT_CODE	0x7102
71*c702a80eSMichael Zhilin 
72*c702a80eSMichael Zhilin #define	ADM6996FC_SC3		0x11
73*c702a80eSMichael Zhilin #define	ADM6996FC_VF0L		0x40
74*c702a80eSMichael Zhilin #define	ADM6996FC_VF0H		0x41
75*c702a80eSMichael Zhilin #define	ADM6996FC_CI0		0xa0
76*c702a80eSMichael Zhilin #define	ADM6996FC_CI1		0xa1
77*c702a80eSMichael Zhilin #define	ADM6996FC_PHY_C0	0x200
78*c702a80eSMichael Zhilin 
79*c702a80eSMichael Zhilin #define	ADM6996FC_PC_SHIFT	4
80*c702a80eSMichael Zhilin #define	ADM6996FC_TBV_SHIFT	5
81*c702a80eSMichael Zhilin #define	ADM6996FC_PVID_SHIFT	10
82*c702a80eSMichael Zhilin #define	ADM6996FC_OPTE_SHIFT	4
83*c702a80eSMichael Zhilin #define	ADM6996FC_VV_SHIFT	15
84*c702a80eSMichael Zhilin 
85*c702a80eSMichael Zhilin #define	ADM6996FC_PHY_SIZE	0x20
86*c702a80eSMichael Zhilin 
87*c702a80eSMichael Zhilin MALLOC_DECLARE(M_ADM6996FC);
88*c702a80eSMichael Zhilin MALLOC_DEFINE(M_ADM6996FC, "adm6996fc", "adm6996fc data structures");
89*c702a80eSMichael Zhilin 
90*c702a80eSMichael Zhilin struct adm6996fc_softc {
91*c702a80eSMichael Zhilin 	struct mtx	sc_mtx;		/* serialize access to softc */
92*c702a80eSMichael Zhilin 	device_t	sc_dev;
93*c702a80eSMichael Zhilin 	int		vlan_mode;
94*c702a80eSMichael Zhilin 	int		media;		/* cpu port media */
95*c702a80eSMichael Zhilin 	int		cpuport;	/* which PHY is connected to the CPU */
96*c702a80eSMichael Zhilin 	int		phymask;	/* PHYs we manage */
97*c702a80eSMichael Zhilin 	int		numports;	/* number of ports */
98*c702a80eSMichael Zhilin 	int		ifpport[MII_NPHY];
99*c702a80eSMichael Zhilin 	int		*portphy;
100*c702a80eSMichael Zhilin 	char		**ifname;
101*c702a80eSMichael Zhilin 	device_t	**miibus;
102*c702a80eSMichael Zhilin 	struct ifnet	**ifp;
103*c702a80eSMichael Zhilin 	struct callout	callout_tick;
104*c702a80eSMichael Zhilin 	etherswitch_info_t	info;
105*c702a80eSMichael Zhilin };
106*c702a80eSMichael Zhilin 
107*c702a80eSMichael Zhilin #define	ADM6996FC_LOCK(_sc)			\
108*c702a80eSMichael Zhilin 	    mtx_lock(&(_sc)->sc_mtx)
109*c702a80eSMichael Zhilin #define	ADM6996FC_UNLOCK(_sc)			\
110*c702a80eSMichael Zhilin 	    mtx_unlock(&(_sc)->sc_mtx)
111*c702a80eSMichael Zhilin #define	ADM6996FC_LOCK_ASSERT(_sc, _what)	\
112*c702a80eSMichael Zhilin 	    mtx_assert(&(_sc)->sc_mtx, (_what))
113*c702a80eSMichael Zhilin #define	ADM6996FC_TRYLOCK(_sc)			\
114*c702a80eSMichael Zhilin 	    mtx_trylock(&(_sc)->sc_mtx)
115*c702a80eSMichael Zhilin 
116*c702a80eSMichael Zhilin #if defined(DEBUG)
117*c702a80eSMichael Zhilin #define	DPRINTF(dev, args...) device_printf(dev, args)
118*c702a80eSMichael Zhilin #else
119*c702a80eSMichael Zhilin #define	DPRINTF(dev, args...)
120*c702a80eSMichael Zhilin #endif
121*c702a80eSMichael Zhilin 
122*c702a80eSMichael Zhilin static inline int adm6996fc_portforphy(struct adm6996fc_softc *, int);
123*c702a80eSMichael Zhilin static void adm6996fc_tick(void *);
124*c702a80eSMichael Zhilin static int adm6996fc_ifmedia_upd(struct ifnet *);
125*c702a80eSMichael Zhilin static void adm6996fc_ifmedia_sts(struct ifnet *, struct ifmediareq *);
126*c702a80eSMichael Zhilin 
127*c702a80eSMichael Zhilin #define	ADM6996FC_READREG(dev, x)					\
128*c702a80eSMichael Zhilin 	MDIO_READREG(dev, ((x) >> 5), ((x) & 0x1f));
129*c702a80eSMichael Zhilin #define	ADM6996FC_WRITEREG(dev, x, v)					\
130*c702a80eSMichael Zhilin 	MDIO_WRITEREG(dev, ((x) >> 5), ((x) & 0x1f), v);
131*c702a80eSMichael Zhilin 
132*c702a80eSMichael Zhilin #define	ADM6996FC_PVIDBYDATA(data1, data2)				\
133*c702a80eSMichael Zhilin 	((((data1) >> ADM6996FC_PVID_SHIFT) & 0x0f) | ((data2) << 4))
134*c702a80eSMichael Zhilin 
135*c702a80eSMichael Zhilin static int
136*c702a80eSMichael Zhilin adm6996fc_probe(device_t dev)
137*c702a80eSMichael Zhilin {
138*c702a80eSMichael Zhilin 	int data1, data2;
139*c702a80eSMichael Zhilin 	int pc;
140*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
141*c702a80eSMichael Zhilin 
142*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
143*c702a80eSMichael Zhilin 	bzero(sc, sizeof(*sc));
144*c702a80eSMichael Zhilin 
145*c702a80eSMichael Zhilin 	data1 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI0);
146*c702a80eSMichael Zhilin 	data2 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI1);
147*c702a80eSMichael Zhilin 	pc = ((data2 << 16) | data1) >> ADM6996FC_PC_SHIFT;
148*c702a80eSMichael Zhilin 	if (bootverbose)
149*c702a80eSMichael Zhilin 		device_printf(dev,"Chip Identifier Register %x %x\n", data1,
150*c702a80eSMichael Zhilin 		    data2);
151*c702a80eSMichael Zhilin 
152*c702a80eSMichael Zhilin 	/* check Product Code */
153*c702a80eSMichael Zhilin 	if (pc != ADM6996FC_PRODUCT_CODE) {
154*c702a80eSMichael Zhilin 		return (ENXIO);
155*c702a80eSMichael Zhilin 	}
156*c702a80eSMichael Zhilin 
157*c702a80eSMichael Zhilin 	device_set_desc_copy(dev, "Infineon ADM6996FC/M/MX MDIO switch driver");
158*c702a80eSMichael Zhilin 	return (BUS_PROBE_DEFAULT);
159*c702a80eSMichael Zhilin }
160*c702a80eSMichael Zhilin 
161*c702a80eSMichael Zhilin static int
162*c702a80eSMichael Zhilin adm6996fc_attach_phys(struct adm6996fc_softc *sc)
163*c702a80eSMichael Zhilin {
164*c702a80eSMichael Zhilin 	int phy, port, err;
165*c702a80eSMichael Zhilin 	char name[IFNAMSIZ];
166*c702a80eSMichael Zhilin 
167*c702a80eSMichael Zhilin 	port = 0;
168*c702a80eSMichael Zhilin 	err = 0;
169*c702a80eSMichael Zhilin 	/* PHYs need an interface, so we generate a dummy one */
170*c702a80eSMichael Zhilin 	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
171*c702a80eSMichael Zhilin 	for (phy = 0; phy < sc->numports; phy++) {
172*c702a80eSMichael Zhilin 		if (((1 << phy) & sc->phymask) == 0)
173*c702a80eSMichael Zhilin 			continue;
174*c702a80eSMichael Zhilin 		sc->ifpport[phy] = port;
175*c702a80eSMichael Zhilin 		sc->portphy[port] = phy;
176*c702a80eSMichael Zhilin 		sc->ifp[port] = if_alloc(IFT_ETHER);
177*c702a80eSMichael Zhilin 		sc->ifp[port]->if_softc = sc;
178*c702a80eSMichael Zhilin 		sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
179*c702a80eSMichael Zhilin 		    IFF_DRV_RUNNING | IFF_SIMPLEX;
180*c702a80eSMichael Zhilin 		if_initname(sc->ifp[port], name, port);
181*c702a80eSMichael Zhilin 		sc->miibus[port] = malloc(sizeof(device_t), M_ADM6996FC,
182*c702a80eSMichael Zhilin 		    M_WAITOK | M_ZERO);
183*c702a80eSMichael Zhilin 		if (sc->miibus[port] == NULL) {
184*c702a80eSMichael Zhilin 			err = ENOMEM;
185*c702a80eSMichael Zhilin 			goto failed;
186*c702a80eSMichael Zhilin 		}
187*c702a80eSMichael Zhilin 		err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
188*c702a80eSMichael Zhilin 		    adm6996fc_ifmedia_upd, adm6996fc_ifmedia_sts, \
189*c702a80eSMichael Zhilin 		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
190*c702a80eSMichael Zhilin 		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
191*c702a80eSMichael Zhilin 		    device_get_nameunit(*sc->miibus[port]),
192*c702a80eSMichael Zhilin 		    sc->ifp[port]->if_xname);
193*c702a80eSMichael Zhilin 		if (err != 0) {
194*c702a80eSMichael Zhilin 			device_printf(sc->sc_dev,
195*c702a80eSMichael Zhilin 			    "attaching PHY %d failed\n",
196*c702a80eSMichael Zhilin 			    phy);
197*c702a80eSMichael Zhilin 			goto failed;
198*c702a80eSMichael Zhilin 		}
199*c702a80eSMichael Zhilin 		++port;
200*c702a80eSMichael Zhilin 	}
201*c702a80eSMichael Zhilin 	sc->info.es_nports = port;
202*c702a80eSMichael Zhilin 	if (sc->cpuport != -1) {
203*c702a80eSMichael Zhilin 		/* assume cpuport is last one */
204*c702a80eSMichael Zhilin 		sc->ifpport[sc->cpuport] = port;
205*c702a80eSMichael Zhilin 		sc->portphy[port] = sc->cpuport;
206*c702a80eSMichael Zhilin 		++sc->info.es_nports;
207*c702a80eSMichael Zhilin 	}
208*c702a80eSMichael Zhilin 	return (0);
209*c702a80eSMichael Zhilin 
210*c702a80eSMichael Zhilin failed:
211*c702a80eSMichael Zhilin 	for (phy = 0; phy < sc->numports; phy++) {
212*c702a80eSMichael Zhilin 		if (((1 << phy) & sc->phymask) == 0)
213*c702a80eSMichael Zhilin 			continue;
214*c702a80eSMichael Zhilin 		port = adm6996fc_portforphy(sc, phy);
215*c702a80eSMichael Zhilin 		if (sc->miibus[port] != NULL)
216*c702a80eSMichael Zhilin 			device_delete_child(sc->sc_dev, (*sc->miibus[port]));
217*c702a80eSMichael Zhilin 		if (sc->ifp[port] != NULL)
218*c702a80eSMichael Zhilin 			if_free(sc->ifp[port]);
219*c702a80eSMichael Zhilin 		if (sc->ifname[port] != NULL)
220*c702a80eSMichael Zhilin 			free(sc->ifname[port], M_ADM6996FC);
221*c702a80eSMichael Zhilin 		if (sc->miibus[port] != NULL)
222*c702a80eSMichael Zhilin 			free(sc->miibus[port], M_ADM6996FC);
223*c702a80eSMichael Zhilin 	}
224*c702a80eSMichael Zhilin 	return (err);
225*c702a80eSMichael Zhilin }
226*c702a80eSMichael Zhilin 
227*c702a80eSMichael Zhilin static int
228*c702a80eSMichael Zhilin adm6996fc_attach(device_t dev)
229*c702a80eSMichael Zhilin {
230*c702a80eSMichael Zhilin 	struct adm6996fc_softc	*sc;
231*c702a80eSMichael Zhilin 	int			 err;
232*c702a80eSMichael Zhilin 
233*c702a80eSMichael Zhilin 	err = 0;
234*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
235*c702a80eSMichael Zhilin 
236*c702a80eSMichael Zhilin 	sc->sc_dev = dev;
237*c702a80eSMichael Zhilin 	mtx_init(&sc->sc_mtx, "adm6996fc", NULL, MTX_DEF);
238*c702a80eSMichael Zhilin 	strlcpy(sc->info.es_name, device_get_desc(dev),
239*c702a80eSMichael Zhilin 	    sizeof(sc->info.es_name));
240*c702a80eSMichael Zhilin 
241*c702a80eSMichael Zhilin 	/* ADM6996FC Defaults */
242*c702a80eSMichael Zhilin 	sc->numports = 6;
243*c702a80eSMichael Zhilin 	sc->phymask = 0x1f;
244*c702a80eSMichael Zhilin 	sc->cpuport = 5;
245*c702a80eSMichael Zhilin 	sc->media = 100;
246*c702a80eSMichael Zhilin 
247*c702a80eSMichael Zhilin 	sc->info.es_nvlangroups = 16;
248*c702a80eSMichael Zhilin 	sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
249*c702a80eSMichael Zhilin 
250*c702a80eSMichael Zhilin 	sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_ADM6996FC,
251*c702a80eSMichael Zhilin 	    M_WAITOK | M_ZERO);
252*c702a80eSMichael Zhilin 	sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC,
253*c702a80eSMichael Zhilin 	    M_WAITOK | M_ZERO);
254*c702a80eSMichael Zhilin 	sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC,
255*c702a80eSMichael Zhilin 	    M_WAITOK | M_ZERO);
256*c702a80eSMichael Zhilin 	sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC,
257*c702a80eSMichael Zhilin 	    M_WAITOK | M_ZERO);
258*c702a80eSMichael Zhilin 
259*c702a80eSMichael Zhilin 	if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL ||
260*c702a80eSMichael Zhilin 	    sc->portphy == NULL) {
261*c702a80eSMichael Zhilin 		err = ENOMEM;
262*c702a80eSMichael Zhilin 		goto failed;
263*c702a80eSMichael Zhilin 	}
264*c702a80eSMichael Zhilin 
265*c702a80eSMichael Zhilin 	/*
266*c702a80eSMichael Zhilin 	 * Attach the PHYs and complete the bus enumeration.
267*c702a80eSMichael Zhilin 	 */
268*c702a80eSMichael Zhilin 	err = adm6996fc_attach_phys(sc);
269*c702a80eSMichael Zhilin 	if (err != 0)
270*c702a80eSMichael Zhilin 		goto failed;
271*c702a80eSMichael Zhilin 
272*c702a80eSMichael Zhilin 	bus_generic_probe(dev);
273*c702a80eSMichael Zhilin 	bus_enumerate_hinted_children(dev);
274*c702a80eSMichael Zhilin 	err = bus_generic_attach(dev);
275*c702a80eSMichael Zhilin 	if (err != 0)
276*c702a80eSMichael Zhilin 		goto failed;
277*c702a80eSMichael Zhilin 
278*c702a80eSMichael Zhilin 	callout_init(&sc->callout_tick, 0);
279*c702a80eSMichael Zhilin 
280*c702a80eSMichael Zhilin 	adm6996fc_tick(sc);
281*c702a80eSMichael Zhilin 
282*c702a80eSMichael Zhilin 	return (0);
283*c702a80eSMichael Zhilin 
284*c702a80eSMichael Zhilin failed:
285*c702a80eSMichael Zhilin 	if (sc->portphy != NULL)
286*c702a80eSMichael Zhilin 		free(sc->portphy, M_ADM6996FC);
287*c702a80eSMichael Zhilin 	if (sc->miibus != NULL)
288*c702a80eSMichael Zhilin 		free(sc->miibus, M_ADM6996FC);
289*c702a80eSMichael Zhilin 	if (sc->ifname != NULL)
290*c702a80eSMichael Zhilin 		free(sc->ifname, M_ADM6996FC);
291*c702a80eSMichael Zhilin 	if (sc->ifp != NULL)
292*c702a80eSMichael Zhilin 		free(sc->ifp, M_ADM6996FC);
293*c702a80eSMichael Zhilin 
294*c702a80eSMichael Zhilin 	return (err);
295*c702a80eSMichael Zhilin }
296*c702a80eSMichael Zhilin 
297*c702a80eSMichael Zhilin static int
298*c702a80eSMichael Zhilin adm6996fc_detach(device_t dev)
299*c702a80eSMichael Zhilin {
300*c702a80eSMichael Zhilin 	struct adm6996fc_softc	*sc;
301*c702a80eSMichael Zhilin 	int			 i, port;
302*c702a80eSMichael Zhilin 
303*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
304*c702a80eSMichael Zhilin 
305*c702a80eSMichael Zhilin 	callout_drain(&sc->callout_tick);
306*c702a80eSMichael Zhilin 
307*c702a80eSMichael Zhilin 	for (i = 0; i < MII_NPHY; i++) {
308*c702a80eSMichael Zhilin 		if (((1 << i) & sc->phymask) == 0)
309*c702a80eSMichael Zhilin 			continue;
310*c702a80eSMichael Zhilin 		port = adm6996fc_portforphy(sc, i);
311*c702a80eSMichael Zhilin 		if (sc->miibus[port] != NULL)
312*c702a80eSMichael Zhilin 			device_delete_child(dev, (*sc->miibus[port]));
313*c702a80eSMichael Zhilin 		if (sc->ifp[port] != NULL)
314*c702a80eSMichael Zhilin 			if_free(sc->ifp[port]);
315*c702a80eSMichael Zhilin 		free(sc->ifname[port], M_ADM6996FC);
316*c702a80eSMichael Zhilin 		free(sc->miibus[port], M_ADM6996FC);
317*c702a80eSMichael Zhilin 	}
318*c702a80eSMichael Zhilin 
319*c702a80eSMichael Zhilin 	free(sc->portphy, M_ADM6996FC);
320*c702a80eSMichael Zhilin 	free(sc->miibus, M_ADM6996FC);
321*c702a80eSMichael Zhilin 	free(sc->ifname, M_ADM6996FC);
322*c702a80eSMichael Zhilin 	free(sc->ifp, M_ADM6996FC);
323*c702a80eSMichael Zhilin 
324*c702a80eSMichael Zhilin 	bus_generic_detach(dev);
325*c702a80eSMichael Zhilin 	mtx_destroy(&sc->sc_mtx);
326*c702a80eSMichael Zhilin 
327*c702a80eSMichael Zhilin 	return (0);
328*c702a80eSMichael Zhilin }
329*c702a80eSMichael Zhilin 
330*c702a80eSMichael Zhilin /*
331*c702a80eSMichael Zhilin  * Convert PHY number to port number.
332*c702a80eSMichael Zhilin  */
333*c702a80eSMichael Zhilin static inline int
334*c702a80eSMichael Zhilin adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy)
335*c702a80eSMichael Zhilin {
336*c702a80eSMichael Zhilin 
337*c702a80eSMichael Zhilin 	return (sc->ifpport[phy]);
338*c702a80eSMichael Zhilin }
339*c702a80eSMichael Zhilin 
340*c702a80eSMichael Zhilin static inline struct mii_data *
341*c702a80eSMichael Zhilin adm6996fc_miiforport(struct adm6996fc_softc *sc, int port)
342*c702a80eSMichael Zhilin {
343*c702a80eSMichael Zhilin 
344*c702a80eSMichael Zhilin 	if (port < 0 || port > sc->numports)
345*c702a80eSMichael Zhilin 		return (NULL);
346*c702a80eSMichael Zhilin 	if (port == sc->cpuport)
347*c702a80eSMichael Zhilin 		return (NULL);
348*c702a80eSMichael Zhilin 	return (device_get_softc(*sc->miibus[port]));
349*c702a80eSMichael Zhilin }
350*c702a80eSMichael Zhilin 
351*c702a80eSMichael Zhilin static inline struct ifnet *
352*c702a80eSMichael Zhilin adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port)
353*c702a80eSMichael Zhilin {
354*c702a80eSMichael Zhilin 
355*c702a80eSMichael Zhilin 	if (port < 0 || port > sc->numports)
356*c702a80eSMichael Zhilin 		return (NULL);
357*c702a80eSMichael Zhilin 	return (sc->ifp[port]);
358*c702a80eSMichael Zhilin }
359*c702a80eSMichael Zhilin 
360*c702a80eSMichael Zhilin /*
361*c702a80eSMichael Zhilin  * Poll the status for all PHYs.
362*c702a80eSMichael Zhilin  */
363*c702a80eSMichael Zhilin static void
364*c702a80eSMichael Zhilin adm6996fc_miipollstat(struct adm6996fc_softc *sc)
365*c702a80eSMichael Zhilin {
366*c702a80eSMichael Zhilin 	int i, port;
367*c702a80eSMichael Zhilin 	struct mii_data *mii;
368*c702a80eSMichael Zhilin 	struct mii_softc *miisc;
369*c702a80eSMichael Zhilin 
370*c702a80eSMichael Zhilin 	ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
371*c702a80eSMichael Zhilin 
372*c702a80eSMichael Zhilin 	for (i = 0; i < MII_NPHY; i++) {
373*c702a80eSMichael Zhilin 		if (((1 << i) & sc->phymask) == 0)
374*c702a80eSMichael Zhilin 			continue;
375*c702a80eSMichael Zhilin 		port = adm6996fc_portforphy(sc, i);
376*c702a80eSMichael Zhilin 		if ((*sc->miibus[port]) == NULL)
377*c702a80eSMichael Zhilin 			continue;
378*c702a80eSMichael Zhilin 		mii = device_get_softc(*sc->miibus[port]);
379*c702a80eSMichael Zhilin 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
380*c702a80eSMichael Zhilin 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
381*c702a80eSMichael Zhilin 			    miisc->mii_inst)
382*c702a80eSMichael Zhilin 				continue;
383*c702a80eSMichael Zhilin 			ukphy_status(miisc);
384*c702a80eSMichael Zhilin 			mii_phy_update(miisc, MII_POLLSTAT);
385*c702a80eSMichael Zhilin 		}
386*c702a80eSMichael Zhilin 	}
387*c702a80eSMichael Zhilin }
388*c702a80eSMichael Zhilin 
389*c702a80eSMichael Zhilin static void
390*c702a80eSMichael Zhilin adm6996fc_tick(void *arg)
391*c702a80eSMichael Zhilin {
392*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
393*c702a80eSMichael Zhilin 
394*c702a80eSMichael Zhilin 	sc = arg;
395*c702a80eSMichael Zhilin 
396*c702a80eSMichael Zhilin 	adm6996fc_miipollstat(sc);
397*c702a80eSMichael Zhilin 	callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc);
398*c702a80eSMichael Zhilin }
399*c702a80eSMichael Zhilin 
400*c702a80eSMichael Zhilin static void
401*c702a80eSMichael Zhilin adm6996fc_lock(device_t dev)
402*c702a80eSMichael Zhilin {
403*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
404*c702a80eSMichael Zhilin 
405*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
406*c702a80eSMichael Zhilin 
407*c702a80eSMichael Zhilin 	ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
408*c702a80eSMichael Zhilin 	ADM6996FC_LOCK(sc);
409*c702a80eSMichael Zhilin }
410*c702a80eSMichael Zhilin 
411*c702a80eSMichael Zhilin static void
412*c702a80eSMichael Zhilin adm6996fc_unlock(device_t dev)
413*c702a80eSMichael Zhilin {
414*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
415*c702a80eSMichael Zhilin 
416*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
417*c702a80eSMichael Zhilin 
418*c702a80eSMichael Zhilin 	ADM6996FC_LOCK_ASSERT(sc, MA_OWNED);
419*c702a80eSMichael Zhilin 	ADM6996FC_UNLOCK(sc);
420*c702a80eSMichael Zhilin }
421*c702a80eSMichael Zhilin 
422*c702a80eSMichael Zhilin static etherswitch_info_t *
423*c702a80eSMichael Zhilin adm6996fc_getinfo(device_t dev)
424*c702a80eSMichael Zhilin {
425*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
426*c702a80eSMichael Zhilin 
427*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
428*c702a80eSMichael Zhilin 
429*c702a80eSMichael Zhilin 	return (&sc->info);
430*c702a80eSMichael Zhilin }
431*c702a80eSMichael Zhilin 
432*c702a80eSMichael Zhilin static int
433*c702a80eSMichael Zhilin adm6996fc_getport(device_t dev, etherswitch_port_t *p)
434*c702a80eSMichael Zhilin {
435*c702a80eSMichael Zhilin 	struct adm6996fc_softc	*sc;
436*c702a80eSMichael Zhilin 	struct mii_data		*mii;
437*c702a80eSMichael Zhilin 	struct ifmediareq	*ifmr;
438*c702a80eSMichael Zhilin 	device_t		 parent;
439*c702a80eSMichael Zhilin 	int 			 err, phy;
440*c702a80eSMichael Zhilin 	int			 data1, data2;
441*c702a80eSMichael Zhilin 
442*c702a80eSMichael Zhilin 	int	bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
443*c702a80eSMichael Zhilin 	int	vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
444*c702a80eSMichael Zhilin 
445*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
446*c702a80eSMichael Zhilin 	ifmr = &p->es_ifmr;
447*c702a80eSMichael Zhilin 
448*c702a80eSMichael Zhilin 	if (p->es_port < 0 || p->es_port >= sc->numports)
449*c702a80eSMichael Zhilin 		return (ENXIO);
450*c702a80eSMichael Zhilin 
451*c702a80eSMichael Zhilin 	parent = device_get_parent(dev);
452*c702a80eSMichael Zhilin 
453*c702a80eSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
454*c702a80eSMichael Zhilin 		data1 = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
455*c702a80eSMichael Zhilin 		data2 = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
456*c702a80eSMichael Zhilin 		/* only port 4 is hi bit */
457*c702a80eSMichael Zhilin 		if (p->es_port == 4)
458*c702a80eSMichael Zhilin 			data2 = (data2 >> 8) & 0xff;
459*c702a80eSMichael Zhilin 		else
460*c702a80eSMichael Zhilin 			data2 = data2 & 0xff;
461*c702a80eSMichael Zhilin 
462*c702a80eSMichael Zhilin 		p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2);
463*c702a80eSMichael Zhilin 		if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1)
464*c702a80eSMichael Zhilin 			p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
465*c702a80eSMichael Zhilin 		else
466*c702a80eSMichael Zhilin 			p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
467*c702a80eSMichael Zhilin 	} else {
468*c702a80eSMichael Zhilin 		p->es_pvid = 0;
469*c702a80eSMichael Zhilin 	}
470*c702a80eSMichael Zhilin 
471*c702a80eSMichael Zhilin 	phy = sc->portphy[p->es_port];
472*c702a80eSMichael Zhilin 	mii = adm6996fc_miiforport(sc, p->es_port);
473*c702a80eSMichael Zhilin 	if (sc->cpuport != -1 && phy == sc->cpuport) {
474*c702a80eSMichael Zhilin 		/* fill in fixed values for CPU port */
475*c702a80eSMichael Zhilin 		p->es_flags |= ETHERSWITCH_PORT_CPU;
476*c702a80eSMichael Zhilin 		ifmr->ifm_count = 0;
477*c702a80eSMichael Zhilin 		if (sc->media == 100)
478*c702a80eSMichael Zhilin 			ifmr->ifm_current = ifmr->ifm_active =
479*c702a80eSMichael Zhilin 			    IFM_ETHER | IFM_100_TX | IFM_FDX;
480*c702a80eSMichael Zhilin 		else
481*c702a80eSMichael Zhilin 			ifmr->ifm_current = ifmr->ifm_active =
482*c702a80eSMichael Zhilin 			    IFM_ETHER | IFM_1000_T | IFM_FDX;
483*c702a80eSMichael Zhilin 		ifmr->ifm_mask = 0;
484*c702a80eSMichael Zhilin 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
485*c702a80eSMichael Zhilin 	} else if (mii != NULL) {
486*c702a80eSMichael Zhilin 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
487*c702a80eSMichael Zhilin 		    &mii->mii_media, SIOCGIFMEDIA);
488*c702a80eSMichael Zhilin 		if (err)
489*c702a80eSMichael Zhilin 			return (err);
490*c702a80eSMichael Zhilin 	} else {
491*c702a80eSMichael Zhilin 		return (ENXIO);
492*c702a80eSMichael Zhilin 	}
493*c702a80eSMichael Zhilin 	return (0);
494*c702a80eSMichael Zhilin }
495*c702a80eSMichael Zhilin 
496*c702a80eSMichael Zhilin static int
497*c702a80eSMichael Zhilin adm6996fc_setport(device_t dev, etherswitch_port_t *p)
498*c702a80eSMichael Zhilin {
499*c702a80eSMichael Zhilin 	struct adm6996fc_softc	*sc;
500*c702a80eSMichael Zhilin 	struct ifmedia		*ifm;
501*c702a80eSMichael Zhilin 	struct mii_data		*mii;
502*c702a80eSMichael Zhilin 	struct ifnet		*ifp;
503*c702a80eSMichael Zhilin 	device_t		 parent;
504*c702a80eSMichael Zhilin 	int 			 err;
505*c702a80eSMichael Zhilin 	int			 data;
506*c702a80eSMichael Zhilin 
507*c702a80eSMichael Zhilin 	int	bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
508*c702a80eSMichael Zhilin 	int	vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
509*c702a80eSMichael Zhilin 
510*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
511*c702a80eSMichael Zhilin 	parent = device_get_parent(dev);
512*c702a80eSMichael Zhilin 
513*c702a80eSMichael Zhilin 	if (p->es_port < 0 || p->es_port >= sc->numports)
514*c702a80eSMichael Zhilin 		return (ENXIO);
515*c702a80eSMichael Zhilin 
516*c702a80eSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
517*c702a80eSMichael Zhilin 		data = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
518*c702a80eSMichael Zhilin 		data &= ~(0xf << 10);
519*c702a80eSMichael Zhilin 		data |= (p->es_pvid & 0xf) << ADM6996FC_PVID_SHIFT;
520*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, bcaddr[p->es_port], data);
521*c702a80eSMichael Zhilin 		data = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
522*c702a80eSMichael Zhilin 		/* only port 4 is hi bit */
523*c702a80eSMichael Zhilin 		if (p->es_port == 4) {
524*c702a80eSMichael Zhilin 			data &= ~(0xff << 8);
525*c702a80eSMichael Zhilin 			data = data | (((p->es_pvid >> 4) & 0xff) << 8);
526*c702a80eSMichael Zhilin 		} else {
527*c702a80eSMichael Zhilin 			data &= ~0xff;
528*c702a80eSMichael Zhilin 			data = data | ((p->es_pvid >> 4) & 0xff);
529*c702a80eSMichael Zhilin 		}
530*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data);
531*c702a80eSMichael Zhilin 		err = 0;
532*c702a80eSMichael Zhilin 	} else {
533*c702a80eSMichael Zhilin 		if (sc->portphy[p->es_port] == sc->cpuport)
534*c702a80eSMichael Zhilin 			return (ENXIO);
535*c702a80eSMichael Zhilin 	}
536*c702a80eSMichael Zhilin 
537*c702a80eSMichael Zhilin 	if (sc->portphy[p->es_port] != sc->cpuport) {
538*c702a80eSMichael Zhilin 		mii = adm6996fc_miiforport(sc, p->es_port);
539*c702a80eSMichael Zhilin 		if (mii == NULL)
540*c702a80eSMichael Zhilin 			return (ENXIO);
541*c702a80eSMichael Zhilin 
542*c702a80eSMichael Zhilin 		ifp = adm6996fc_ifpforport(sc, p->es_port);
543*c702a80eSMichael Zhilin 
544*c702a80eSMichael Zhilin 		ifm = &mii->mii_media;
545*c702a80eSMichael Zhilin 		err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
546*c702a80eSMichael Zhilin 	}
547*c702a80eSMichael Zhilin 	return (err);
548*c702a80eSMichael Zhilin }
549*c702a80eSMichael Zhilin 
550*c702a80eSMichael Zhilin static int
551*c702a80eSMichael Zhilin adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
552*c702a80eSMichael Zhilin {
553*c702a80eSMichael Zhilin 	struct adm6996fc_softc	*sc;
554*c702a80eSMichael Zhilin 	device_t		 parent;
555*c702a80eSMichael Zhilin 	int			 datahi, datalo;
556*c702a80eSMichael Zhilin 
557*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
558*c702a80eSMichael Zhilin 	parent = device_get_parent(dev);
559*c702a80eSMichael Zhilin 
560*c702a80eSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
561*c702a80eSMichael Zhilin 		if (vg->es_vlangroup <= 5) {
562*c702a80eSMichael Zhilin 			vg->es_vid = ETHERSWITCH_VID_VALID;
563*c702a80eSMichael Zhilin 			vg->es_vid |= vg->es_vlangroup;
564*c702a80eSMichael Zhilin 			datalo = ADM6996FC_READREG(parent,
565*c702a80eSMichael Zhilin 			    ADM6996FC_VF0L + 2 * vg->es_vlangroup);
566*c702a80eSMichael Zhilin 			datahi = ADM6996FC_READREG(parent,
567*c702a80eSMichael Zhilin 			    ADM6996FC_VF0H + 2 * vg->es_vlangroup);
568*c702a80eSMichael Zhilin 
569*c702a80eSMichael Zhilin 			vg->es_member_ports = datalo & 0x3f;
570*c702a80eSMichael Zhilin 			vg->es_untagged_ports = vg->es_member_ports;
571*c702a80eSMichael Zhilin 			vg->es_fid = 0;
572*c702a80eSMichael Zhilin 		} else {
573*c702a80eSMichael Zhilin 			vg->es_vid = 0;
574*c702a80eSMichael Zhilin 		}
575*c702a80eSMichael Zhilin 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
576*c702a80eSMichael Zhilin 		datalo = ADM6996FC_READREG(parent,
577*c702a80eSMichael Zhilin 		    ADM6996FC_VF0L + 2 * vg->es_vlangroup);
578*c702a80eSMichael Zhilin 		datahi = ADM6996FC_READREG(parent,
579*c702a80eSMichael Zhilin 		    ADM6996FC_VF0H + 2 * vg->es_vlangroup);
580*c702a80eSMichael Zhilin 
581*c702a80eSMichael Zhilin 		if (datahi & (1 << ADM6996FC_VV_SHIFT)) {
582*c702a80eSMichael Zhilin 			vg->es_vid = ETHERSWITCH_VID_VALID;
583*c702a80eSMichael Zhilin 			vg->es_vid |= datahi & 0xfff;
584*c702a80eSMichael Zhilin 			vg->es_member_ports = datalo & 0x3f;
585*c702a80eSMichael Zhilin 			vg->es_untagged_ports = (~datalo >> 6) & 0x3f;
586*c702a80eSMichael Zhilin 			vg->es_fid = 0;
587*c702a80eSMichael Zhilin 		} else {
588*c702a80eSMichael Zhilin 			vg->es_fid = 0;
589*c702a80eSMichael Zhilin 		}
590*c702a80eSMichael Zhilin 	} else {
591*c702a80eSMichael Zhilin 		vg->es_fid = 0;
592*c702a80eSMichael Zhilin 	}
593*c702a80eSMichael Zhilin 
594*c702a80eSMichael Zhilin 	return (0);
595*c702a80eSMichael Zhilin }
596*c702a80eSMichael Zhilin 
597*c702a80eSMichael Zhilin static int
598*c702a80eSMichael Zhilin adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
599*c702a80eSMichael Zhilin {
600*c702a80eSMichael Zhilin 	struct adm6996fc_softc	*sc;
601*c702a80eSMichael Zhilin 	device_t		 parent;
602*c702a80eSMichael Zhilin 
603*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
604*c702a80eSMichael Zhilin 	parent = device_get_parent(dev);
605*c702a80eSMichael Zhilin 
606*c702a80eSMichael Zhilin 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
607*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
608*c702a80eSMichael Zhilin 		    vg->es_member_ports);
609*c702a80eSMichael Zhilin 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
610*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
611*c702a80eSMichael Zhilin 		    vg->es_member_ports | ((~vg->es_untagged_ports & 0x3f)<< 6));
612*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * vg->es_vlangroup,
613*c702a80eSMichael Zhilin 		    (1 << ADM6996FC_VV_SHIFT) | vg->es_vid);
614*c702a80eSMichael Zhilin 	}
615*c702a80eSMichael Zhilin 
616*c702a80eSMichael Zhilin 	return (0);
617*c702a80eSMichael Zhilin }
618*c702a80eSMichael Zhilin 
619*c702a80eSMichael Zhilin static int
620*c702a80eSMichael Zhilin adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf)
621*c702a80eSMichael Zhilin {
622*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
623*c702a80eSMichael Zhilin 
624*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
625*c702a80eSMichael Zhilin 
626*c702a80eSMichael Zhilin 	/* Return the VLAN mode. */
627*c702a80eSMichael Zhilin 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
628*c702a80eSMichael Zhilin 	conf->vlan_mode = sc->vlan_mode;
629*c702a80eSMichael Zhilin 
630*c702a80eSMichael Zhilin 	return (0);
631*c702a80eSMichael Zhilin }
632*c702a80eSMichael Zhilin 
633*c702a80eSMichael Zhilin static int
634*c702a80eSMichael Zhilin adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf)
635*c702a80eSMichael Zhilin {
636*c702a80eSMichael Zhilin 	struct adm6996fc_softc	*sc;
637*c702a80eSMichael Zhilin 	device_t		 parent;
638*c702a80eSMichael Zhilin 	int 			 i;
639*c702a80eSMichael Zhilin 	int 			 data;
640*c702a80eSMichael Zhilin 	int	bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
641*c702a80eSMichael Zhilin 
642*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
643*c702a80eSMichael Zhilin 	parent = device_get_parent(dev);
644*c702a80eSMichael Zhilin 
645*c702a80eSMichael Zhilin 	if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
646*c702a80eSMichael Zhilin 		return (0);
647*c702a80eSMichael Zhilin 
648*c702a80eSMichael Zhilin 	if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) {
649*c702a80eSMichael Zhilin 		sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
650*c702a80eSMichael Zhilin 		data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
651*c702a80eSMichael Zhilin 		data &= ~(1 << ADM6996FC_TBV_SHIFT);
652*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
653*c702a80eSMichael Zhilin 		for (i = 0;i <= 5; ++i) {
654*c702a80eSMichael Zhilin 			data = ADM6996FC_READREG(parent, bcaddr[i]);
655*c702a80eSMichael Zhilin 			data &= ~(0xf << 10);
656*c702a80eSMichael Zhilin 			data |= (i << 10);
657*c702a80eSMichael Zhilin 			ADM6996FC_WRITEREG(parent, bcaddr[i], data);
658*c702a80eSMichael Zhilin 			ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i,
659*c702a80eSMichael Zhilin 			    0x003f);
660*c702a80eSMichael Zhilin 			ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
661*c702a80eSMichael Zhilin 			    (1 << ADM6996FC_VV_SHIFT) | 1);
662*c702a80eSMichael Zhilin 		}
663*c702a80eSMichael Zhilin 	} else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
664*c702a80eSMichael Zhilin 		sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
665*c702a80eSMichael Zhilin 		data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
666*c702a80eSMichael Zhilin 		data |= (1 << ADM6996FC_TBV_SHIFT);
667*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
668*c702a80eSMichael Zhilin 		for (i = 0;i <= 5; ++i) {
669*c702a80eSMichael Zhilin 			data = ADM6996FC_READREG(parent, bcaddr[i]);
670*c702a80eSMichael Zhilin 			/* Private VID set 1 */
671*c702a80eSMichael Zhilin 			data &= ~(0xf << 10);
672*c702a80eSMichael Zhilin 			data |= (1 << 10);
673*c702a80eSMichael Zhilin 			/* Output Packet Tagging Enable */
674*c702a80eSMichael Zhilin 			if (i == 5)
675*c702a80eSMichael Zhilin 				data |= (1 << 4);
676*c702a80eSMichael Zhilin 			ADM6996FC_WRITEREG(parent, bcaddr[i], data);
677*c702a80eSMichael Zhilin 		}
678*c702a80eSMichael Zhilin 		for (i = 2;i <= 15; ++i) {
679*c702a80eSMichael Zhilin 			ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
680*c702a80eSMichael Zhilin 			    0x0000);
681*c702a80eSMichael Zhilin 		}
682*c702a80eSMichael Zhilin 	} else {
683*c702a80eSMichael Zhilin 		/*
684*c702a80eSMichael Zhilin 		 ADM6996FC have no VLAN off. Then set Port base and
685*c702a80eSMichael Zhilin 		 add all port to member. Use VLAN Filter 1 is reset
686*c702a80eSMichael Zhilin 		 default.
687*c702a80eSMichael Zhilin 		 */
688*c702a80eSMichael Zhilin 		sc->vlan_mode = 0;
689*c702a80eSMichael Zhilin 		data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
690*c702a80eSMichael Zhilin 		data &= ~(1 << ADM6996FC_TBV_SHIFT);
691*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
692*c702a80eSMichael Zhilin 		for (i = 0;i <= 5; ++i) {
693*c702a80eSMichael Zhilin 			data = ADM6996FC_READREG(parent, bcaddr[i]);
694*c702a80eSMichael Zhilin 			data &= ~(0xf << 10);
695*c702a80eSMichael Zhilin 			data |= (1 << 10);
696*c702a80eSMichael Zhilin 			if (i == 5)
697*c702a80eSMichael Zhilin 				data &= ~(1 << 4);
698*c702a80eSMichael Zhilin 			ADM6996FC_WRITEREG(parent, bcaddr[i], data);
699*c702a80eSMichael Zhilin 		}
700*c702a80eSMichael Zhilin 		/* default setting */
701*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2, 0x003f);
702*c702a80eSMichael Zhilin 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2,
703*c702a80eSMichael Zhilin 		    (1 << ADM6996FC_VV_SHIFT) | 1);
704*c702a80eSMichael Zhilin 	}
705*c702a80eSMichael Zhilin 
706*c702a80eSMichael Zhilin 
707*c702a80eSMichael Zhilin 	return (0);
708*c702a80eSMichael Zhilin }
709*c702a80eSMichael Zhilin 
710*c702a80eSMichael Zhilin static void
711*c702a80eSMichael Zhilin adm6996fc_statchg(device_t dev)
712*c702a80eSMichael Zhilin {
713*c702a80eSMichael Zhilin 
714*c702a80eSMichael Zhilin 	DPRINTF(dev, "%s\n", __func__);
715*c702a80eSMichael Zhilin }
716*c702a80eSMichael Zhilin 
717*c702a80eSMichael Zhilin static int
718*c702a80eSMichael Zhilin adm6996fc_ifmedia_upd(struct ifnet *ifp)
719*c702a80eSMichael Zhilin {
720*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
721*c702a80eSMichael Zhilin 	struct mii_data *mii;
722*c702a80eSMichael Zhilin 
723*c702a80eSMichael Zhilin 	sc = ifp->if_softc;
724*c702a80eSMichael Zhilin 	mii = adm6996fc_miiforport(sc, ifp->if_dunit);
725*c702a80eSMichael Zhilin 
726*c702a80eSMichael Zhilin 	DPRINTF(sc->sc_dev, "%s\n", __func__);
727*c702a80eSMichael Zhilin 	if (mii == NULL)
728*c702a80eSMichael Zhilin 		return (ENXIO);
729*c702a80eSMichael Zhilin 	mii_mediachg(mii);
730*c702a80eSMichael Zhilin 	return (0);
731*c702a80eSMichael Zhilin }
732*c702a80eSMichael Zhilin 
733*c702a80eSMichael Zhilin static void
734*c702a80eSMichael Zhilin adm6996fc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
735*c702a80eSMichael Zhilin {
736*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
737*c702a80eSMichael Zhilin 	struct mii_data *mii;
738*c702a80eSMichael Zhilin 
739*c702a80eSMichael Zhilin 	sc = ifp->if_softc;
740*c702a80eSMichael Zhilin 	mii = adm6996fc_miiforport(sc, ifp->if_dunit);
741*c702a80eSMichael Zhilin 
742*c702a80eSMichael Zhilin 	DPRINTF(sc->sc_dev, "%s\n", __func__);
743*c702a80eSMichael Zhilin 
744*c702a80eSMichael Zhilin 	if (mii == NULL)
745*c702a80eSMichael Zhilin 		return;
746*c702a80eSMichael Zhilin 	mii_pollstat(mii);
747*c702a80eSMichael Zhilin 	ifmr->ifm_active = mii->mii_media_active;
748*c702a80eSMichael Zhilin 	ifmr->ifm_status = mii->mii_media_status;
749*c702a80eSMichael Zhilin }
750*c702a80eSMichael Zhilin 
751*c702a80eSMichael Zhilin static int
752*c702a80eSMichael Zhilin adm6996fc_readphy(device_t dev, int phy, int reg)
753*c702a80eSMichael Zhilin {
754*c702a80eSMichael Zhilin 	struct adm6996fc_softc	*sc;
755*c702a80eSMichael Zhilin 	int			 data;
756*c702a80eSMichael Zhilin 
757*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
758*c702a80eSMichael Zhilin 	ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
759*c702a80eSMichael Zhilin 
760*c702a80eSMichael Zhilin 	if (phy < 0 || phy >= 32)
761*c702a80eSMichael Zhilin 		return (ENXIO);
762*c702a80eSMichael Zhilin 	if (reg < 0 || reg >= 32)
763*c702a80eSMichael Zhilin 		return (ENXIO);
764*c702a80eSMichael Zhilin 
765*c702a80eSMichael Zhilin 	ADM6996FC_LOCK(sc);
766*c702a80eSMichael Zhilin 	data = ADM6996FC_READREG(device_get_parent(dev),
767*c702a80eSMichael Zhilin 	    (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg);
768*c702a80eSMichael Zhilin 	ADM6996FC_UNLOCK(sc);
769*c702a80eSMichael Zhilin 
770*c702a80eSMichael Zhilin 	return (data);
771*c702a80eSMichael Zhilin }
772*c702a80eSMichael Zhilin 
773*c702a80eSMichael Zhilin static int
774*c702a80eSMichael Zhilin adm6996fc_writephy(device_t dev, int phy, int reg, int data)
775*c702a80eSMichael Zhilin {
776*c702a80eSMichael Zhilin 	struct adm6996fc_softc *sc;
777*c702a80eSMichael Zhilin 	int err;
778*c702a80eSMichael Zhilin 
779*c702a80eSMichael Zhilin 	sc = device_get_softc(dev);
780*c702a80eSMichael Zhilin 	ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
781*c702a80eSMichael Zhilin 
782*c702a80eSMichael Zhilin 	if (phy < 0 || phy >= 32)
783*c702a80eSMichael Zhilin 		return (ENXIO);
784*c702a80eSMichael Zhilin 	if (reg < 0 || reg >= 32)
785*c702a80eSMichael Zhilin 		return (ENXIO);
786*c702a80eSMichael Zhilin 
787*c702a80eSMichael Zhilin 	ADM6996FC_LOCK(sc);
788*c702a80eSMichael Zhilin 	err = ADM6996FC_WRITEREG(device_get_parent(dev),
789*c702a80eSMichael Zhilin 	    (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg, data);
790*c702a80eSMichael Zhilin 	ADM6996FC_UNLOCK(sc);
791*c702a80eSMichael Zhilin 
792*c702a80eSMichael Zhilin 	return (err);
793*c702a80eSMichael Zhilin }
794*c702a80eSMichael Zhilin 
795*c702a80eSMichael Zhilin static int
796*c702a80eSMichael Zhilin adm6996fc_readreg(device_t dev, int addr)
797*c702a80eSMichael Zhilin {
798*c702a80eSMichael Zhilin 
799*c702a80eSMichael Zhilin 	return ADM6996FC_READREG(device_get_parent(dev),  addr);
800*c702a80eSMichael Zhilin }
801*c702a80eSMichael Zhilin 
802*c702a80eSMichael Zhilin static int
803*c702a80eSMichael Zhilin adm6996fc_writereg(device_t dev, int addr, int value)
804*c702a80eSMichael Zhilin {
805*c702a80eSMichael Zhilin 	int err;
806*c702a80eSMichael Zhilin 
807*c702a80eSMichael Zhilin 	err = ADM6996FC_WRITEREG(device_get_parent(dev), addr, value);
808*c702a80eSMichael Zhilin 	return (err);
809*c702a80eSMichael Zhilin }
810*c702a80eSMichael Zhilin 
811*c702a80eSMichael Zhilin static device_method_t adm6996fc_methods[] = {
812*c702a80eSMichael Zhilin 	/* Device interface */
813*c702a80eSMichael Zhilin 	DEVMETHOD(device_probe,		adm6996fc_probe),
814*c702a80eSMichael Zhilin 	DEVMETHOD(device_attach,	adm6996fc_attach),
815*c702a80eSMichael Zhilin 	DEVMETHOD(device_detach,	adm6996fc_detach),
816*c702a80eSMichael Zhilin 
817*c702a80eSMichael Zhilin 	/* bus interface */
818*c702a80eSMichael Zhilin 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
819*c702a80eSMichael Zhilin 
820*c702a80eSMichael Zhilin 	/* MII interface */
821*c702a80eSMichael Zhilin 	DEVMETHOD(miibus_readreg,	adm6996fc_readphy),
822*c702a80eSMichael Zhilin 	DEVMETHOD(miibus_writereg,	adm6996fc_writephy),
823*c702a80eSMichael Zhilin 	DEVMETHOD(miibus_statchg,	adm6996fc_statchg),
824*c702a80eSMichael Zhilin 
825*c702a80eSMichael Zhilin 	/* MDIO interface */
826*c702a80eSMichael Zhilin 	DEVMETHOD(mdio_readreg,		adm6996fc_readphy),
827*c702a80eSMichael Zhilin 	DEVMETHOD(mdio_writereg,	adm6996fc_writephy),
828*c702a80eSMichael Zhilin 
829*c702a80eSMichael Zhilin 	/* etherswitch interface */
830*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_lock,	adm6996fc_lock),
831*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_unlock,	adm6996fc_unlock),
832*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_getinfo,	adm6996fc_getinfo),
833*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_readreg,	adm6996fc_readreg),
834*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_writereg,	adm6996fc_writereg),
835*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_readphyreg,	adm6996fc_readphy),
836*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_writephyreg,	adm6996fc_writephy),
837*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_getport,	adm6996fc_getport),
838*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_setport,	adm6996fc_setport),
839*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_getvgroup,	adm6996fc_getvgroup),
840*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_setvgroup,	adm6996fc_setvgroup),
841*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_setconf,	adm6996fc_setconf),
842*c702a80eSMichael Zhilin 	DEVMETHOD(etherswitch_getconf,	adm6996fc_getconf),
843*c702a80eSMichael Zhilin 
844*c702a80eSMichael Zhilin 	DEVMETHOD_END
845*c702a80eSMichael Zhilin };
846*c702a80eSMichael Zhilin 
847*c702a80eSMichael Zhilin DEFINE_CLASS_0(adm6996fc, adm6996fc_driver, adm6996fc_methods,
848*c702a80eSMichael Zhilin     sizeof(struct adm6996fc_softc));
849*c702a80eSMichael Zhilin static devclass_t adm6996fc_devclass;
850*c702a80eSMichael Zhilin 
851*c702a80eSMichael Zhilin DRIVER_MODULE(adm6996fc, mdio, adm6996fc_driver, adm6996fc_devclass, 0, 0);
852*c702a80eSMichael Zhilin DRIVER_MODULE(miibus, adm6996fc, miibus_driver, miibus_devclass, 0, 0);
853*c702a80eSMichael Zhilin DRIVER_MODULE(mdio, adm6996fc, mdio_driver, mdio_devclass, 0, 0);
854*c702a80eSMichael Zhilin DRIVER_MODULE(etherswitch, adm6996fc, etherswitch_driver, etherswitch_devclass,
855*c702a80eSMichael Zhilin     0, 0);
856*c702a80eSMichael Zhilin MODULE_VERSION(adm6996fc, 1);
857*c702a80eSMichael Zhilin MODULE_DEPEND(adm6996fc, miibus, 1, 1, 1); /* XXX which versions? */
858*c702a80eSMichael Zhilin MODULE_DEPEND(adm6996fc, etherswitch, 1, 1, 1); /* XXX which versions? */
859