]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/etherswitch/infineon/adm6996fc.c
Merge llvm trunk r300422 and resolve conflicts.
[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  */
39
40 #include <sys/param.h>
41 #include <sys/bus.h>
42 #include <sys/errno.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/malloc.h>
46 #include <sys/module.h>
47 #include <sys/mutex.h>
48 #include <sys/socket.h>
49 #include <sys/sockio.h>
50 #include <sys/sysctl.h>
51 #include <sys/systm.h>
52
53 #include <net/if.h>
54 #include <net/if_var.h>
55 #include <net/ethernet.h>
56 #include <net/if_media.h>
57 #include <net/if_types.h>
58
59 #include <machine/bus.h>
60 #include <dev/mii/mii.h>
61 #include <dev/mii/miivar.h>
62 #include <dev/mdio/mdio.h>
63
64 #include <dev/etherswitch/etherswitch.h>
65
66 #include "mdio_if.h"
67 #include "miibus_if.h"
68 #include "etherswitch_if.h"
69
70 #define ADM6996FC_PRODUCT_CODE  0x7102
71
72 #define ADM6996FC_SC3           0x11
73 #define ADM6996FC_VF0L          0x40
74 #define ADM6996FC_VF0H          0x41
75 #define ADM6996FC_CI0           0xa0
76 #define ADM6996FC_CI1           0xa1
77 #define ADM6996FC_PHY_C0        0x200
78
79 #define ADM6996FC_PC_SHIFT      4
80 #define ADM6996FC_TBV_SHIFT     5
81 #define ADM6996FC_PVID_SHIFT    10
82 #define ADM6996FC_OPTE_SHIFT    4
83 #define ADM6996FC_VV_SHIFT      15
84
85 #define ADM6996FC_PHY_SIZE      0x20
86
87 MALLOC_DECLARE(M_ADM6996FC);
88 MALLOC_DEFINE(M_ADM6996FC, "adm6996fc", "adm6996fc data structures");
89
90 struct adm6996fc_softc {
91         struct mtx      sc_mtx;         /* serialize access to softc */
92         device_t        sc_dev;
93         int             vlan_mode;
94         int             media;          /* cpu port media */
95         int             cpuport;        /* which PHY is connected to the CPU */
96         int             phymask;        /* PHYs we manage */
97         int             numports;       /* number of ports */
98         int             ifpport[MII_NPHY];
99         int             *portphy;
100         char            **ifname;
101         device_t        **miibus;
102         struct ifnet    **ifp;
103         struct callout  callout_tick;
104         etherswitch_info_t      info;
105 };
106
107 #define ADM6996FC_LOCK(_sc)                     \
108             mtx_lock(&(_sc)->sc_mtx)
109 #define ADM6996FC_UNLOCK(_sc)                   \
110             mtx_unlock(&(_sc)->sc_mtx)
111 #define ADM6996FC_LOCK_ASSERT(_sc, _what)       \
112             mtx_assert(&(_sc)->sc_mtx, (_what))
113 #define ADM6996FC_TRYLOCK(_sc)                  \
114             mtx_trylock(&(_sc)->sc_mtx)
115
116 #if defined(DEBUG)
117 #define DPRINTF(dev, args...) device_printf(dev, args)
118 #else
119 #define DPRINTF(dev, args...)
120 #endif
121
122 static inline int adm6996fc_portforphy(struct adm6996fc_softc *, int);
123 static void adm6996fc_tick(void *);
124 static int adm6996fc_ifmedia_upd(struct ifnet *);
125 static void adm6996fc_ifmedia_sts(struct ifnet *, struct ifmediareq *);
126
127 #define ADM6996FC_READREG(dev, x)                                       \
128         MDIO_READREG(dev, ((x) >> 5), ((x) & 0x1f));
129 #define ADM6996FC_WRITEREG(dev, x, v)                                   \
130         MDIO_WRITEREG(dev, ((x) >> 5), ((x) & 0x1f), v);
131
132 #define ADM6996FC_PVIDBYDATA(data1, data2)                              \
133         ((((data1) >> ADM6996FC_PVID_SHIFT) & 0x0f) | ((data2) << 4))
134
135 static int
136 adm6996fc_probe(device_t dev)
137 {
138         int data1, data2;
139         int pc;
140         struct adm6996fc_softc *sc;
141
142         sc = device_get_softc(dev);
143         bzero(sc, sizeof(*sc));
144
145         data1 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI0);
146         data2 = ADM6996FC_READREG(device_get_parent(dev), ADM6996FC_CI1);
147         pc = ((data2 << 16) | data1) >> ADM6996FC_PC_SHIFT;
148         if (bootverbose)
149                 device_printf(dev,"Chip Identifier Register %x %x\n", data1,
150                     data2);
151
152         /* check Product Code */
153         if (pc != ADM6996FC_PRODUCT_CODE) {
154                 return (ENXIO);
155         }
156
157         device_set_desc_copy(dev, "Infineon ADM6996FC/M/MX MDIO switch driver");
158         return (BUS_PROBE_DEFAULT);
159 }
160
161 static int
162 adm6996fc_attach_phys(struct adm6996fc_softc *sc)
163 {
164         int phy, port, err;
165         char name[IFNAMSIZ];
166
167         port = 0;
168         err = 0;
169         /* PHYs need an interface, so we generate a dummy one */
170         snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
171         for (phy = 0; phy < sc->numports; phy++) {
172                 if (((1 << phy) & sc->phymask) == 0)
173                         continue;
174                 sc->ifpport[phy] = port;
175                 sc->portphy[port] = phy;
176                 sc->ifp[port] = if_alloc(IFT_ETHER);
177                 sc->ifp[port]->if_softc = sc;
178                 sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST |
179                     IFF_DRV_RUNNING | IFF_SIMPLEX;
180                 if_initname(sc->ifp[port], name, port);
181                 sc->miibus[port] = malloc(sizeof(device_t), M_ADM6996FC,
182                     M_WAITOK | M_ZERO);
183                 if (sc->miibus[port] == NULL) {
184                         err = ENOMEM;
185                         goto failed;
186                 }
187                 err = mii_attach(sc->sc_dev, sc->miibus[port], sc->ifp[port],
188                     adm6996fc_ifmedia_upd, adm6996fc_ifmedia_sts, \
189                     BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
190                 DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n",
191                     device_get_nameunit(*sc->miibus[port]),
192                     sc->ifp[port]->if_xname);
193                 if (err != 0) {
194                         device_printf(sc->sc_dev,
195                             "attaching PHY %d failed\n",
196                             phy);
197                         goto failed;
198                 }
199                 ++port;
200         }
201         sc->info.es_nports = port;
202         if (sc->cpuport != -1) {
203                 /* assume cpuport is last one */
204                 sc->ifpport[sc->cpuport] = port;
205                 sc->portphy[port] = sc->cpuport;
206                 ++sc->info.es_nports;
207         }
208         return (0);
209
210 failed:
211         for (phy = 0; phy < sc->numports; phy++) {
212                 if (((1 << phy) & sc->phymask) == 0)
213                         continue;
214                 port = adm6996fc_portforphy(sc, phy);
215                 if (sc->miibus[port] != NULL)
216                         device_delete_child(sc->sc_dev, (*sc->miibus[port]));
217                 if (sc->ifp[port] != NULL)
218                         if_free(sc->ifp[port]);
219                 if (sc->ifname[port] != NULL)
220                         free(sc->ifname[port], M_ADM6996FC);
221                 if (sc->miibus[port] != NULL)
222                         free(sc->miibus[port], M_ADM6996FC);
223         }
224         return (err);
225 }
226
227 static int
228 adm6996fc_attach(device_t dev)
229 {
230         struct adm6996fc_softc  *sc;
231         int                      err;
232
233         err = 0;
234         sc = device_get_softc(dev);
235
236         sc->sc_dev = dev;
237         mtx_init(&sc->sc_mtx, "adm6996fc", NULL, MTX_DEF);
238         strlcpy(sc->info.es_name, device_get_desc(dev),
239             sizeof(sc->info.es_name));
240
241         /* ADM6996FC Defaults */
242         sc->numports = 6;
243         sc->phymask = 0x1f;
244         sc->cpuport = 5;
245         sc->media = 100;
246
247         sc->info.es_nvlangroups = 16;
248         sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
249
250         sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_ADM6996FC,
251             M_WAITOK | M_ZERO);
252         sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC,
253             M_WAITOK | M_ZERO);
254         sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC,
255             M_WAITOK | M_ZERO);
256         sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC,
257             M_WAITOK | M_ZERO);
258
259         if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL ||
260             sc->portphy == NULL) {
261                 err = ENOMEM;
262                 goto failed;
263         }
264
265         /*
266          * Attach the PHYs and complete the bus enumeration.
267          */
268         err = adm6996fc_attach_phys(sc);
269         if (err != 0)
270                 goto failed;
271
272         bus_generic_probe(dev);
273         bus_enumerate_hinted_children(dev);
274         err = bus_generic_attach(dev);
275         if (err != 0)
276                 goto failed;
277         
278         callout_init(&sc->callout_tick, 0);
279
280         adm6996fc_tick(sc);
281         
282         return (0);
283
284 failed:
285         if (sc->portphy != NULL)
286                 free(sc->portphy, M_ADM6996FC);
287         if (sc->miibus != NULL)
288                 free(sc->miibus, M_ADM6996FC);
289         if (sc->ifname != NULL)
290                 free(sc->ifname, M_ADM6996FC);
291         if (sc->ifp != NULL)
292                 free(sc->ifp, M_ADM6996FC);
293
294         return (err);
295 }
296
297 static int
298 adm6996fc_detach(device_t dev)
299 {
300         struct adm6996fc_softc  *sc;
301         int                      i, port;
302
303         sc = device_get_softc(dev);
304
305         callout_drain(&sc->callout_tick);
306
307         for (i = 0; i < MII_NPHY; i++) {
308                 if (((1 << i) & sc->phymask) == 0)
309                         continue;
310                 port = adm6996fc_portforphy(sc, i);
311                 if (sc->miibus[port] != NULL)
312                         device_delete_child(dev, (*sc->miibus[port]));
313                 if (sc->ifp[port] != NULL)
314                         if_free(sc->ifp[port]);
315                 free(sc->ifname[port], M_ADM6996FC);
316                 free(sc->miibus[port], M_ADM6996FC);
317         }
318
319         free(sc->portphy, M_ADM6996FC);
320         free(sc->miibus, M_ADM6996FC);
321         free(sc->ifname, M_ADM6996FC);
322         free(sc->ifp, M_ADM6996FC);
323
324         bus_generic_detach(dev);
325         mtx_destroy(&sc->sc_mtx);
326
327         return (0);
328 }
329
330 /*
331  * Convert PHY number to port number.
332  */
333 static inline int
334 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy)
335 {
336
337         return (sc->ifpport[phy]);
338 }
339
340 static inline struct mii_data *
341 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port)
342 {
343
344         if (port < 0 || port > sc->numports)
345                 return (NULL);
346         if (port == sc->cpuport)
347                 return (NULL);
348         return (device_get_softc(*sc->miibus[port]));
349 }
350
351 static inline struct ifnet *
352 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port)
353 {
354
355         if (port < 0 || port > sc->numports)
356                 return (NULL);
357         return (sc->ifp[port]);
358 }
359
360 /*
361  * Poll the status for all PHYs.
362  */
363 static void
364 adm6996fc_miipollstat(struct adm6996fc_softc *sc)
365 {
366         int i, port;
367         struct mii_data *mii;
368         struct mii_softc *miisc;
369
370         ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
371
372         for (i = 0; i < MII_NPHY; i++) {
373                 if (((1 << i) & sc->phymask) == 0)
374                         continue;
375                 port = adm6996fc_portforphy(sc, i);
376                 if ((*sc->miibus[port]) == NULL)
377                         continue;
378                 mii = device_get_softc(*sc->miibus[port]);
379                 LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
380                         if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
381                             miisc->mii_inst)
382                                 continue;
383                         ukphy_status(miisc);
384                         mii_phy_update(miisc, MII_POLLSTAT);
385                 }
386         }
387 }
388
389 static void
390 adm6996fc_tick(void *arg)
391 {
392         struct adm6996fc_softc *sc;
393
394         sc = arg;
395
396         adm6996fc_miipollstat(sc);
397         callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc);
398 }
399
400 static void
401 adm6996fc_lock(device_t dev)
402 {
403         struct adm6996fc_softc *sc;
404
405         sc = device_get_softc(dev);
406
407         ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
408         ADM6996FC_LOCK(sc);
409 }
410
411 static void
412 adm6996fc_unlock(device_t dev)
413 {
414         struct adm6996fc_softc *sc;
415
416         sc = device_get_softc(dev);
417
418         ADM6996FC_LOCK_ASSERT(sc, MA_OWNED);
419         ADM6996FC_UNLOCK(sc);
420 }
421
422 static etherswitch_info_t *
423 adm6996fc_getinfo(device_t dev)
424 {
425         struct adm6996fc_softc *sc;
426
427         sc = device_get_softc(dev);
428         
429         return (&sc->info);
430 }
431
432 static int
433 adm6996fc_getport(device_t dev, etherswitch_port_t *p)
434 {
435         struct adm6996fc_softc  *sc;
436         struct mii_data         *mii;
437         struct ifmediareq       *ifmr;
438         device_t                 parent;
439         int                      err, phy;
440         int                      data1, data2;
441
442         int     bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
443         int     vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
444
445         sc = device_get_softc(dev);
446         ifmr = &p->es_ifmr;
447
448         if (p->es_port < 0 || p->es_port >= sc->numports)
449                 return (ENXIO);
450
451         parent = device_get_parent(dev);
452
453         if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
454                 data1 = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
455                 data2 = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
456                 /* only port 4 is hi bit */
457                 if (p->es_port == 4)
458                         data2 = (data2 >> 8) & 0xff;
459                 else
460                         data2 = data2 & 0xff;
461
462                 p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2);
463                 if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1)
464                         p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
465                 else
466                         p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
467         } else {
468                 p->es_pvid = 0;
469         }
470
471         phy = sc->portphy[p->es_port];
472         mii = adm6996fc_miiforport(sc, p->es_port);
473         if (sc->cpuport != -1 && phy == sc->cpuport) {
474                 /* fill in fixed values for CPU port */
475                 p->es_flags |= ETHERSWITCH_PORT_CPU;
476                 ifmr->ifm_count = 0;
477                 if (sc->media == 100)
478                         ifmr->ifm_current = ifmr->ifm_active =
479                             IFM_ETHER | IFM_100_TX | IFM_FDX;
480                 else
481                         ifmr->ifm_current = ifmr->ifm_active =
482                             IFM_ETHER | IFM_1000_T | IFM_FDX;
483                 ifmr->ifm_mask = 0;
484                 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
485         } else if (mii != NULL) {
486                 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
487                     &mii->mii_media, SIOCGIFMEDIA);
488                 if (err)
489                         return (err);
490         } else {
491                 return (ENXIO);
492         }
493         return (0);
494 }
495
496 static int
497 adm6996fc_setport(device_t dev, etherswitch_port_t *p)
498 {
499         struct adm6996fc_softc  *sc;
500         struct ifmedia          *ifm;
501         struct mii_data         *mii;
502         struct ifnet            *ifp;
503         device_t                 parent;
504         int                      err;
505         int                      data;
506
507         int     bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
508         int     vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
509
510         sc = device_get_softc(dev);
511         parent = device_get_parent(dev);
512
513         if (p->es_port < 0 || p->es_port >= sc->numports)
514                 return (ENXIO);
515
516         if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
517                 data = ADM6996FC_READREG(parent, bcaddr[p->es_port]);
518                 data &= ~(0xf << 10);
519                 data |= (p->es_pvid & 0xf) << ADM6996FC_PVID_SHIFT;
520                 ADM6996FC_WRITEREG(parent, bcaddr[p->es_port], data);
521                 data = ADM6996FC_READREG(parent, vidaddr[p->es_port]);
522                 /* only port 4 is hi bit */
523                 if (p->es_port == 4) {
524                         data &= ~(0xff << 8);
525                         data = data | (((p->es_pvid >> 4) & 0xff) << 8);
526                 } else {
527                         data &= ~0xff;
528                         data = data | ((p->es_pvid >> 4) & 0xff);
529                 }
530                 ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data);
531                 err = 0;
532         } else {
533                 if (sc->portphy[p->es_port] == sc->cpuport)
534                         return (ENXIO);
535         } 
536
537         if (sc->portphy[p->es_port] != sc->cpuport) {
538                 mii = adm6996fc_miiforport(sc, p->es_port);
539                 if (mii == NULL)
540                         return (ENXIO);
541
542                 ifp = adm6996fc_ifpforport(sc, p->es_port);
543
544                 ifm = &mii->mii_media;
545                 err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
546         }
547         return (err);
548 }
549
550 static int
551 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
552 {
553         struct adm6996fc_softc  *sc;
554         device_t                 parent;
555         int                      datahi, datalo;
556
557         sc = device_get_softc(dev);
558         parent = device_get_parent(dev);
559
560         if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
561                 if (vg->es_vlangroup <= 5) {
562                         vg->es_vid = ETHERSWITCH_VID_VALID;
563                         vg->es_vid |= vg->es_vlangroup;
564                         datalo = ADM6996FC_READREG(parent,
565                             ADM6996FC_VF0L + 2 * vg->es_vlangroup);
566                         datahi = ADM6996FC_READREG(parent,
567                             ADM6996FC_VF0H + 2 * vg->es_vlangroup);
568
569                         vg->es_member_ports = datalo & 0x3f;
570                         vg->es_untagged_ports = vg->es_member_ports;
571                         vg->es_fid = 0;
572                 } else {
573                         vg->es_vid = 0;
574                 }
575         } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
576                 datalo = ADM6996FC_READREG(parent,
577                     ADM6996FC_VF0L + 2 * vg->es_vlangroup);
578                 datahi = ADM6996FC_READREG(parent,
579                     ADM6996FC_VF0H + 2 * vg->es_vlangroup);
580                 
581                 if (datahi & (1 << ADM6996FC_VV_SHIFT)) {
582                         vg->es_vid = ETHERSWITCH_VID_VALID;
583                         vg->es_vid |= datahi & 0xfff;
584                         vg->es_member_ports = datalo & 0x3f;
585                         vg->es_untagged_ports = (~datalo >> 6) & 0x3f;
586                         vg->es_fid = 0;
587                 } else {
588                         vg->es_fid = 0;
589                 }
590         } else {
591                 vg->es_fid = 0;
592         }
593
594         return (0);
595 }
596
597 static int
598 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
599 {
600         struct adm6996fc_softc  *sc;
601         device_t                 parent;
602
603         sc = device_get_softc(dev);
604         parent = device_get_parent(dev);
605
606         if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) {
607                 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
608                     vg->es_member_ports);
609         } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
610                 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * vg->es_vlangroup,
611                     vg->es_member_ports | ((~vg->es_untagged_ports & 0x3f)<< 6));
612                 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * vg->es_vlangroup,
613                     (1 << ADM6996FC_VV_SHIFT) | vg->es_vid);
614         }
615
616         return (0);
617 }
618
619 static int
620 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf)
621 {
622         struct adm6996fc_softc *sc;
623
624         sc = device_get_softc(dev);
625
626         /* Return the VLAN mode. */
627         conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
628         conf->vlan_mode = sc->vlan_mode;
629
630         return (0);
631 }
632
633 static int
634 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf)
635 {
636         struct adm6996fc_softc  *sc;
637         device_t                 parent;
638         int                      i;
639         int                      data;
640         int     bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
641
642         sc = device_get_softc(dev);
643         parent = device_get_parent(dev);
644
645         if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
646                 return (0);
647
648         if (conf->vlan_mode == ETHERSWITCH_VLAN_PORT) {
649                 sc->vlan_mode = ETHERSWITCH_VLAN_PORT;
650                 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
651                 data &= ~(1 << ADM6996FC_TBV_SHIFT);
652                 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
653                 for (i = 0;i <= 5; ++i) {
654                         data = ADM6996FC_READREG(parent, bcaddr[i]);
655                         data &= ~(0xf << 10);
656                         data |= (i << 10);
657                         ADM6996FC_WRITEREG(parent, bcaddr[i], data);
658                         ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i,
659                             0x003f);
660                         ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
661                             (1 << ADM6996FC_VV_SHIFT) | 1);
662                 }
663         } else if (conf->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
664                 sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
665                 data = ADM6996FC_READREG(parent, ADM6996FC_SC3);
666                 data |= (1 << ADM6996FC_TBV_SHIFT);
667                 ADM6996FC_WRITEREG(parent, ADM6996FC_SC3, data);
668                 for (i = 0;i <= 5; ++i) {
669                         data = ADM6996FC_READREG(parent, bcaddr[i]);
670                         /* Private VID set 1 */
671                         data &= ~(0xf << 10);
672                         data |= (1 << 10);
673                         /* Output Packet Tagging Enable */
674                         if (i == 5)
675                                 data |= (1 << 4);
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? */