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