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.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
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
37 * This code development on Netgear WGR614Cv7.
40 #include <sys/param.h>
42 #include <sys/errno.h>
43 #include <sys/kernel.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>
54 #include <net/if_var.h>
55 #include <net/ethernet.h>
56 #include <net/if_media.h>
57 #include <net/if_types.h>
59 #include <machine/bus.h>
60 #include <dev/mii/mii.h>
61 #include <dev/mii/miivar.h>
62 #include <dev/mdio/mdio.h>
64 #include <dev/etherswitch/etherswitch.h>
67 #include "miibus_if.h"
68 #include "etherswitch_if.h"
70 #define ADM6996FC_PRODUCT_CODE 0x7102
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
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
85 #define ADM6996FC_PHY_SIZE 0x20
87 MALLOC_DECLARE(M_ADM6996FC);
88 MALLOC_DEFINE(M_ADM6996FC, "adm6996fc", "adm6996fc data structures");
90 struct adm6996fc_softc {
91 struct mtx sc_mtx; /* serialize access to softc */
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];
103 struct callout callout_tick;
104 etherswitch_info_t info;
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)
117 #define DPRINTF(dev, args...) device_printf(dev, args)
119 #define DPRINTF(dev, args...)
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 *);
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);
132 #define ADM6996FC_PVIDBYDATA(data1, data2) \
133 ((((data1) >> ADM6996FC_PVID_SHIFT) & 0x0f) | ((data2) << 4))
136 adm6996fc_probe(device_t dev)
140 struct adm6996fc_softc *sc;
142 sc = device_get_softc(dev);
143 bzero(sc, sizeof(*sc));
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;
149 device_printf(dev,"Chip Identifier Register %x %x\n", data1,
152 /* check Product Code */
153 if (pc != ADM6996FC_PRODUCT_CODE) {
157 device_set_desc_copy(dev, "Infineon ADM6996FC/M/MX MDIO switch driver");
158 return (BUS_PROBE_DEFAULT);
162 adm6996fc_attach_phys(struct adm6996fc_softc *sc)
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)
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,
183 if (sc->miibus[port] == NULL) {
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);
194 device_printf(sc->sc_dev,
195 "attaching PHY %d failed\n",
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;
211 for (phy = 0; phy < sc->numports; phy++) {
212 if (((1 << phy) & sc->phymask) == 0)
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);
228 adm6996fc_attach(device_t dev)
230 struct adm6996fc_softc *sc;
234 sc = device_get_softc(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));
241 /* ADM6996FC Defaults */
247 sc->info.es_nvlangroups = 16;
248 sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
250 sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_ADM6996FC,
252 sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC,
254 sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC,
256 sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC,
259 if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL ||
260 sc->portphy == NULL) {
266 * Attach the PHYs and complete the bus enumeration.
268 err = adm6996fc_attach_phys(sc);
272 bus_generic_probe(dev);
273 bus_enumerate_hinted_children(dev);
274 err = bus_generic_attach(dev);
278 callout_init(&sc->callout_tick, 0);
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);
292 free(sc->ifp, M_ADM6996FC);
298 adm6996fc_detach(device_t dev)
300 struct adm6996fc_softc *sc;
303 sc = device_get_softc(dev);
305 callout_drain(&sc->callout_tick);
307 for (i = 0; i < MII_NPHY; i++) {
308 if (((1 << i) & sc->phymask) == 0)
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);
319 free(sc->portphy, M_ADM6996FC);
320 free(sc->miibus, M_ADM6996FC);
321 free(sc->ifname, M_ADM6996FC);
322 free(sc->ifp, M_ADM6996FC);
324 bus_generic_detach(dev);
325 mtx_destroy(&sc->sc_mtx);
331 * Convert PHY number to port number.
334 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy)
337 return (sc->ifpport[phy]);
340 static inline struct mii_data *
341 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port)
344 if (port < 0 || port > sc->numports)
346 if (port == sc->cpuport)
348 return (device_get_softc(*sc->miibus[port]));
351 static inline struct ifnet *
352 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port)
355 if (port < 0 || port > sc->numports)
357 return (sc->ifp[port]);
361 * Poll the status for all PHYs.
364 adm6996fc_miipollstat(struct adm6996fc_softc *sc)
367 struct mii_data *mii;
368 struct mii_softc *miisc;
370 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
372 for (i = 0; i < MII_NPHY; i++) {
373 if (((1 << i) & sc->phymask) == 0)
375 port = adm6996fc_portforphy(sc, i);
376 if ((*sc->miibus[port]) == NULL)
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) !=
384 mii_phy_update(miisc, MII_POLLSTAT);
390 adm6996fc_tick(void *arg)
392 struct adm6996fc_softc *sc;
396 adm6996fc_miipollstat(sc);
397 callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc);
401 adm6996fc_lock(device_t dev)
403 struct adm6996fc_softc *sc;
405 sc = device_get_softc(dev);
407 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
412 adm6996fc_unlock(device_t dev)
414 struct adm6996fc_softc *sc;
416 sc = device_get_softc(dev);
418 ADM6996FC_LOCK_ASSERT(sc, MA_OWNED);
419 ADM6996FC_UNLOCK(sc);
422 static etherswitch_info_t *
423 adm6996fc_getinfo(device_t dev)
425 struct adm6996fc_softc *sc;
427 sc = device_get_softc(dev);
433 adm6996fc_getport(device_t dev, etherswitch_port_t *p)
435 struct adm6996fc_softc *sc;
436 struct mii_data *mii;
437 struct ifmediareq *ifmr;
442 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
443 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
445 sc = device_get_softc(dev);
448 if (p->es_port < 0 || p->es_port >= sc->numports)
451 parent = device_get_parent(dev);
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 */
458 data2 = (data2 >> 8) & 0xff;
460 data2 = data2 & 0xff;
462 p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2);
463 if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1)
464 p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
466 p->es_flags |= ETHERSWITCH_PORT_STRIPTAG;
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;
477 if (sc->media == 100)
478 ifmr->ifm_current = ifmr->ifm_active =
479 IFM_ETHER | IFM_100_TX | IFM_FDX;
481 ifmr->ifm_current = ifmr->ifm_active =
482 IFM_ETHER | IFM_1000_T | IFM_FDX;
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);
497 adm6996fc_setport(device_t dev, etherswitch_port_t *p)
499 struct adm6996fc_softc *sc;
501 struct mii_data *mii;
507 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
508 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
510 sc = device_get_softc(dev);
511 parent = device_get_parent(dev);
513 if (p->es_port < 0 || p->es_port >= sc->numports)
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);
528 data = data | ((p->es_pvid >> 4) & 0xff);
530 ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data);
533 if (sc->portphy[p->es_port] == sc->cpuport)
537 if (sc->portphy[p->es_port] != sc->cpuport) {
538 mii = adm6996fc_miiforport(sc, p->es_port);
542 ifp = adm6996fc_ifpforport(sc, p->es_port);
544 ifm = &mii->mii_media;
545 err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
551 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
553 struct adm6996fc_softc *sc;
557 sc = device_get_softc(dev);
558 parent = device_get_parent(dev);
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);
569 vg->es_member_ports = datalo & 0x3f;
570 vg->es_untagged_ports = vg->es_member_ports;
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);
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;
598 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
600 struct adm6996fc_softc *sc;
603 sc = device_get_softc(dev);
604 parent = device_get_parent(dev);
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);
620 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf)
622 struct adm6996fc_softc *sc;
624 sc = device_get_softc(dev);
626 /* Return the VLAN mode. */
627 conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
628 conf->vlan_mode = sc->vlan_mode;
634 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf)
636 struct adm6996fc_softc *sc;
640 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
642 sc = device_get_softc(dev);
643 parent = device_get_parent(dev);
645 if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
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);
657 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
658 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i,
660 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
661 (1 << ADM6996FC_VV_SHIFT) | 1);
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);
673 /* Output Packet Tagging Enable */
676 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
678 for (i = 2;i <= 15; ++i) {
679 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
684 ADM6996FC have no VLAN off. Then set Port base and
685 add all port to member. Use VLAN Filter 1 is reset
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);
698 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
700 /* default setting */
701 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2, 0x003f);
702 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2,
703 (1 << ADM6996FC_VV_SHIFT) | 1);
711 adm6996fc_statchg(device_t dev)
714 DPRINTF(dev, "%s\n", __func__);
718 adm6996fc_ifmedia_upd(struct ifnet *ifp)
720 struct adm6996fc_softc *sc;
721 struct mii_data *mii;
724 mii = adm6996fc_miiforport(sc, ifp->if_dunit);
726 DPRINTF(sc->sc_dev, "%s\n", __func__);
734 adm6996fc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
736 struct adm6996fc_softc *sc;
737 struct mii_data *mii;
740 mii = adm6996fc_miiforport(sc, ifp->if_dunit);
742 DPRINTF(sc->sc_dev, "%s\n", __func__);
747 ifmr->ifm_active = mii->mii_media_active;
748 ifmr->ifm_status = mii->mii_media_status;
752 adm6996fc_readphy(device_t dev, int phy, int reg)
754 struct adm6996fc_softc *sc;
757 sc = device_get_softc(dev);
758 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
760 if (phy < 0 || phy >= 32)
762 if (reg < 0 || reg >= 32)
766 data = ADM6996FC_READREG(device_get_parent(dev),
767 (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg);
768 ADM6996FC_UNLOCK(sc);
774 adm6996fc_writephy(device_t dev, int phy, int reg, int data)
776 struct adm6996fc_softc *sc;
779 sc = device_get_softc(dev);
780 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
782 if (phy < 0 || phy >= 32)
784 if (reg < 0 || reg >= 32)
788 err = ADM6996FC_WRITEREG(device_get_parent(dev),
789 (ADM6996FC_PHY_C0 + ADM6996FC_PHY_SIZE * phy) + reg, data);
790 ADM6996FC_UNLOCK(sc);
796 adm6996fc_readreg(device_t dev, int addr)
799 return ADM6996FC_READREG(device_get_parent(dev), addr);
803 adm6996fc_writereg(device_t dev, int addr, int value)
807 err = ADM6996FC_WRITEREG(device_get_parent(dev), addr, value);
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),
818 DEVMETHOD(bus_add_child, device_add_child_ordered),
821 DEVMETHOD(miibus_readreg, adm6996fc_readphy),
822 DEVMETHOD(miibus_writereg, adm6996fc_writephy),
823 DEVMETHOD(miibus_statchg, adm6996fc_statchg),
826 DEVMETHOD(mdio_readreg, adm6996fc_readphy),
827 DEVMETHOD(mdio_writereg, adm6996fc_writephy),
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),
847 DEFINE_CLASS_0(adm6996fc, adm6996fc_driver, adm6996fc_methods,
848 sizeof(struct adm6996fc_softc));
849 static devclass_t adm6996fc_devclass;
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,
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? */