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