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