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