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