xref: /freebsd/sys/dev/etherswitch/infineon/adm6996fc.c (revision 2e3f49888ec8851bafb22011533217487764fdb0)
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_copy(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 		if (sc->ifp[port] == NULL) {
177 			device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n");
178 			err = ENOMEM;
179 			break;
180 		}
181 
182 		sc->ifp[port]->if_softc = sc;
183 		sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
184 		    IFF_DRV_RUNNING | IFF_SIMPLEX;
185 		if_initname(sc->ifp[port], name, port);
186 		sc->miibus[port] = malloc(sizeof(device_t), M_ADM6996FC,
187 		    M_WAITOK | M_ZERO);
188 		if (sc->miibus[port] == NULL) {
189 			err = ENOMEM;
190 			goto failed;
191 		}
192 		err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
193 		    adm6996fc_ifmedia_upd, adm6996fc_ifmedia_sts, \
194 		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
195 		DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
196 		    device_get_nameunit(*sc->miibus[port]),
197 		    sc->ifp[port]->if_xname);
198 		if (err != 0) {
199 			device_printf(sc->sc_dev,
200 			    "attaching PHY %d failed\n",
201 			    phy);
202 			goto failed;
203 		}
204 		++port;
205 	}
206 	sc->info.es_nports = port;
207 	if (sc->cpuport != -1) {
208 		/* assume cpuport is last one */
209 		sc->ifpport[sc->cpuport] = port;
210 		sc->portphy[port] = sc->cpuport;
211 		++sc->info.es_nports;
212 	}
213 	return (0);
214 
215 failed:
216 	for (phy = 0; phy < sc->numports; phy++) {
217 		if (((1 << phy) & sc->phymask) == 0)
218 			continue;
219 		port = adm6996fc_portforphy(sc, phy);
220 		if (sc->miibus[port] != NULL)
221 			device_delete_child(sc->sc_dev, (*sc->miibus[port]));
222 		if (sc->ifp[port] != NULL)
223 			if_free(sc->ifp[port]);
224 		if (sc->ifname[port] != NULL)
225 			free(sc->ifname[port], M_ADM6996FC);
226 		if (sc->miibus[port] != NULL)
227 			free(sc->miibus[port], M_ADM6996FC);
228 	}
229 	return (err);
230 }
231 
232 static int
233 adm6996fc_attach(device_t dev)
234 {
235 	struct adm6996fc_softc	*sc;
236 	int			 err;
237 
238 	err = 0;
239 	sc = device_get_softc(dev);
240 
241 	sc->sc_dev = dev;
242 	mtx_init(&sc->sc_mtx, "adm6996fc", NULL, MTX_DEF);
243 	strlcpy(sc->info.es_name, device_get_desc(dev),
244 	    sizeof(sc->info.es_name));
245 
246 	/* ADM6996FC Defaults */
247 	sc->numports = 6;
248 	sc->phymask = 0x1f;
249 	sc->cpuport = 5;
250 	sc->media = 100;
251 
252 	sc->info.es_nvlangroups = 16;
253 	sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
254 
255 	sc->ifp = malloc(sizeof(if_t) * sc->numports, M_ADM6996FC,
256 	    M_WAITOK | M_ZERO);
257 	sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC,
258 	    M_WAITOK | M_ZERO);
259 	sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC,
260 	    M_WAITOK | M_ZERO);
261 	sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC,
262 	    M_WAITOK | M_ZERO);
263 
264 	if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL ||
265 	    sc->portphy == NULL) {
266 		err = ENOMEM;
267 		goto failed;
268 	}
269 
270 	/*
271 	 * Attach the PHYs and complete the bus enumeration.
272 	 */
273 	err = adm6996fc_attach_phys(sc);
274 	if (err != 0)
275 		goto failed;
276 
277 	bus_generic_probe(dev);
278 	bus_enumerate_hinted_children(dev);
279 	err = bus_generic_attach(dev);
280 	if (err != 0)
281 		goto failed;
282 
283 	callout_init(&sc->callout_tick, 0);
284 
285 	adm6996fc_tick(sc);
286 
287 	return (0);
288 
289 failed:
290 	if (sc->portphy != NULL)
291 		free(sc->portphy, M_ADM6996FC);
292 	if (sc->miibus != NULL)
293 		free(sc->miibus, M_ADM6996FC);
294 	if (sc->ifname != NULL)
295 		free(sc->ifname, M_ADM6996FC);
296 	if (sc->ifp != NULL)
297 		free(sc->ifp, M_ADM6996FC);
298 
299 	return (err);
300 }
301 
302 static int
303 adm6996fc_detach(device_t dev)
304 {
305 	struct adm6996fc_softc	*sc;
306 	int			 i, port;
307 
308 	sc = device_get_softc(dev);
309 
310 	callout_drain(&sc->callout_tick);
311 
312 	for (i = 0; i < MII_NPHY; i++) {
313 		if (((1 << i) & sc->phymask) == 0)
314 			continue;
315 		port = adm6996fc_portforphy(sc, i);
316 		if (sc->miibus[port] != NULL)
317 			device_delete_child(dev, (*sc->miibus[port]));
318 		if (sc->ifp[port] != NULL)
319 			if_free(sc->ifp[port]);
320 		free(sc->ifname[port], M_ADM6996FC);
321 		free(sc->miibus[port], M_ADM6996FC);
322 	}
323 
324 	free(sc->portphy, M_ADM6996FC);
325 	free(sc->miibus, M_ADM6996FC);
326 	free(sc->ifname, M_ADM6996FC);
327 	free(sc->ifp, M_ADM6996FC);
328 
329 	bus_generic_detach(dev);
330 	mtx_destroy(&sc->sc_mtx);
331 
332 	return (0);
333 }
334 
335 /*
336  * Convert PHY number to port number.
337  */
338 static inline int
339 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy)
340 {
341 
342 	return (sc->ifpport[phy]);
343 }
344 
345 static inline struct mii_data *
346 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port)
347 {
348 
349 	if (port < 0 || port > sc->numports)
350 		return (NULL);
351 	if (port == sc->cpuport)
352 		return (NULL);
353 	return (device_get_softc(*sc->miibus[port]));
354 }
355 
356 static inline if_t
357 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port)
358 {
359 
360 	if (port < 0 || port > sc->numports)
361 		return (NULL);
362 	return (sc->ifp[port]);
363 }
364 
365 /*
366  * Poll the status for all PHYs.
367  */
368 static void
369 adm6996fc_miipollstat(struct adm6996fc_softc *sc)
370 {
371 	int i, port;
372 	struct mii_data *mii;
373 	struct mii_softc *miisc;
374 
375 	ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
376 
377 	for (i = 0; i < MII_NPHY; i++) {
378 		if (((1 << i) & sc->phymask) == 0)
379 			continue;
380 		port = adm6996fc_portforphy(sc, i);
381 		if ((*sc->miibus[port]) == NULL)
382 			continue;
383 		mii = device_get_softc(*sc->miibus[port]);
384 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
385 			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
386 			    miisc->mii_inst)
387 				continue;
388 			ukphy_status(miisc);
389 			mii_phy_update(miisc, MII_POLLSTAT);
390 		}
391 	}
392 }
393 
394 static void
395 adm6996fc_tick(void *arg)
396 {
397 	struct adm6996fc_softc *sc;
398 
399 	sc = arg;
400 
401 	adm6996fc_miipollstat(sc);
402 	callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc);
403 }
404 
405 static void
406 adm6996fc_lock(device_t dev)
407 {
408 	struct adm6996fc_softc *sc;
409 
410 	sc = device_get_softc(dev);
411 
412 	ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
413 	ADM6996FC_LOCK(sc);
414 }
415 
416 static void
417 adm6996fc_unlock(device_t dev)
418 {
419 	struct adm6996fc_softc *sc;
420 
421 	sc = device_get_softc(dev);
422 
423 	ADM6996FC_LOCK_ASSERT(sc, MA_OWNED);
424 	ADM6996FC_UNLOCK(sc);
425 }
426 
427 static etherswitch_info_t *
428 adm6996fc_getinfo(device_t dev)
429 {
430 	struct adm6996fc_softc *sc;
431 
432 	sc = device_get_softc(dev);
433 
434 	return (&sc->info);
435 }
436 
437 static int
438 adm6996fc_getport(device_t dev, etherswitch_port_t *p)
439 {
440 	struct adm6996fc_softc	*sc;
441 	struct mii_data		*mii;
442 	struct ifmediareq	*ifmr;
443 	device_t		 parent;
444 	int 			 err, phy;
445 	int			 data1, data2;
446 
447 	int	bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
448 	int	vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
449 
450 	sc = device_get_softc(dev);
451 	ifmr = &p->es_ifmr;
452 
453 	if (p->es_port < 0 || p->es_port >= sc->numports)
454 		return (ENXIO);
455 
456 	parent = device_get_parent(dev);
457 
458 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
459 		data1 = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
460 		data2 = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
461 		/* only port 4 is hi bit */
462 		if (p->es_port == 4)
463 			data2 = (data2 >> 8) & 0xff;
464 		else
465 			data2 = data2 & 0xff;
466 
467 		p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2);
468 		if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1)
469 			p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
470 	} else {
471 		p->es_pvid = 0;
472 	}
473 
474 	phy = sc->portphy[p->es_port];
475 	mii = adm6996fc_miiforport(sc, p->es_port);
476 	if (sc->cpuport != -1 && phy == sc->cpuport) {
477 		/* fill in fixed values for CPU port */
478 		p->es_flags |= ETHERSWITCH_PORT_CPU;
479 		ifmr->ifm_count = 0;
480 		if (sc->media == 100)
481 			ifmr->ifm_current = ifmr->ifm_active =
482 			    IFM_ETHER | IFM_100_TX | IFM_FDX;
483 		else
484 			ifmr->ifm_current = ifmr->ifm_active =
485 			    IFM_ETHER | IFM_1000_T | IFM_FDX;
486 		ifmr->ifm_mask = 0;
487 		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
488 	} else if (mii != NULL) {
489 		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
490 		    &mii->mii_media, SIOCGIFMEDIA);
491 		if (err)
492 			return (err);
493 	} else {
494 		return (ENXIO);
495 	}
496 	return (0);
497 }
498 
499 static int
500 adm6996fc_setport(device_t dev, etherswitch_port_t *p)
501 {
502 	struct adm6996fc_softc	*sc;
503 	struct ifmedia		*ifm;
504 	struct mii_data		*mii;
505 	if_t ifp;
506 	device_t		 parent;
507 	int 			 err;
508 	int			 data;
509 
510 	int	bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
511 	int	vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
512 
513 	sc = device_get_softc(dev);
514 	parent = device_get_parent(dev);
515 
516 	if (p->es_port < 0 || p->es_port >= sc->numports)
517 		return (ENXIO);
518 
519 	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
520 		data = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
521 		data &= ~(0xf << 10);
522 		data |= (p->es_pvid & 0xf) << ADM6996FC_PVID_SHIFT;
523 		if (p->es_flags & ETHERSWITCH_PORT_ADDTAG)
524 			data |= 1 << ADM6996FC_OPTE_SHIFT;
525 		else
526 			data &= ~(1 << ADM6996FC_OPTE_SHIFT);
527 		ADM6996FC_WRITEREG(parent, bcaddr[p->es_port], data);
528 		data = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
529 		/* only port 4 is hi bit */
530 		if (p->es_port == 4) {
531 			data &= ~(0xff << 8);
532 			data = data | (((p->es_pvid >> 4) & 0xff) << 8);
533 		} else {
534 			data &= ~0xff;
535 			data = data | ((p->es_pvid >> 4) & 0xff);
536 		}
537 		ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data);
538 		err = 0;
539 	} else {
540 		if (sc->portphy[p->es_port] == sc->cpuport)
541 			return (ENXIO);
542 	}
543 
544 	if (sc->portphy[p->es_port] != sc->cpuport) {
545 		mii = adm6996fc_miiforport(sc, p->es_port);
546 		if (mii == NULL)
547 			return (ENXIO);
548 
549 		ifp = adm6996fc_ifpforport(sc, p->es_port);
550 
551 		ifm = &mii->mii_media;
552 		err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
553 	}
554 	return (err);
555 }
556 
557 static int
558 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
559 {
560 	struct adm6996fc_softc	*sc;
561 	device_t		 parent;
562 	int			 datahi, datalo;
563 
564 	sc = device_get_softc(dev);
565 	parent = device_get_parent(dev);
566 
567 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
568 		if (vg->es_vlangroup <= 5) {
569 			vg->es_vid = ETHERSWITCH_VID_VALID;
570 			vg->es_vid |= vg->es_vlangroup;
571 			datalo = ADM6996FC_READREG(parent,
572 			    ADM6996FC_VF0L + 2 * vg->es_vlangroup);
573 			datahi = ADM6996FC_READREG(parent,
574 			    ADM6996FC_VF0H + 2 * vg->es_vlangroup);
575 
576 			vg->es_member_ports = datalo & 0x3f;
577 			vg->es_untagged_ports = vg->es_member_ports;
578 			vg->es_fid = 0;
579 		} else {
580 			vg->es_vid = 0;
581 		}
582 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
583 		datalo = ADM6996FC_READREG(parent,
584 		    ADM6996FC_VF0L + 2 * vg->es_vlangroup);
585 		datahi = ADM6996FC_READREG(parent,
586 		    ADM6996FC_VF0H + 2 * vg->es_vlangroup);
587 
588 		if (datahi & (1 << ADM6996FC_VV_SHIFT)) {
589 			vg->es_vid = ETHERSWITCH_VID_VALID;
590 			vg->es_vid |= datahi & 0xfff;
591 			vg->es_member_ports = datalo & 0x3f;
592 			vg->es_untagged_ports = (~datalo >> 6) & 0x3f;
593 			vg->es_fid = 0;
594 		} else {
595 			vg->es_fid = 0;
596 		}
597 	} else {
598 		vg->es_fid = 0;
599 	}
600 
601 	return (0);
602 }
603 
604 static int
605 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
606 {
607 	struct adm6996fc_softc	*sc;
608 	device_t		 parent;
609 
610 	sc = device_get_softc(dev);
611 	parent = device_get_parent(dev);
612 
613 	if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
614 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
615 		    vg->es_member_ports);
616 	} else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
617 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
618 		    vg->es_member_ports | ((~vg->es_untagged_ports & 0x3f)<< 6));
619 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * vg->es_vlangroup,
620 		    (1 << ADM6996FC_VV_SHIFT) | vg->es_vid);
621 	}
622 
623 	return (0);
624 }
625 
626 static int
627 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf)
628 {
629 	struct adm6996fc_softc *sc;
630 
631 	sc = device_get_softc(dev);
632 
633 	/* Return the VLAN mode. */
634 	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
635 	conf->vlan_mode = sc->vlan_mode;
636 
637 	return (0);
638 }
639 
640 static int
641 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf)
642 {
643 	struct adm6996fc_softc	*sc;
644 	device_t		 parent;
645 	int 			 i;
646 	int 			 data;
647 	int	bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
648 
649 	sc = device_get_softc(dev);
650 	parent = device_get_parent(dev);
651 
652 	if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
653 		return (0);
654 
655 	if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) {
656 		sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
657 		data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
658 		data &= ~(1 << ADM6996FC_TBV_SHIFT);
659 		ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
660 		for (i = 0;i <= 5; ++i) {
661 			data = ADM6996FC_READREG(parent, bcaddr[i]);
662 			data &= ~(0xf << 10);
663 			data |= (i << 10);
664 			ADM6996FC_WRITEREG(parent, bcaddr[i], data);
665 			ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i,
666 			    0x003f);
667 			ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
668 			    (1 << ADM6996FC_VV_SHIFT) | 1);
669 		}
670 	} else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
671 		sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
672 		data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
673 		data |= (1 << ADM6996FC_TBV_SHIFT);
674 		ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
675 		for (i = 0;i <= 5; ++i) {
676 			data = ADM6996FC_READREG(parent, bcaddr[i]);
677 			/* Private VID set 1 */
678 			data &= ~(0xf << 10);
679 			data |= (1 << 10);
680 			ADM6996FC_WRITEREG(parent, bcaddr[i], data);
681 		}
682 		for (i = 2;i <= 15; ++i) {
683 			ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
684 			    0x0000);
685 		}
686 	} else {
687 		/*
688 		 ADM6996FC have no VLAN off. Then set Port base and
689 		 add all port to member. Use VLAN Filter 1 is reset
690 		 default.
691 		 */
692 		sc->vlan_mode = 0;
693 		data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
694 		data &= ~(1 << ADM6996FC_TBV_SHIFT);
695 		ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
696 		for (i = 0;i <= 5; ++i) {
697 			data = ADM6996FC_READREG(parent, bcaddr[i]);
698 			data &= ~(0xf << 10);
699 			data |= (1 << 10);
700 			if (i == 5)
701 				data &= ~(1 << 4);
702 			ADM6996FC_WRITEREG(parent, bcaddr[i], data);
703 		}
704 		/* default setting */
705 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2, 0x003f);
706 		ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2,
707 		    (1 << ADM6996FC_VV_SHIFT) | 1);
708 	}
709 
710 
711 	return (0);
712 }
713 
714 static void
715 adm6996fc_statchg(device_t dev)
716 {
717 
718 	DPRINTF(dev, "%s\n", __func__);
719 }
720 
721 static int
722 adm6996fc_ifmedia_upd(if_t ifp)
723 {
724 	struct adm6996fc_softc *sc;
725 	struct mii_data *mii;
726 
727 	sc = if_getsoftc(ifp);
728 	mii = adm6996fc_miiforport(sc, if_getdunit(ifp));
729 
730 	DPRINTF(sc->sc_dev, "%s\n", __func__);
731 	if (mii == NULL)
732 		return (ENXIO);
733 	mii_mediachg(mii);
734 	return (0);
735 }
736 
737 static void
738 adm6996fc_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
739 {
740 	struct adm6996fc_softc *sc;
741 	struct mii_data *mii;
742 
743 	sc = if_getsoftc(ifp);
744 	mii = adm6996fc_miiforport(sc, if_getdunit(ifp));
745 
746 	DPRINTF(sc->sc_dev, "%s\n", __func__);
747 
748 	if (mii == NULL)
749 		return;
750 	mii_pollstat(mii);
751 	ifmr->ifm_active = mii->mii_media_active;
752 	ifmr->ifm_status = mii->mii_media_status;
753 }
754 
755 static int
756 adm6996fc_readphy(device_t dev, int phy, int reg)
757 {
758 	struct adm6996fc_softc	*sc;
759 	int			 data;
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 	data = ADM6996FC_READREG(device_get_parent(dev),
771 	    (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg);
772 	ADM6996FC_UNLOCK(sc);
773 
774 	return (data);
775 }
776 
777 static int
778 adm6996fc_writephy(device_t dev, int phy, int reg, int data)
779 {
780 	struct adm6996fc_softc *sc;
781 	int err;
782 
783 	sc = device_get_softc(dev);
784 	ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
785 
786 	if (phy < 0 || phy >= 32)
787 		return (ENXIO);
788 	if (reg < 0 || reg >= 32)
789 		return (ENXIO);
790 
791 	ADM6996FC_LOCK(sc);
792 	err = ADM6996FC_WRITEREG(device_get_parent(dev),
793 	    (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg, data);
794 	ADM6996FC_UNLOCK(sc);
795 
796 	return (err);
797 }
798 
799 static int
800 adm6996fc_readreg(device_t dev, int addr)
801 {
802 
803 	return ADM6996FC_READREG(device_get_parent(dev),  addr);
804 }
805 
806 static int
807 adm6996fc_writereg(device_t dev, int addr, int value)
808 {
809 	int err;
810 
811 	err = ADM6996FC_WRITEREG(device_get_parent(dev), addr, value);
812 	return (err);
813 }
814 
815 static device_method_t adm6996fc_methods[] = {
816 	/* Device interface */
817 	DEVMETHOD(device_probe,		adm6996fc_probe),
818 	DEVMETHOD(device_attach,	adm6996fc_attach),
819 	DEVMETHOD(device_detach,	adm6996fc_detach),
820 
821 	/* bus interface */
822 	DEVMETHOD(bus_add_child,	device_add_child_ordered),
823 
824 	/* MII interface */
825 	DEVMETHOD(miibus_readreg,	adm6996fc_readphy),
826 	DEVMETHOD(miibus_writereg,	adm6996fc_writephy),
827 	DEVMETHOD(miibus_statchg,	adm6996fc_statchg),
828 
829 	/* MDIO interface */
830 	DEVMETHOD(mdio_readreg,		adm6996fc_readphy),
831 	DEVMETHOD(mdio_writereg,	adm6996fc_writephy),
832 
833 	/* etherswitch interface */
834 	DEVMETHOD(etherswitch_lock,	adm6996fc_lock),
835 	DEVMETHOD(etherswitch_unlock,	adm6996fc_unlock),
836 	DEVMETHOD(etherswitch_getinfo,	adm6996fc_getinfo),
837 	DEVMETHOD(etherswitch_readreg,	adm6996fc_readreg),
838 	DEVMETHOD(etherswitch_writereg,	adm6996fc_writereg),
839 	DEVMETHOD(etherswitch_readphyreg,	adm6996fc_readphy),
840 	DEVMETHOD(etherswitch_writephyreg,	adm6996fc_writephy),
841 	DEVMETHOD(etherswitch_getport,	adm6996fc_getport),
842 	DEVMETHOD(etherswitch_setport,	adm6996fc_setport),
843 	DEVMETHOD(etherswitch_getvgroup,	adm6996fc_getvgroup),
844 	DEVMETHOD(etherswitch_setvgroup,	adm6996fc_setvgroup),
845 	DEVMETHOD(etherswitch_setconf,	adm6996fc_setconf),
846 	DEVMETHOD(etherswitch_getconf,	adm6996fc_getconf),
847 
848 	DEVMETHOD_END
849 };
850 
851 DEFINE_CLASS_0(adm6996fc, adm6996fc_driver, adm6996fc_methods,
852     sizeof(struct adm6996fc_softc));
853 
854 DRIVER_MODULE(adm6996fc, mdio, adm6996fc_driver, 0, 0);
855 DRIVER_MODULE(miibus, adm6996fc, miibus_driver, 0, 0);
856 DRIVER_MODULE(mdio, adm6996fc, mdio_driver, 0, 0);
857 DRIVER_MODULE(etherswitch, adm6996fc, etherswitch_driver, 0, 0);
858 MODULE_VERSION(adm6996fc, 1);
859 MODULE_DEPEND(adm6996fc, miibus, 1, 1, 1); /* XXX which versions? */
860 MODULE_DEPEND(adm6996fc, etherswitch, 1, 1, 1); /* XXX which versions? */
861