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