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