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