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