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.
38 * etherswitchcfg command port option support addtag.
41 #include <sys/param.h>
43 #include <sys/errno.h>
44 #include <sys/kernel.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>
55 #include <net/if_var.h>
56 #include <net/ethernet.h>
57 #include <net/if_media.h>
58 #include <net/if_types.h>
60 #include <machine/bus.h>
61 #include <dev/mii/mii.h>
62 #include <dev/mii/miivar.h>
63 #include <dev/mdio/mdio.h>
65 #include <dev/etherswitch/etherswitch.h>
68 #include "miibus_if.h"
69 #include "etherswitch_if.h"
71 #define ADM6996FC_PRODUCT_CODE 0x7102
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
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
86 #define ADM6996FC_PHY_SIZE 0x20
88 MALLOC_DECLARE(M_ADM6996FC);
89 MALLOC_DEFINE(M_ADM6996FC, "adm6996fc", "adm6996fc data structures");
91 struct adm6996fc_softc {
92 struct mtx sc_mtx; /* serialize access to softc */
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];
104 struct callout callout_tick;
105 etherswitch_info_t info;
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)
118 #define DPRINTF(dev, args...) device_printf(dev, args)
120 #define DPRINTF(dev, args...)
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 *);
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);
133 #define ADM6996FC_PVIDBYDATA(data1, data2) \
134 ((((data1) >> ADM6996FC_PVID_SHIFT) & 0x0f) | ((data2) << 4))
137 adm6996fc_probe(device_t dev)
141 struct adm6996fc_softc *sc;
143 sc = device_get_softc(dev);
144 bzero(sc, sizeof(*sc));
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;
150 device_printf(dev,"Chip Identifier Register %x %x\n", data1,
153 /* check Product Code */
154 if (pc != ADM6996FC_PRODUCT_CODE) {
158 device_set_desc_copy(dev, "Infineon ADM6996FC/M/MX MDIO switch driver");
159 return (BUS_PROBE_DEFAULT);
163 adm6996fc_attach_phys(struct adm6996fc_softc *sc)
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)
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,
184 if (sc->miibus[port] == NULL) {
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);
195 device_printf(sc->sc_dev,
196 "attaching PHY %d failed\n",
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;
212 for (phy = 0; phy < sc->numports; phy++) {
213 if (((1 << phy) & sc->phymask) == 0)
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);
229 adm6996fc_attach(device_t dev)
231 struct adm6996fc_softc *sc;
235 sc = device_get_softc(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));
242 /* ADM6996FC Defaults */
248 sc->info.es_nvlangroups = 16;
249 sc->info.es_vlan_caps = ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOT1Q;
251 sc->ifp = malloc(sizeof(struct ifnet *) * sc->numports, M_ADM6996FC,
253 sc->ifname = malloc(sizeof(char *) * sc->numports, M_ADM6996FC,
255 sc->miibus = malloc(sizeof(device_t *) * sc->numports, M_ADM6996FC,
257 sc->portphy = malloc(sizeof(int) * sc->numports, M_ADM6996FC,
260 if (sc->ifp == NULL || sc->ifname == NULL || sc->miibus == NULL ||
261 sc->portphy == NULL) {
267 * Attach the PHYs and complete the bus enumeration.
269 err = adm6996fc_attach_phys(sc);
273 bus_generic_probe(dev);
274 bus_enumerate_hinted_children(dev);
275 err = bus_generic_attach(dev);
279 callout_init(&sc->callout_tick, 0);
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);
293 free(sc->ifp, M_ADM6996FC);
299 adm6996fc_detach(device_t dev)
301 struct adm6996fc_softc *sc;
304 sc = device_get_softc(dev);
306 callout_drain(&sc->callout_tick);
308 for (i = 0; i < MII_NPHY; i++) {
309 if (((1 << i) & sc->phymask) == 0)
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);
320 free(sc->portphy, M_ADM6996FC);
321 free(sc->miibus, M_ADM6996FC);
322 free(sc->ifname, M_ADM6996FC);
323 free(sc->ifp, M_ADM6996FC);
325 bus_generic_detach(dev);
326 mtx_destroy(&sc->sc_mtx);
332 * Convert PHY number to port number.
335 adm6996fc_portforphy(struct adm6996fc_softc *sc, int phy)
338 return (sc->ifpport[phy]);
341 static inline struct mii_data *
342 adm6996fc_miiforport(struct adm6996fc_softc *sc, int port)
345 if (port < 0 || port > sc->numports)
347 if (port == sc->cpuport)
349 return (device_get_softc(*sc->miibus[port]));
352 static inline struct ifnet *
353 adm6996fc_ifpforport(struct adm6996fc_softc *sc, int port)
356 if (port < 0 || port > sc->numports)
358 return (sc->ifp[port]);
362 * Poll the status for all PHYs.
365 adm6996fc_miipollstat(struct adm6996fc_softc *sc)
368 struct mii_data *mii;
369 struct mii_softc *miisc;
371 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
373 for (i = 0; i < MII_NPHY; i++) {
374 if (((1 << i) & sc->phymask) == 0)
376 port = adm6996fc_portforphy(sc, i);
377 if ((*sc->miibus[port]) == NULL)
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) !=
385 mii_phy_update(miisc, MII_POLLSTAT);
391 adm6996fc_tick(void *arg)
393 struct adm6996fc_softc *sc;
397 adm6996fc_miipollstat(sc);
398 callout_reset(&sc->callout_tick, hz, adm6996fc_tick, sc);
402 adm6996fc_lock(device_t dev)
404 struct adm6996fc_softc *sc;
406 sc = device_get_softc(dev);
408 ADM6996FC_LOCK_ASSERT(sc, MA_NOTOWNED);
413 adm6996fc_unlock(device_t dev)
415 struct adm6996fc_softc *sc;
417 sc = device_get_softc(dev);
419 ADM6996FC_LOCK_ASSERT(sc, MA_OWNED);
420 ADM6996FC_UNLOCK(sc);
423 static etherswitch_info_t *
424 adm6996fc_getinfo(device_t dev)
426 struct adm6996fc_softc *sc;
428 sc = device_get_softc(dev);
434 adm6996fc_getport(device_t dev, etherswitch_port_t *p)
436 struct adm6996fc_softc *sc;
437 struct mii_data *mii;
438 struct ifmediareq *ifmr;
443 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
444 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
446 sc = device_get_softc(dev);
449 if (p->es_port < 0 || p->es_port >= sc->numports)
452 parent = device_get_parent(dev);
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 */
459 data2 = (data2 >> 8) & 0xff;
461 data2 = data2 & 0xff;
463 p->es_pvid = ADM6996FC_PVIDBYDATA(data1, data2);
464 if (((data1 >> ADM6996FC_OPTE_SHIFT) & 0x01) == 1)
465 p->es_flags |= ETHERSWITCH_PORT_ADDTAG;
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;
476 if (sc->media == 100)
477 ifmr->ifm_current = ifmr->ifm_active =
478 IFM_ETHER | IFM_100_TX | IFM_FDX;
480 ifmr->ifm_current = ifmr->ifm_active =
481 IFM_ETHER | IFM_1000_T | IFM_FDX;
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);
496 adm6996fc_setport(device_t dev, etherswitch_port_t *p)
498 struct adm6996fc_softc *sc;
500 struct mii_data *mii;
506 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
507 int vidaddr[6] = {0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c};
509 sc = device_get_softc(dev);
510 parent = device_get_parent(dev);
512 if (p->es_port < 0 || p->es_port >= sc->numports)
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;
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);
531 data = data | ((p->es_pvid >> 4) & 0xff);
533 ADM6996FC_WRITEREG(parent, vidaddr[p->es_port], data);
536 if (sc->portphy[p->es_port] == sc->cpuport)
540 if (sc->portphy[p->es_port] != sc->cpuport) {
541 mii = adm6996fc_miiforport(sc, p->es_port);
545 ifp = adm6996fc_ifpforport(sc, p->es_port);
547 ifm = &mii->mii_media;
548 err = ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA);
554 adm6996fc_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
556 struct adm6996fc_softc *sc;
560 sc = device_get_softc(dev);
561 parent = device_get_parent(dev);
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);
572 vg->es_member_ports = datalo & 0x3f;
573 vg->es_untagged_ports = vg->es_member_ports;
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);
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;
601 adm6996fc_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
603 struct adm6996fc_softc *sc;
606 sc = device_get_softc(dev);
607 parent = device_get_parent(dev);
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);
623 adm6996fc_getconf(device_t dev, etherswitch_conf_t *conf)
625 struct adm6996fc_softc *sc;
627 sc = device_get_softc(dev);
629 /* Return the VLAN mode. */
630 conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
631 conf->vlan_mode = sc->vlan_mode;
637 adm6996fc_setconf(device_t dev, etherswitch_conf_t *conf)
639 struct adm6996fc_softc *sc;
643 int bcaddr[6] = {0x01, 0x03, 0x05, 0x07, 0x08, 0x09};
645 sc = device_get_softc(dev);
646 parent = device_get_parent(dev);
648 if ((conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) == 0)
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);
660 ADM6996FC_WRITEREG(parent, bcaddr[i], data);
661 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0L + 2 * i,
663 ADM6996FC_WRITEREG(parent, ADM6996FC_VF0H + 2 * i,
664 (1 << ADM6996FC_VV_SHIFT) | 1);
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);
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? */