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