1a043e8c7SAdrian Chadd /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4a043e8c7SAdrian Chadd * Copyright (c) 2011-2012 Stefan Bethke. 5a043e8c7SAdrian Chadd * Copyright (c) 2012 Adrian Chadd. 6a043e8c7SAdrian Chadd * All rights reserved. 7a043e8c7SAdrian Chadd * 8a043e8c7SAdrian Chadd * Redistribution and use in source and binary forms, with or without 9a043e8c7SAdrian Chadd * modification, are permitted provided that the following conditions 10a043e8c7SAdrian Chadd * are met: 11a043e8c7SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 12a043e8c7SAdrian Chadd * notice, this list of conditions and the following disclaimer. 13a043e8c7SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 14a043e8c7SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 15a043e8c7SAdrian Chadd * documentation and/or other materials provided with the distribution. 16a043e8c7SAdrian Chadd * 17a043e8c7SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18a043e8c7SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19a043e8c7SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20a043e8c7SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21a043e8c7SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22a043e8c7SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23a043e8c7SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24a043e8c7SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25a043e8c7SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26a043e8c7SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27a043e8c7SAdrian Chadd * SUCH DAMAGE. 28a043e8c7SAdrian Chadd * 29a043e8c7SAdrian Chadd * $FreeBSD$ 30a043e8c7SAdrian Chadd */ 31a043e8c7SAdrian Chadd 32a043e8c7SAdrian Chadd #include <sys/param.h> 33a043e8c7SAdrian Chadd #include <sys/bus.h> 34a043e8c7SAdrian Chadd #include <sys/errno.h> 35a043e8c7SAdrian Chadd #include <sys/kernel.h> 36104dc214SGleb Smirnoff #include <sys/malloc.h> 37a043e8c7SAdrian Chadd #include <sys/module.h> 38a043e8c7SAdrian Chadd #include <sys/socket.h> 39a043e8c7SAdrian Chadd #include <sys/sockio.h> 40a043e8c7SAdrian Chadd #include <sys/sysctl.h> 41a043e8c7SAdrian Chadd #include <sys/systm.h> 42a043e8c7SAdrian Chadd 43a043e8c7SAdrian Chadd #include <net/if.h> 44104dc214SGleb Smirnoff #include <net/if_var.h> 45a043e8c7SAdrian Chadd #include <net/if_arp.h> 46a043e8c7SAdrian Chadd #include <net/ethernet.h> 47a043e8c7SAdrian Chadd #include <net/if_dl.h> 48a043e8c7SAdrian Chadd #include <net/if_media.h> 49a043e8c7SAdrian Chadd #include <net/if_types.h> 50a043e8c7SAdrian Chadd 51a043e8c7SAdrian Chadd #include <machine/bus.h> 52efce3748SRui Paulo #include <dev/iicbus/iic.h> 53a043e8c7SAdrian Chadd #include <dev/iicbus/iiconf.h> 54a043e8c7SAdrian Chadd #include <dev/iicbus/iicbus.h> 55a043e8c7SAdrian Chadd #include <dev/mii/mii.h> 56a043e8c7SAdrian Chadd #include <dev/mii/miivar.h> 5771e8eac4SAdrian Chadd #include <dev/mdio/mdio.h> 58a043e8c7SAdrian Chadd 59a043e8c7SAdrian Chadd #include <dev/etherswitch/etherswitch.h> 60a043e8c7SAdrian Chadd 61a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchreg.h> 62a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchvar.h> 63a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_reg.h> 64a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_phy.h> 65b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_vlans.h> 66a043e8c7SAdrian Chadd 6727a2ecaaSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_7240.h> 68a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8216.h> 69a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8226.h> 70a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8316.h> 71482d268dSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8327.h> 72b2152161SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_9340.h> 73a043e8c7SAdrian Chadd 74a043e8c7SAdrian Chadd #include "mdio_if.h" 75a043e8c7SAdrian Chadd #include "miibus_if.h" 76a043e8c7SAdrian Chadd #include "etherswitch_if.h" 77a043e8c7SAdrian Chadd 78c94dc808SAdrian Chadd /* Map ETHERSWITCH_PORT_LED_* to Atheros pattern codes */ 79c94dc808SAdrian Chadd static int led_pattern_table[] = { 80c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_DEFAULT] = 0x3, 81c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_ON] = 0x2, 82c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_OFF] = 0x0, 83c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_BLINK] = 0x1 84c94dc808SAdrian Chadd }; 85c94dc808SAdrian Chadd 86a043e8c7SAdrian Chadd static inline int arswitch_portforphy(int phy); 87a043e8c7SAdrian Chadd static void arswitch_tick(void *arg); 88a043e8c7SAdrian Chadd static int arswitch_ifmedia_upd(struct ifnet *); 89a043e8c7SAdrian Chadd static void arswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *); 90a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_setup(struct arswitch_softc *sc, 91a9ad4222SAdrian Chadd etherswitch_port_t *p); 92a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_get(struct arswitch_softc *sc, 93a9ad4222SAdrian Chadd etherswitch_port_t *p); 94c94dc808SAdrian Chadd static int arswitch_setled(struct arswitch_softc *sc, int phy, int led, 95c94dc808SAdrian Chadd int style); 96a043e8c7SAdrian Chadd 97a043e8c7SAdrian Chadd static int 98a043e8c7SAdrian Chadd arswitch_probe(device_t dev) 99a043e8c7SAdrian Chadd { 100a043e8c7SAdrian Chadd struct arswitch_softc *sc; 101a043e8c7SAdrian Chadd uint32_t id; 102a043e8c7SAdrian Chadd char *chipname, desc[256]; 103a043e8c7SAdrian Chadd 104a043e8c7SAdrian Chadd sc = device_get_softc(dev); 105a043e8c7SAdrian Chadd bzero(sc, sizeof(*sc)); 106a043e8c7SAdrian Chadd sc->page = -1; 10727a2ecaaSAdrian Chadd 10827a2ecaaSAdrian Chadd /* AR7240 probe */ 10927a2ecaaSAdrian Chadd if (ar7240_probe(dev) == 0) { 11027a2ecaaSAdrian Chadd chipname = "AR7240"; 11127a2ecaaSAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR7240; 112b2152161SAdrian Chadd sc->is_internal_switch = 1; 113b2152161SAdrian Chadd id = 0; 114b2152161SAdrian Chadd goto done; 115b2152161SAdrian Chadd } 116b2152161SAdrian Chadd 117b2152161SAdrian Chadd /* AR9340 probe */ 118b2152161SAdrian Chadd if (ar9340_probe(dev) == 0) { 119b2152161SAdrian Chadd chipname = "AR9340"; 120b2152161SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR9340; 121b2152161SAdrian Chadd sc->is_internal_switch = 1; 122daa4deacSAleksandr Rybalko id = 0; 12327a2ecaaSAdrian Chadd goto done; 12427a2ecaaSAdrian Chadd } 12527a2ecaaSAdrian Chadd 12627a2ecaaSAdrian Chadd /* AR8xxx probe */ 127a043e8c7SAdrian Chadd id = arswitch_readreg(dev, AR8X16_REG_MASK_CTRL); 128dd843f87SAdrian Chadd sc->chip_rev = (id & AR8X16_MASK_CTRL_REV_MASK); 129dd843f87SAdrian Chadd sc->chip_ver = (id & AR8X16_MASK_CTRL_VER_MASK) > AR8X16_MASK_CTRL_VER_SHIFT; 130b2152161SAdrian Chadd switch (id & (AR8X16_MASK_CTRL_VER_MASK | AR8X16_MASK_CTRL_REV_MASK)) { 131b2152161SAdrian Chadd case 0x0101: 132a043e8c7SAdrian Chadd chipname = "AR8216"; 133a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8216; 134a043e8c7SAdrian Chadd break; 135b2152161SAdrian Chadd case 0x0201: 136a043e8c7SAdrian Chadd chipname = "AR8226"; 137a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8226; 138a043e8c7SAdrian Chadd break; 139b2152161SAdrian Chadd /* 0x0301 - AR8236 */ 140b2152161SAdrian Chadd case 0x1000: 141b2152161SAdrian Chadd case 0x1001: 142a043e8c7SAdrian Chadd chipname = "AR8316"; 143a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8316; 144a043e8c7SAdrian Chadd break; 1450e67bf94SAdrian Chadd case 0x1202: 1469682e347SAdrian Chadd case 0x1204: 1470e67bf94SAdrian Chadd chipname = "AR8327"; 1480e67bf94SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8327; 1490e67bf94SAdrian Chadd sc->mii_lo_first = 1; 1500e67bf94SAdrian Chadd break; 151a043e8c7SAdrian Chadd default: 152a043e8c7SAdrian Chadd chipname = NULL; 153a043e8c7SAdrian Chadd } 15427a2ecaaSAdrian Chadd 15527a2ecaaSAdrian Chadd done: 156b2152161SAdrian Chadd 1571b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, "chipname=%s, id=%08x\n", chipname, id); 158a043e8c7SAdrian Chadd if (chipname != NULL) { 159a043e8c7SAdrian Chadd snprintf(desc, sizeof(desc), 16078549b94SAdrian Chadd "Atheros %s Ethernet Switch (ver %d rev %d)", 16178549b94SAdrian Chadd chipname, 16278549b94SAdrian Chadd sc->chip_ver, 16378549b94SAdrian Chadd sc->chip_rev); 164a043e8c7SAdrian Chadd device_set_desc_copy(dev, desc); 165a043e8c7SAdrian Chadd return (BUS_PROBE_DEFAULT); 166a043e8c7SAdrian Chadd } 167a043e8c7SAdrian Chadd return (ENXIO); 168a043e8c7SAdrian Chadd } 169a043e8c7SAdrian Chadd 170a043e8c7SAdrian Chadd static int 171a043e8c7SAdrian Chadd arswitch_attach_phys(struct arswitch_softc *sc) 172a043e8c7SAdrian Chadd { 173a043e8c7SAdrian Chadd int phy, err = 0; 174a043e8c7SAdrian Chadd char name[IFNAMSIZ]; 175a043e8c7SAdrian Chadd 176a043e8c7SAdrian Chadd /* PHYs need an interface, so we generate a dummy one */ 177a043e8c7SAdrian Chadd snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 178a043e8c7SAdrian Chadd for (phy = 0; phy < sc->numphys; phy++) { 179a043e8c7SAdrian Chadd sc->ifp[phy] = if_alloc(IFT_ETHER); 1800774131eSMichael Zhilin if (sc->ifp[phy] == NULL) { 1810774131eSMichael Zhilin device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n"); 1820774131eSMichael Zhilin err = ENOMEM; 1830774131eSMichael Zhilin break; 1840774131eSMichael Zhilin } 1850774131eSMichael Zhilin 186a043e8c7SAdrian Chadd sc->ifp[phy]->if_softc = sc; 187a043e8c7SAdrian Chadd sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST | 188a043e8c7SAdrian Chadd IFF_DRV_RUNNING | IFF_SIMPLEX; 189a043e8c7SAdrian Chadd sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK); 190a043e8c7SAdrian Chadd bcopy(name, sc->ifname[phy], strlen(name)+1); 191a043e8c7SAdrian Chadd if_initname(sc->ifp[phy], sc->ifname[phy], 192a043e8c7SAdrian Chadd arswitch_portforphy(phy)); 193a043e8c7SAdrian Chadd err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy], 194a043e8c7SAdrian Chadd arswitch_ifmedia_upd, arswitch_ifmedia_sts, \ 195a043e8c7SAdrian Chadd BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 19678549b94SAdrian Chadd #if 0 197a043e8c7SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 198a043e8c7SAdrian Chadd device_get_nameunit(sc->miibus[phy]), 199a043e8c7SAdrian Chadd sc->ifp[phy]->if_xname); 20078549b94SAdrian Chadd #endif 201a043e8c7SAdrian Chadd if (err != 0) { 202a043e8c7SAdrian Chadd device_printf(sc->sc_dev, 203a043e8c7SAdrian Chadd "attaching PHY %d failed\n", 204a043e8c7SAdrian Chadd phy); 205a043e8c7SAdrian Chadd return (err); 206a043e8c7SAdrian Chadd } 207a043e8c7SAdrian Chadd 208c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) { 209c94dc808SAdrian Chadd int led; 210c94dc808SAdrian Chadd char ledname[IFNAMSIZ+4]; 211c94dc808SAdrian Chadd 212c94dc808SAdrian Chadd for (led = 0; led < 3; led++) { 213c94dc808SAdrian Chadd sprintf(ledname, "%s%dled%d", name, 214c94dc808SAdrian Chadd arswitch_portforphy(phy), led+1); 215c94dc808SAdrian Chadd sc->dev_led[phy][led].sc = sc; 216c94dc808SAdrian Chadd sc->dev_led[phy][led].phy = phy; 217c94dc808SAdrian Chadd sc->dev_led[phy][led].lednum = led; 218c94dc808SAdrian Chadd } 219c94dc808SAdrian Chadd } 220c94dc808SAdrian Chadd } 221c94dc808SAdrian Chadd return (0); 222c94dc808SAdrian Chadd } 223c94dc808SAdrian Chadd 224a043e8c7SAdrian Chadd static int 225b9f07b86SLuiz Otavio O Souza arswitch_reset(device_t dev) 226b9f07b86SLuiz Otavio O Souza { 227b9f07b86SLuiz Otavio O Souza 228b9f07b86SLuiz Otavio O Souza arswitch_writereg(dev, AR8X16_REG_MASK_CTRL, 229b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET); 230b9f07b86SLuiz Otavio O Souza DELAY(1000); 231b9f07b86SLuiz Otavio O Souza if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) & 232b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET) { 233b9f07b86SLuiz Otavio O Souza device_printf(dev, "unable to reset switch\n"); 234b9f07b86SLuiz Otavio O Souza return (-1); 235b9f07b86SLuiz Otavio O Souza } 236b9f07b86SLuiz Otavio O Souza return (0); 237b9f07b86SLuiz Otavio O Souza } 238b9f07b86SLuiz Otavio O Souza 239b9f07b86SLuiz Otavio O Souza static int 240b9f07b86SLuiz Otavio O Souza arswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode) 241b9f07b86SLuiz Otavio O Souza { 242b9f07b86SLuiz Otavio O Souza 243b9f07b86SLuiz Otavio O Souza /* Check for invalid modes. */ 244b9f07b86SLuiz Otavio O Souza if ((mode & sc->info.es_vlan_caps) != mode) 245b9f07b86SLuiz Otavio O Souza return (EINVAL); 246b9f07b86SLuiz Otavio O Souza 247b9f07b86SLuiz Otavio O Souza switch (mode) { 248b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_DOT1Q: 249b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 250b9f07b86SLuiz Otavio O Souza break; 251b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_PORT: 252b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_PORT; 253b9f07b86SLuiz Otavio O Souza break; 254b9f07b86SLuiz Otavio O Souza default: 255b9f07b86SLuiz Otavio O Souza sc->vlan_mode = 0; 25674b8d63dSPedro F. Giffuni } 257b9f07b86SLuiz Otavio O Souza 258b9f07b86SLuiz Otavio O Souza /* Reset VLANs. */ 2596dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw(sc); 260b9f07b86SLuiz Otavio O Souza 261b9f07b86SLuiz Otavio O Souza return (0); 262b9f07b86SLuiz Otavio O Souza } 263b9f07b86SLuiz Otavio O Souza 264b9f07b86SLuiz Otavio O Souza static void 265a9ad4222SAdrian Chadd ar8xxx_port_init(struct arswitch_softc *sc, int port) 266b9f07b86SLuiz Otavio O Souza { 267b9f07b86SLuiz Otavio O Souza 268b9f07b86SLuiz Otavio O Souza /* Port0 - CPU */ 269df892897SAdrian Chadd if (port == AR8X16_PORT_CPU) { 270b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(0), 271b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 272b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_SPEED_100 : AR8X16_PORT_STS_SPEED_1000) | 273b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_RXFLOW) | 274b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_TXFLOW) | 275b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_RXMAC | 276b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_TXMAC | 277b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_DUPLEX); 278b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0), 279b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0)) & 280b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER); 281df892897SAdrian Chadd } else { 282b9f07b86SLuiz Otavio O Souza /* Set ports to auto negotiation. */ 283b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(port), 284b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_LINK_AUTO); 285b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port), 286b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port)) & 287b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER); 288b9f07b86SLuiz Otavio O Souza } 289b9f07b86SLuiz Otavio O Souza } 290b9f07b86SLuiz Otavio O Souza 291b9f07b86SLuiz Otavio O Souza static int 29262042c97SAdrian Chadd ar8xxx_atu_wait_ready(struct arswitch_softc *sc) 2934ff2f60dSAdrian Chadd { 2944ff2f60dSAdrian Chadd int ret; 2954ff2f60dSAdrian Chadd 29662042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 29762042c97SAdrian Chadd 2984ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev, 2994ff2f60dSAdrian Chadd AR8216_REG_ATU, 3004ff2f60dSAdrian Chadd AR8216_ATU_ACTIVE, 3014ff2f60dSAdrian Chadd 0, 3024ff2f60dSAdrian Chadd 1000); 3034ff2f60dSAdrian Chadd 30462042c97SAdrian Chadd return (ret); 30562042c97SAdrian Chadd } 30662042c97SAdrian Chadd 30762042c97SAdrian Chadd /* 30862042c97SAdrian Chadd * Flush all ATU entries. 30962042c97SAdrian Chadd */ 31062042c97SAdrian Chadd static int 31162042c97SAdrian Chadd ar8xxx_atu_flush(struct arswitch_softc *sc) 31262042c97SAdrian Chadd { 31362042c97SAdrian Chadd int ret; 31462042c97SAdrian Chadd 31562042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 31662042c97SAdrian Chadd 31762042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing all ports\n", __func__); 31862042c97SAdrian Chadd 31962042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc); 3204ff2f60dSAdrian Chadd if (ret) 3214ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 3224ff2f60dSAdrian Chadd 3234ff2f60dSAdrian Chadd if (!ret) 3244ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev, 3254ff2f60dSAdrian Chadd AR8216_REG_ATU, 3262c6ceccaSAdrian Chadd AR8216_ATU_OP_FLUSH | AR8216_ATU_ACTIVE); 3274ff2f60dSAdrian Chadd 3284ff2f60dSAdrian Chadd return (ret); 3294ff2f60dSAdrian Chadd } 3304ff2f60dSAdrian Chadd 33162042c97SAdrian Chadd /* 33262042c97SAdrian Chadd * Flush ATU entries for a single port. 33362042c97SAdrian Chadd */ 33462042c97SAdrian Chadd static int 33562042c97SAdrian Chadd ar8xxx_atu_flush_port(struct arswitch_softc *sc, int port) 33662042c97SAdrian Chadd { 33762042c97SAdrian Chadd int ret, val; 33862042c97SAdrian Chadd 33962042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing port %d\n", __func__, 34062042c97SAdrian Chadd port); 34162042c97SAdrian Chadd 34262042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 34362042c97SAdrian Chadd 34462042c97SAdrian Chadd /* Flush unicast entries on port */ 34562042c97SAdrian Chadd val = AR8216_ATU_OP_FLUSH_UNICAST; 34662042c97SAdrian Chadd 34762042c97SAdrian Chadd /* TODO: bit 4 indicates whether to flush dynamic (0) or static (1) */ 34862042c97SAdrian Chadd 34962042c97SAdrian Chadd /* Which port */ 35062042c97SAdrian Chadd val |= SM(port, AR8216_ATU_PORT_NUM); 35162042c97SAdrian Chadd 35262042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc); 35362042c97SAdrian Chadd if (ret) 35462042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 35562042c97SAdrian Chadd 35662042c97SAdrian Chadd if (!ret) 35762042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 35862042c97SAdrian Chadd AR8216_REG_ATU, 35962042c97SAdrian Chadd val | AR8216_ATU_ACTIVE); 36062042c97SAdrian Chadd 36162042c97SAdrian Chadd return (ret); 36262042c97SAdrian Chadd } 36362042c97SAdrian Chadd 36462042c97SAdrian Chadd /* 36562042c97SAdrian Chadd * XXX TODO: flush a single MAC address. 36662042c97SAdrian Chadd */ 36762042c97SAdrian Chadd 36862042c97SAdrian Chadd /* 36962042c97SAdrian Chadd * Fetch a single entry from the ATU. 37062042c97SAdrian Chadd */ 37162042c97SAdrian Chadd static int 37262042c97SAdrian Chadd ar8xxx_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e, 37362042c97SAdrian Chadd int atu_fetch_op) 37462042c97SAdrian Chadd { 37562042c97SAdrian Chadd uint32_t ret0, ret1, ret2, val; 37662042c97SAdrian Chadd 37762042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 37862042c97SAdrian Chadd 37962042c97SAdrian Chadd switch (atu_fetch_op) { 38062042c97SAdrian Chadd case 0: 38162042c97SAdrian Chadd /* Initialise things for the first fetch */ 38262042c97SAdrian Chadd 38362042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__); 38462042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 38562042c97SAdrian Chadd 38662042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 38762042c97SAdrian Chadd AR8216_REG_ATU, AR8216_ATU_OP_GET_NEXT); 38862042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 38962042c97SAdrian Chadd AR8216_REG_ATU_DATA, 0); 39062042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 39162042c97SAdrian Chadd AR8216_REG_ATU_CTRL2, 0); 39262042c97SAdrian Chadd 39362042c97SAdrian Chadd return (0); 39462042c97SAdrian Chadd case 1: 39562042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__); 39662042c97SAdrian Chadd /* 39762042c97SAdrian Chadd * Attempt to read the next address entry; don't modify what 39862042c97SAdrian Chadd * is there in AT_ADDR{4,5} as its used for the next fetch 39962042c97SAdrian Chadd */ 40062042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 40162042c97SAdrian Chadd 40262042c97SAdrian Chadd /* Begin the next read event; not modifying anything */ 40362042c97SAdrian Chadd val = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU); 40462042c97SAdrian Chadd val |= AR8216_ATU_ACTIVE; 40562042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8216_REG_ATU, val); 40662042c97SAdrian Chadd 40762042c97SAdrian Chadd /* Wait for it to complete */ 40862042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 40962042c97SAdrian Chadd 41062042c97SAdrian Chadd /* Fetch the ethernet address and ATU status */ 41162042c97SAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU); 41262042c97SAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_DATA); 41362042c97SAdrian Chadd ret2 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_CTRL2); 41462042c97SAdrian Chadd 41562042c97SAdrian Chadd /* If the status is zero, then we're done */ 41662042c97SAdrian Chadd if (MS(ret2, AR8216_ATU_CTRL2_AT_STATUS) == 0) 41762042c97SAdrian Chadd return (-1); 41862042c97SAdrian Chadd 41962042c97SAdrian Chadd /* MAC address */ 42062042c97SAdrian Chadd e->es_macaddr[5] = MS(ret0, AR8216_ATU_ADDR5); 42162042c97SAdrian Chadd e->es_macaddr[4] = MS(ret0, AR8216_ATU_ADDR4); 42262042c97SAdrian Chadd e->es_macaddr[3] = MS(ret1, AR8216_ATU_ADDR3); 42362042c97SAdrian Chadd e->es_macaddr[2] = MS(ret1, AR8216_ATU_ADDR2); 42462042c97SAdrian Chadd e->es_macaddr[1] = MS(ret1, AR8216_ATU_ADDR1); 42562042c97SAdrian Chadd e->es_macaddr[0] = MS(ret1, AR8216_ATU_ADDR0); 42662042c97SAdrian Chadd 42762042c97SAdrian Chadd /* Bitmask of ports this entry is for */ 42862042c97SAdrian Chadd e->es_portmask = MS(ret2, AR8216_ATU_CTRL2_DESPORT); 42962042c97SAdrian Chadd 43062042c97SAdrian Chadd /* TODO: other flags that are interesting */ 43162042c97SAdrian Chadd 43262042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n", 43362042c97SAdrian Chadd __func__, 43462042c97SAdrian Chadd e->es_macaddr, ":", e->es_portmask); 43562042c97SAdrian Chadd return (0); 43662042c97SAdrian Chadd default: 43762042c97SAdrian Chadd return (-1); 43862042c97SAdrian Chadd } 43962042c97SAdrian Chadd return (-1); 44062042c97SAdrian Chadd } 44162042c97SAdrian Chadd 44262042c97SAdrian Chadd /* 44362042c97SAdrian Chadd * Configure aging register defaults. 44462042c97SAdrian Chadd */ 44562042c97SAdrian Chadd static int 44662042c97SAdrian Chadd ar8xxx_atu_learn_default(struct arswitch_softc *sc) 44762042c97SAdrian Chadd { 44862042c97SAdrian Chadd int ret; 44962042c97SAdrian Chadd uint32_t val; 45062042c97SAdrian Chadd 45162042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: resetting learning\n", __func__); 45262042c97SAdrian Chadd 45362042c97SAdrian Chadd /* 45462042c97SAdrian Chadd * For now, configure the aging defaults: 45562042c97SAdrian Chadd * 45662042c97SAdrian Chadd * + ARP_EN - enable "acknowledgement" of ARP frames - they are 45762042c97SAdrian Chadd * forwarded to the CPU port 45862042c97SAdrian Chadd * + LEARN_CHANGE_EN - hash table violations when learning MAC addresses 45962042c97SAdrian Chadd * will force an entry to be expired/updated and a new one to be 46062042c97SAdrian Chadd * programmed in. 46162042c97SAdrian Chadd * + AGE_EN - enable address table aging 46262042c97SAdrian Chadd * + AGE_TIME - set to 5 minutes 46362042c97SAdrian Chadd */ 46462042c97SAdrian Chadd val = 0; 46562042c97SAdrian Chadd val |= AR8216_ATU_CTRL_ARP_EN; 46662042c97SAdrian Chadd val |= AR8216_ATU_CTRL_LEARN_CHANGE; 46762042c97SAdrian Chadd val |= AR8216_ATU_CTRL_AGE_EN; 46862042c97SAdrian Chadd val |= 0x2b; /* 5 minutes; bits 15:0 */ 46962042c97SAdrian Chadd 47062042c97SAdrian Chadd ret = arswitch_writereg(sc->sc_dev, 47162042c97SAdrian Chadd AR8216_REG_ATU_CTRL, 47262042c97SAdrian Chadd val); 47362042c97SAdrian Chadd 47462042c97SAdrian Chadd if (ret) 47562042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: writereg failed\n", __func__); 47662042c97SAdrian Chadd 47762042c97SAdrian Chadd return (ret); 47862042c97SAdrian Chadd } 47962042c97SAdrian Chadd 48062042c97SAdrian Chadd /* 48162042c97SAdrian Chadd * XXX TODO: add another routine to configure the leaky behaviour 48262042c97SAdrian Chadd * when unknown frames are received. These must be consistent 48362042c97SAdrian Chadd * between ethernet switches. 48462042c97SAdrian Chadd */ 48562042c97SAdrian Chadd 48662042c97SAdrian Chadd /* 4872ba4bf8fSAdrian Chadd * Fetch the configured switch MAC address. 4882ba4bf8fSAdrian Chadd */ 4892ba4bf8fSAdrian Chadd static int 4902ba4bf8fSAdrian Chadd ar8xxx_hw_get_switch_macaddr(struct arswitch_softc *sc, struct ether_addr *ea) 4912ba4bf8fSAdrian Chadd { 4922ba4bf8fSAdrian Chadd uint32_t ret0, ret1; 4932ba4bf8fSAdrian Chadd char *s; 4942ba4bf8fSAdrian Chadd 4952ba4bf8fSAdrian Chadd s = (void *) ea; 4962ba4bf8fSAdrian Chadd 4972ba4bf8fSAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR0); 4982ba4bf8fSAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR1); 4992ba4bf8fSAdrian Chadd 5002ba4bf8fSAdrian Chadd s[5] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE5); 5012ba4bf8fSAdrian Chadd s[4] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE4); 5022ba4bf8fSAdrian Chadd s[3] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE3); 5032ba4bf8fSAdrian Chadd s[2] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE2); 5042ba4bf8fSAdrian Chadd s[1] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE1); 5052ba4bf8fSAdrian Chadd s[0] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE0); 5062ba4bf8fSAdrian Chadd 5072ba4bf8fSAdrian Chadd return (0); 5082ba4bf8fSAdrian Chadd } 5092ba4bf8fSAdrian Chadd 5102ba4bf8fSAdrian Chadd /* 5112ba4bf8fSAdrian Chadd * Set the switch mac address. 5122ba4bf8fSAdrian Chadd */ 5132ba4bf8fSAdrian Chadd static int 5142ba4bf8fSAdrian Chadd ar8xxx_hw_set_switch_macaddr(struct arswitch_softc *sc, 5152ba4bf8fSAdrian Chadd const struct ether_addr *ea) 5162ba4bf8fSAdrian Chadd { 5172ba4bf8fSAdrian Chadd 5182ba4bf8fSAdrian Chadd return (ENXIO); 5192ba4bf8fSAdrian Chadd } 5202ba4bf8fSAdrian Chadd 5212ba4bf8fSAdrian Chadd /* 52262042c97SAdrian Chadd * XXX TODO: this attach routine does NOT free all memory, resources 52362042c97SAdrian Chadd * upon failure! 52462042c97SAdrian Chadd */ 5254ff2f60dSAdrian Chadd static int 526a043e8c7SAdrian Chadd arswitch_attach(device_t dev) 527a043e8c7SAdrian Chadd { 5281b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 5291b334c8bSAdrian Chadd struct sysctl_ctx_list *ctx; 5301b334c8bSAdrian Chadd struct sysctl_oid *tree; 531a043e8c7SAdrian Chadd int err = 0; 532df892897SAdrian Chadd int port; 533a043e8c7SAdrian Chadd 534a043e8c7SAdrian Chadd /* sc->sc_switchtype is already decided in arswitch_probe() */ 535a043e8c7SAdrian Chadd sc->sc_dev = dev; 536a043e8c7SAdrian Chadd mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF); 537a043e8c7SAdrian Chadd sc->page = -1; 538a043e8c7SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev), 539a043e8c7SAdrian Chadd sizeof(sc->info.es_name)); 540a043e8c7SAdrian Chadd 5411b334c8bSAdrian Chadd /* Debugging */ 5421b334c8bSAdrian Chadd ctx = device_get_sysctl_ctx(sc->sc_dev); 5431b334c8bSAdrian Chadd tree = device_get_sysctl_tree(sc->sc_dev); 5441b334c8bSAdrian Chadd SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5451b334c8bSAdrian Chadd "debug", CTLFLAG_RW, &sc->sc_debug, 0, 5461b334c8bSAdrian Chadd "control debugging printfs"); 5471b334c8bSAdrian Chadd 54862042c97SAdrian Chadd /* Allocate a 128 entry ATU table; hopefully its big enough! */ 54962042c97SAdrian Chadd /* XXX TODO: make this per chip */ 55062042c97SAdrian Chadd sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128, 55162042c97SAdrian Chadd M_DEVBUF, M_NOWAIT); 55262042c97SAdrian Chadd if (sc->atu.entries == NULL) { 55362042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n", 55462042c97SAdrian Chadd __func__); 55562042c97SAdrian Chadd return (ENXIO); 55662042c97SAdrian Chadd } 55762042c97SAdrian Chadd sc->atu.count = 0; 55862042c97SAdrian Chadd sc->atu.size = 128; 55962042c97SAdrian Chadd 560ddbc4420SAdrian Chadd /* Default HAL methods */ 561a9ad4222SAdrian Chadd sc->hal.arswitch_port_init = ar8xxx_port_init; 562a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup; 563a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get; 5646dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans; 5652ba4bf8fSAdrian Chadd sc->hal.arswitch_hw_get_switch_macaddr = ar8xxx_hw_get_switch_macaddr; 5662ba4bf8fSAdrian Chadd sc->hal.arswitch_hw_set_switch_macaddr = ar8xxx_hw_set_switch_macaddr; 567749cac13SAdrian Chadd 5686dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup; 5696dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup; 570749cac13SAdrian Chadd 5716dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid; 5726dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid; 573749cac13SAdrian Chadd 574749cac13SAdrian Chadd sc->hal.arswitch_get_dot1q_vlan = ar8xxx_get_dot1q_vlan; 575749cac13SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan = ar8xxx_set_dot1q_vlan; 576f35f94f4SAdrian Chadd sc->hal.arswitch_flush_dot1q_vlan = ar8xxx_flush_dot1q_vlan; 577f35f94f4SAdrian Chadd sc->hal.arswitch_purge_dot1q_vlan = ar8xxx_purge_dot1q_vlan; 578749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8xxx_get_port_vlan; 579749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan; 580749cac13SAdrian Chadd 5814ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8xxx_atu_flush; 58262042c97SAdrian Chadd sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port; 58362042c97SAdrian Chadd sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default; 58462042c97SAdrian Chadd sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table; 585749cac13SAdrian Chadd 58678549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_internal; 58778549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_internal; 588ddbc4420SAdrian Chadd 589a043e8c7SAdrian Chadd /* 590a043e8c7SAdrian Chadd * Attach switch related functions 591a043e8c7SAdrian Chadd */ 59227a2ecaaSAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR7240)) 59327a2ecaaSAdrian Chadd ar7240_attach(sc); 594b2152161SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR9340)) 595b2152161SAdrian Chadd ar9340_attach(sc); 59627a2ecaaSAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8216)) 597a043e8c7SAdrian Chadd ar8216_attach(sc); 598a043e8c7SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8226)) 599a043e8c7SAdrian Chadd ar8226_attach(sc); 600a043e8c7SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8316)) 601a043e8c7SAdrian Chadd ar8316_attach(sc); 602482d268dSAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8327)) 603482d268dSAdrian Chadd ar8327_attach(sc); 60478549b94SAdrian Chadd else { 6051b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6061b334c8bSAdrian Chadd "%s: unknown switch (%d)?\n", __func__, sc->sc_switchtype); 607a043e8c7SAdrian Chadd return (ENXIO); 60878549b94SAdrian Chadd } 609a043e8c7SAdrian Chadd 610b9f07b86SLuiz Otavio O Souza /* Common defaults. */ 611a043e8c7SAdrian Chadd sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */ 612a043e8c7SAdrian Chadd 613a043e8c7SAdrian Chadd /* XXX Defaults for externally connected AR8316 */ 614a043e8c7SAdrian Chadd sc->numphys = 4; 615a043e8c7SAdrian Chadd sc->phy4cpu = 1; 616a043e8c7SAdrian Chadd sc->is_rgmii = 1; 617a043e8c7SAdrian Chadd sc->is_gmii = 0; 618b2152161SAdrian Chadd sc->is_mii = 0; 619a043e8c7SAdrian Chadd 620a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 621a043e8c7SAdrian Chadd "numphys", &sc->numphys); 622a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 623a043e8c7SAdrian Chadd "phy4cpu", &sc->phy4cpu); 624a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 625a043e8c7SAdrian Chadd "is_rgmii", &sc->is_rgmii); 626a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 627a043e8c7SAdrian Chadd "is_gmii", &sc->is_gmii); 628b2152161SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 629b2152161SAdrian Chadd "is_mii", &sc->is_mii); 630a043e8c7SAdrian Chadd 631b9f07b86SLuiz Otavio O Souza if (sc->numphys > AR8X16_NUM_PHYS) 632b9f07b86SLuiz Otavio O Souza sc->numphys = AR8X16_NUM_PHYS; 633a043e8c7SAdrian Chadd 634b9f07b86SLuiz Otavio O Souza /* Reset the switch. */ 63578549b94SAdrian Chadd if (arswitch_reset(dev)) { 6361b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6371b334c8bSAdrian Chadd "%s: arswitch_reset: failed\n", __func__); 638b9f07b86SLuiz Otavio O Souza return (ENXIO); 63978549b94SAdrian Chadd } 640a043e8c7SAdrian Chadd 641b2152161SAdrian Chadd err = sc->hal.arswitch_hw_setup(sc); 6421b334c8bSAdrian Chadd if (err != 0) { 6431b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6441b334c8bSAdrian Chadd "%s: hw_setup: err=%d\n", __func__, err); 645b2152161SAdrian Chadd return (err); 6461b334c8bSAdrian Chadd } 647b2152161SAdrian Chadd 648a043e8c7SAdrian Chadd err = sc->hal.arswitch_hw_global_setup(sc); 6491b334c8bSAdrian Chadd if (err != 0) { 6501b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6511b334c8bSAdrian Chadd "%s: hw_global_setup: err=%d\n", __func__, err); 652a043e8c7SAdrian Chadd return (err); 6531b334c8bSAdrian Chadd } 654a043e8c7SAdrian Chadd 65562042c97SAdrian Chadd /* 65662042c97SAdrian Chadd * Configure the default address table learning parameters for this 65762042c97SAdrian Chadd * switch. 65862042c97SAdrian Chadd */ 65962042c97SAdrian Chadd err = sc->hal.arswitch_atu_learn_default(sc); 66062042c97SAdrian Chadd if (err != 0) { 66162042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 66262042c97SAdrian Chadd "%s: atu_learn_default: err=%d\n", __func__, err); 66362042c97SAdrian Chadd return (err); 66462042c97SAdrian Chadd } 66562042c97SAdrian Chadd 666b9f07b86SLuiz Otavio O Souza /* Initialize the switch ports. */ 667df892897SAdrian Chadd for (port = 0; port <= sc->numphys; port++) { 668ddbc4420SAdrian Chadd sc->hal.arswitch_port_init(sc, port); 669df892897SAdrian Chadd } 670b9f07b86SLuiz Otavio O Souza 671a043e8c7SAdrian Chadd /* 672a043e8c7SAdrian Chadd * Attach the PHYs and complete the bus enumeration. 673a043e8c7SAdrian Chadd */ 674a043e8c7SAdrian Chadd err = arswitch_attach_phys(sc); 6751b334c8bSAdrian Chadd if (err != 0) { 6761b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6771b334c8bSAdrian Chadd "%s: attach_phys: err=%d\n", __func__, err); 678a043e8c7SAdrian Chadd return (err); 6791b334c8bSAdrian Chadd } 680a043e8c7SAdrian Chadd 681b9f07b86SLuiz Otavio O Souza /* Default to ingress filters off. */ 682b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, 0); 6831b334c8bSAdrian Chadd if (err != 0) { 6841b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6851b334c8bSAdrian Chadd "%s: set_vlan_mode: err=%d\n", __func__, err); 686b9f07b86SLuiz Otavio O Souza return (err); 6871b334c8bSAdrian Chadd } 688b9f07b86SLuiz Otavio O Souza 689a043e8c7SAdrian Chadd bus_generic_probe(dev); 690a043e8c7SAdrian Chadd bus_enumerate_hinted_children(dev); 691a043e8c7SAdrian Chadd err = bus_generic_attach(dev); 6921b334c8bSAdrian Chadd if (err != 0) { 6931b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6941b334c8bSAdrian Chadd "%s: bus_generic_attach: err=%d\n", __func__, err); 695a043e8c7SAdrian Chadd return (err); 6961b334c8bSAdrian Chadd } 697a043e8c7SAdrian Chadd 698a043e8c7SAdrian Chadd callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0); 699454d507aSAleksandr Rybalko 700454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc); 701a043e8c7SAdrian Chadd arswitch_tick(sc); 702454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc); 703a043e8c7SAdrian Chadd 704a043e8c7SAdrian Chadd return (err); 705a043e8c7SAdrian Chadd } 706a043e8c7SAdrian Chadd 707a043e8c7SAdrian Chadd static int 708a043e8c7SAdrian Chadd arswitch_detach(device_t dev) 709a043e8c7SAdrian Chadd { 710a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 711a043e8c7SAdrian Chadd int i; 712a043e8c7SAdrian Chadd 713a043e8c7SAdrian Chadd callout_drain(&sc->callout_tick); 714a043e8c7SAdrian Chadd 715a043e8c7SAdrian Chadd for (i=0; i < sc->numphys; i++) { 716a043e8c7SAdrian Chadd if (sc->miibus[i] != NULL) 717a043e8c7SAdrian Chadd device_delete_child(dev, sc->miibus[i]); 718a043e8c7SAdrian Chadd if (sc->ifp[i] != NULL) 719a043e8c7SAdrian Chadd if_free(sc->ifp[i]); 720a043e8c7SAdrian Chadd free(sc->ifname[i], M_DEVBUF); 721a043e8c7SAdrian Chadd } 722a043e8c7SAdrian Chadd 72362042c97SAdrian Chadd free(sc->atu.entries, M_DEVBUF); 72462042c97SAdrian Chadd 725a043e8c7SAdrian Chadd bus_generic_detach(dev); 726a043e8c7SAdrian Chadd mtx_destroy(&sc->sc_mtx); 727a043e8c7SAdrian Chadd 728a043e8c7SAdrian Chadd return (0); 729a043e8c7SAdrian Chadd } 730a043e8c7SAdrian Chadd 731a043e8c7SAdrian Chadd /* 732a043e8c7SAdrian Chadd * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to 733a043e8c7SAdrian Chadd * port 2, etc. 734a043e8c7SAdrian Chadd */ 735a043e8c7SAdrian Chadd static inline int 736a043e8c7SAdrian Chadd arswitch_portforphy(int phy) 737a043e8c7SAdrian Chadd { 738a043e8c7SAdrian Chadd return (phy+1); 739a043e8c7SAdrian Chadd } 740a043e8c7SAdrian Chadd 741a043e8c7SAdrian Chadd static inline struct mii_data * 742a043e8c7SAdrian Chadd arswitch_miiforport(struct arswitch_softc *sc, int port) 743a043e8c7SAdrian Chadd { 744a043e8c7SAdrian Chadd int phy = port-1; 745a043e8c7SAdrian Chadd 746a043e8c7SAdrian Chadd if (phy < 0 || phy >= sc->numphys) 747a043e8c7SAdrian Chadd return (NULL); 748a043e8c7SAdrian Chadd return (device_get_softc(sc->miibus[phy])); 749a043e8c7SAdrian Chadd } 750a043e8c7SAdrian Chadd 751a043e8c7SAdrian Chadd static inline struct ifnet * 752a043e8c7SAdrian Chadd arswitch_ifpforport(struct arswitch_softc *sc, int port) 753a043e8c7SAdrian Chadd { 754a043e8c7SAdrian Chadd int phy = port-1; 755a043e8c7SAdrian Chadd 756a043e8c7SAdrian Chadd if (phy < 0 || phy >= sc->numphys) 757a043e8c7SAdrian Chadd return (NULL); 758a043e8c7SAdrian Chadd return (sc->ifp[phy]); 759a043e8c7SAdrian Chadd } 760a043e8c7SAdrian Chadd 761a043e8c7SAdrian Chadd /* 762a043e8c7SAdrian Chadd * Convert port status to ifmedia. 763a043e8c7SAdrian Chadd */ 764a043e8c7SAdrian Chadd static void 765a043e8c7SAdrian Chadd arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active) 766a043e8c7SAdrian Chadd { 767a043e8c7SAdrian Chadd *media_active = IFM_ETHER; 768a043e8c7SAdrian Chadd *media_status = IFM_AVALID; 769a043e8c7SAdrian Chadd 770a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0) 771a043e8c7SAdrian Chadd *media_status |= IFM_ACTIVE; 772a043e8c7SAdrian Chadd else { 773a043e8c7SAdrian Chadd *media_active |= IFM_NONE; 774a043e8c7SAdrian Chadd return; 775a043e8c7SAdrian Chadd } 776a043e8c7SAdrian Chadd switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) { 777a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_10: 778a043e8c7SAdrian Chadd *media_active |= IFM_10_T; 779a043e8c7SAdrian Chadd break; 780a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_100: 781a043e8c7SAdrian Chadd *media_active |= IFM_100_TX; 782a043e8c7SAdrian Chadd break; 783a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_1000: 784a043e8c7SAdrian Chadd *media_active |= IFM_1000_T; 785a043e8c7SAdrian Chadd break; 786a043e8c7SAdrian Chadd } 787a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0) 788a043e8c7SAdrian Chadd *media_active |= IFM_FDX; 789a043e8c7SAdrian Chadd else 790a043e8c7SAdrian Chadd *media_active |= IFM_HDX; 791a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0) 792a043e8c7SAdrian Chadd *media_active |= IFM_ETH_TXPAUSE; 793a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0) 794a043e8c7SAdrian Chadd *media_active |= IFM_ETH_RXPAUSE; 795a043e8c7SAdrian Chadd } 796a043e8c7SAdrian Chadd 797a043e8c7SAdrian Chadd /* 798a043e8c7SAdrian Chadd * Poll the status for all PHYs. We're using the switch port status because 799a043e8c7SAdrian Chadd * thats a lot quicker to read than talking to all the PHYs. Care must be 800a043e8c7SAdrian Chadd * taken that the resulting ifmedia_active is identical to what the PHY will 801a043e8c7SAdrian Chadd * compute, or gratuitous link status changes will occur whenever the PHYs 802a043e8c7SAdrian Chadd * update function is called. 803a043e8c7SAdrian Chadd */ 804a043e8c7SAdrian Chadd static void 805a043e8c7SAdrian Chadd arswitch_miipollstat(struct arswitch_softc *sc) 806a043e8c7SAdrian Chadd { 807a043e8c7SAdrian Chadd int i; 808a043e8c7SAdrian Chadd struct mii_data *mii; 809a043e8c7SAdrian Chadd struct mii_softc *miisc; 810a043e8c7SAdrian Chadd int portstatus; 8114ff2f60dSAdrian Chadd int port_flap = 0; 812a043e8c7SAdrian Chadd 813454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 814454d507aSAleksandr Rybalko 815a043e8c7SAdrian Chadd for (i = 0; i < sc->numphys; i++) { 816a043e8c7SAdrian Chadd if (sc->miibus[i] == NULL) 817a043e8c7SAdrian Chadd continue; 818a043e8c7SAdrian Chadd mii = device_get_softc(sc->miibus[i]); 819ddbc4420SAdrian Chadd /* XXX This would be nice to have abstracted out to be per-chip */ 820ddbc4420SAdrian Chadd /* AR8327/AR8337 has a different register base */ 821ddbc4420SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) 822ddbc4420SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev, 823ddbc4420SAdrian Chadd AR8327_REG_PORT_STATUS(arswitch_portforphy(i))); 824ddbc4420SAdrian Chadd else 825a043e8c7SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev, 826a043e8c7SAdrian Chadd AR8X16_REG_PORT_STS(arswitch_portforphy(i))); 8271b334c8bSAdrian Chadd #if 1 8281b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "p[%d]=0x%08x (%b)\n", 829b2152161SAdrian Chadd i, 830a043e8c7SAdrian Chadd portstatus, 8311b334c8bSAdrian Chadd portstatus, 832a043e8c7SAdrian Chadd "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7" 833a043e8c7SAdrian Chadd "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE"); 834a043e8c7SAdrian Chadd #endif 8354ff2f60dSAdrian Chadd /* 8364ff2f60dSAdrian Chadd * If the current status is down, but we have a link 8374ff2f60dSAdrian Chadd * status showing up, we need to do an ATU flush. 8384ff2f60dSAdrian Chadd */ 8394ff2f60dSAdrian Chadd if ((mii->mii_media_status & IFM_ACTIVE) == 0 && 8404ff2f60dSAdrian Chadd (portstatus & AR8X16_PORT_STS_LINK_UP) != 0) { 8414ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: port %d: port -> UP\n", 8424ff2f60dSAdrian Chadd __func__, 8434ff2f60dSAdrian Chadd i); 8444ff2f60dSAdrian Chadd port_flap = 1; 8454ff2f60dSAdrian Chadd } 8464ff2f60dSAdrian Chadd /* 8474ff2f60dSAdrian Chadd * and maybe if a port goes up->down? 8484ff2f60dSAdrian Chadd */ 8494ff2f60dSAdrian Chadd if ((mii->mii_media_status & IFM_ACTIVE) != 0 && 8504ff2f60dSAdrian Chadd (portstatus & AR8X16_PORT_STS_LINK_UP) == 0) { 8514ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: port %d: port -> DOWN\n", 8524ff2f60dSAdrian Chadd __func__, 8534ff2f60dSAdrian Chadd i); 8544ff2f60dSAdrian Chadd port_flap = 1; 8554ff2f60dSAdrian Chadd } 856a043e8c7SAdrian Chadd arswitch_update_ifmedia(portstatus, &mii->mii_media_status, 857a043e8c7SAdrian Chadd &mii->mii_media_active); 858a043e8c7SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 859a043e8c7SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 860a043e8c7SAdrian Chadd miisc->mii_inst) 861a043e8c7SAdrian Chadd continue; 862a043e8c7SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT); 863a043e8c7SAdrian Chadd } 864a043e8c7SAdrian Chadd } 8654ff2f60dSAdrian Chadd 8664ff2f60dSAdrian Chadd /* If a port went from down->up, flush the ATU */ 8674ff2f60dSAdrian Chadd if (port_flap) 8684ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush(sc); 869a043e8c7SAdrian Chadd } 870a043e8c7SAdrian Chadd 871a043e8c7SAdrian Chadd static void 872a043e8c7SAdrian Chadd arswitch_tick(void *arg) 873a043e8c7SAdrian Chadd { 874a043e8c7SAdrian Chadd struct arswitch_softc *sc = arg; 875a043e8c7SAdrian Chadd 876a043e8c7SAdrian Chadd arswitch_miipollstat(sc); 877a043e8c7SAdrian Chadd callout_reset(&sc->callout_tick, hz, arswitch_tick, sc); 878a043e8c7SAdrian Chadd } 879a043e8c7SAdrian Chadd 880454d507aSAleksandr Rybalko static void 881454d507aSAleksandr Rybalko arswitch_lock(device_t dev) 882454d507aSAleksandr Rybalko { 883454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev); 884454d507aSAleksandr Rybalko 885454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 886454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc); 887454d507aSAleksandr Rybalko } 888454d507aSAleksandr Rybalko 889454d507aSAleksandr Rybalko static void 890454d507aSAleksandr Rybalko arswitch_unlock(device_t dev) 891454d507aSAleksandr Rybalko { 892454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev); 893454d507aSAleksandr Rybalko 894454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 895454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc); 896454d507aSAleksandr Rybalko } 897454d507aSAleksandr Rybalko 898a043e8c7SAdrian Chadd static etherswitch_info_t * 899a043e8c7SAdrian Chadd arswitch_getinfo(device_t dev) 900a043e8c7SAdrian Chadd { 901a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 902a043e8c7SAdrian Chadd 903a043e8c7SAdrian Chadd return (&sc->info); 904a043e8c7SAdrian Chadd } 905a043e8c7SAdrian Chadd 906a043e8c7SAdrian Chadd static int 907a9ad4222SAdrian Chadd ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p) 908a043e8c7SAdrian Chadd { 909b9f07b86SLuiz Otavio O Souza uint32_t reg; 910b9f07b86SLuiz Otavio O Souza 911b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc); 912b9f07b86SLuiz Otavio O Souza 913b9f07b86SLuiz Otavio O Souza /* Retrieve the PVID. */ 9146dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid); 915b9f07b86SLuiz Otavio O Souza 916b9f07b86SLuiz Otavio O Souza /* Port flags. */ 917b9f07b86SLuiz Otavio O Souza reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port)); 918b9f07b86SLuiz Otavio O Souza if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG) 919b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG; 920b9f07b86SLuiz Otavio O Souza reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 921b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD) 922b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 923b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP) 924b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_STRIPTAG; 925b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 926a043e8c7SAdrian Chadd 927a9ad4222SAdrian Chadd return (0); 928a9ad4222SAdrian Chadd } 929a9ad4222SAdrian Chadd 930a9ad4222SAdrian Chadd static int 931749cac13SAdrian Chadd arswitch_is_cpuport(struct arswitch_softc *sc, int port) 932749cac13SAdrian Chadd { 933749cac13SAdrian Chadd 934749cac13SAdrian Chadd return ((port == AR8X16_PORT_CPU) || 935749cac13SAdrian Chadd ((AR8X16_IS_SWITCH(sc, AR8327) && 936749cac13SAdrian Chadd port == AR8327_PORT_GMAC6))); 937749cac13SAdrian Chadd } 938749cac13SAdrian Chadd 939749cac13SAdrian Chadd static int 940a9ad4222SAdrian Chadd arswitch_getport(device_t dev, etherswitch_port_t *p) 941a9ad4222SAdrian Chadd { 942a9ad4222SAdrian Chadd struct arswitch_softc *sc; 943a9ad4222SAdrian Chadd struct mii_data *mii; 944a9ad4222SAdrian Chadd struct ifmediareq *ifmr; 945a9ad4222SAdrian Chadd int err; 946a9ad4222SAdrian Chadd 947a9ad4222SAdrian Chadd sc = device_get_softc(dev); 948749cac13SAdrian Chadd /* XXX +1 is for AR8327; should make this configurable! */ 949749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports) 950a9ad4222SAdrian Chadd return (ENXIO); 951a9ad4222SAdrian Chadd 952a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_get(sc, p); 953a9ad4222SAdrian Chadd if (err != 0) 954a9ad4222SAdrian Chadd return (err); 955a9ad4222SAdrian Chadd 956a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port); 957749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) { 958a043e8c7SAdrian Chadd /* fill in fixed values for CPU port */ 959a9ad4222SAdrian Chadd /* XXX is this valid in all cases? */ 960f47857dcSAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 961b9f07b86SLuiz Otavio O Souza ifmr = &p->es_ifmr; 962a043e8c7SAdrian Chadd ifmr->ifm_count = 0; 963a043e8c7SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 964a043e8c7SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 965a043e8c7SAdrian Chadd ifmr->ifm_mask = 0; 966a043e8c7SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 967a043e8c7SAdrian Chadd } else if (mii != NULL) { 968a043e8c7SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 969a043e8c7SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 970a043e8c7SAdrian Chadd if (err) 971a043e8c7SAdrian Chadd return (err); 972a043e8c7SAdrian Chadd } else { 973a043e8c7SAdrian Chadd return (ENXIO); 974a043e8c7SAdrian Chadd } 975c94dc808SAdrian Chadd 976c94dc808SAdrian Chadd if (!arswitch_is_cpuport(sc, p->es_port) && 977c94dc808SAdrian Chadd AR8X16_IS_SWITCH(sc, AR8327)) { 978c94dc808SAdrian Chadd int led; 979c94dc808SAdrian Chadd p->es_nleds = 3; 980c94dc808SAdrian Chadd 981c94dc808SAdrian Chadd for (led = 0; led < p->es_nleds; led++) 982c94dc808SAdrian Chadd { 983c94dc808SAdrian Chadd int style; 984c94dc808SAdrian Chadd uint32_t val; 985c94dc808SAdrian Chadd 986c94dc808SAdrian Chadd /* Find the right style enum for our pattern */ 987c94dc808SAdrian Chadd val = arswitch_readreg(dev, 988c94dc808SAdrian Chadd ar8327_led_mapping[p->es_port-1][led].reg); 989c94dc808SAdrian Chadd val = (val>>ar8327_led_mapping[p->es_port-1][led].shift)&0x03; 990c94dc808SAdrian Chadd 991c94dc808SAdrian Chadd for (style = 0; style < ETHERSWITCH_PORT_LED_MAX; style++) 992c94dc808SAdrian Chadd { 993c94dc808SAdrian Chadd if (led_pattern_table[style] == val) break; 994c94dc808SAdrian Chadd } 995c94dc808SAdrian Chadd 996c94dc808SAdrian Chadd /* can't happen */ 997c94dc808SAdrian Chadd if (style == ETHERSWITCH_PORT_LED_MAX) 998c94dc808SAdrian Chadd style = ETHERSWITCH_PORT_LED_DEFAULT; 999c94dc808SAdrian Chadd 1000c94dc808SAdrian Chadd p->es_led[led] = style; 1001c94dc808SAdrian Chadd } 1002c94dc808SAdrian Chadd } else 1003c94dc808SAdrian Chadd { 1004c94dc808SAdrian Chadd p->es_nleds = 0; 1005c94dc808SAdrian Chadd } 1006c94dc808SAdrian Chadd 1007a043e8c7SAdrian Chadd return (0); 1008a043e8c7SAdrian Chadd } 1009a043e8c7SAdrian Chadd 1010a043e8c7SAdrian Chadd static int 1011a9ad4222SAdrian Chadd ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p) 1012a043e8c7SAdrian Chadd { 1013b9f07b86SLuiz Otavio O Souza uint32_t reg; 1014a9ad4222SAdrian Chadd int err; 1015a043e8c7SAdrian Chadd 1016b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc); 1017a9ad4222SAdrian Chadd 1018b9f07b86SLuiz Otavio O Souza /* Set the PVID. */ 1019b9f07b86SLuiz Otavio O Souza if (p->es_pvid != 0) 10206dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid); 1021b9f07b86SLuiz Otavio O Souza 1022b9f07b86SLuiz Otavio O Souza /* Mutually exclusive. */ 1023b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG && 1024b9f07b86SLuiz Otavio O Souza p->es_flags & ETHERSWITCH_PORT_STRIPTAG) { 1025b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 1026b9f07b86SLuiz Otavio O Souza return (EINVAL); 1027b9f07b86SLuiz Otavio O Souza } 1028b9f07b86SLuiz Otavio O Souza 1029b9f07b86SLuiz Otavio O Souza reg = 0; 1030b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG) 1031b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_DOUBLE_TAG; 1032b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 1033b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD << 1034b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 1035b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 1036b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP << 1037b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 1038b9f07b86SLuiz Otavio O Souza 1039b9f07b86SLuiz Otavio O Souza err = arswitch_modifyreg(sc->sc_dev, 1040b9f07b86SLuiz Otavio O Souza AR8X16_REG_PORT_CTRL(p->es_port), 1041b9f07b86SLuiz Otavio O Souza 0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT | 1042b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_DOUBLE_TAG, reg); 1043b9f07b86SLuiz Otavio O Souza 1044b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 1045a9ad4222SAdrian Chadd return (err); 1046a9ad4222SAdrian Chadd } 1047a9ad4222SAdrian Chadd 1048a9ad4222SAdrian Chadd static int 1049a9ad4222SAdrian Chadd arswitch_setport(device_t dev, etherswitch_port_t *p) 1050a9ad4222SAdrian Chadd { 1051c94dc808SAdrian Chadd int err, i; 1052a9ad4222SAdrian Chadd struct arswitch_softc *sc; 1053a9ad4222SAdrian Chadd struct ifmedia *ifm; 1054a9ad4222SAdrian Chadd struct mii_data *mii; 1055a9ad4222SAdrian Chadd struct ifnet *ifp; 1056a9ad4222SAdrian Chadd 1057a9ad4222SAdrian Chadd sc = device_get_softc(dev); 1058749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports) 1059a9ad4222SAdrian Chadd return (ENXIO); 1060a9ad4222SAdrian Chadd 1061a9ad4222SAdrian Chadd /* Port flags. */ 1062a9ad4222SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 1063a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_setup(sc, p); 1064b9f07b86SLuiz Otavio O Souza if (err) 1065b9f07b86SLuiz Otavio O Souza return (err); 1066b9f07b86SLuiz Otavio O Souza } 1067b9f07b86SLuiz Otavio O Souza 1068c94dc808SAdrian Chadd /* Do not allow media or led changes on CPU port. */ 1069749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) 1070b9f07b86SLuiz Otavio O Souza return (0); 1071a043e8c7SAdrian Chadd 1072c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) 1073c94dc808SAdrian Chadd { 1074c94dc808SAdrian Chadd for (i = 0; i < 3; i++) 1075c94dc808SAdrian Chadd { 1076c94dc808SAdrian Chadd int err; 1077c94dc808SAdrian Chadd err = arswitch_setled(sc, p->es_port-1, i, p->es_led[i]); 1078c94dc808SAdrian Chadd if (err) 1079c94dc808SAdrian Chadd return (err); 1080c94dc808SAdrian Chadd } 1081c94dc808SAdrian Chadd } 1082c94dc808SAdrian Chadd 1083a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port); 1084a043e8c7SAdrian Chadd if (mii == NULL) 1085a043e8c7SAdrian Chadd return (ENXIO); 1086a043e8c7SAdrian Chadd 1087a043e8c7SAdrian Chadd ifp = arswitch_ifpforport(sc, p->es_port); 1088a043e8c7SAdrian Chadd 1089a043e8c7SAdrian Chadd ifm = &mii->mii_media; 1090b9f07b86SLuiz Otavio O Souza return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); 1091a043e8c7SAdrian Chadd } 1092a043e8c7SAdrian Chadd 1093c94dc808SAdrian Chadd static int 1094c94dc808SAdrian Chadd arswitch_setled(struct arswitch_softc *sc, int phy, int led, int style) 1095c94dc808SAdrian Chadd { 1096c94dc808SAdrian Chadd int shift; 10976d011946SKristof Provost int err; 1098c94dc808SAdrian Chadd 1099c94dc808SAdrian Chadd if (phy < 0 || phy > sc->numphys) 1100c94dc808SAdrian Chadd return EINVAL; 1101c94dc808SAdrian Chadd 1102c94dc808SAdrian Chadd if (style < 0 || style > ETHERSWITCH_PORT_LED_MAX) 1103c94dc808SAdrian Chadd return (EINVAL); 1104c94dc808SAdrian Chadd 11056d011946SKristof Provost ARSWITCH_LOCK(sc); 11066d011946SKristof Provost 1107c94dc808SAdrian Chadd shift = ar8327_led_mapping[phy][led].shift; 11086d011946SKristof Provost err = (arswitch_modifyreg(sc->sc_dev, 1109c94dc808SAdrian Chadd ar8327_led_mapping[phy][led].reg, 1110c94dc808SAdrian Chadd 0x03 << shift, led_pattern_table[style] << shift)); 11116d011946SKristof Provost ARSWITCH_UNLOCK(sc); 11126d011946SKristof Provost 11136d011946SKristof Provost return (err); 1114c94dc808SAdrian Chadd } 1115c94dc808SAdrian Chadd 1116a043e8c7SAdrian Chadd static void 1117a043e8c7SAdrian Chadd arswitch_statchg(device_t dev) 1118a043e8c7SAdrian Chadd { 11191b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 1120a043e8c7SAdrian Chadd 11211b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__); 1122a043e8c7SAdrian Chadd } 1123a043e8c7SAdrian Chadd 1124a043e8c7SAdrian Chadd static int 1125a043e8c7SAdrian Chadd arswitch_ifmedia_upd(struct ifnet *ifp) 1126a043e8c7SAdrian Chadd { 1127a043e8c7SAdrian Chadd struct arswitch_softc *sc = ifp->if_softc; 1128a043e8c7SAdrian Chadd struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit); 1129a043e8c7SAdrian Chadd 1130a043e8c7SAdrian Chadd if (mii == NULL) 1131a043e8c7SAdrian Chadd return (ENXIO); 1132a043e8c7SAdrian Chadd mii_mediachg(mii); 1133a043e8c7SAdrian Chadd return (0); 1134a043e8c7SAdrian Chadd } 1135a043e8c7SAdrian Chadd 1136a043e8c7SAdrian Chadd static void 1137a043e8c7SAdrian Chadd arswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 1138a043e8c7SAdrian Chadd { 1139a043e8c7SAdrian Chadd struct arswitch_softc *sc = ifp->if_softc; 1140a043e8c7SAdrian Chadd struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit); 1141a043e8c7SAdrian Chadd 11421b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__); 1143a043e8c7SAdrian Chadd 1144a043e8c7SAdrian Chadd if (mii == NULL) 1145a043e8c7SAdrian Chadd return; 1146a043e8c7SAdrian Chadd mii_pollstat(mii); 1147a043e8c7SAdrian Chadd ifmr->ifm_active = mii->mii_media_active; 1148a043e8c7SAdrian Chadd ifmr->ifm_status = mii->mii_media_status; 1149a043e8c7SAdrian Chadd } 1150a043e8c7SAdrian Chadd 1151b9f07b86SLuiz Otavio O Souza static int 1152b9f07b86SLuiz Otavio O Souza arswitch_getconf(device_t dev, etherswitch_conf_t *conf) 1153b9f07b86SLuiz Otavio O Souza { 1154b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc; 11552ba4bf8fSAdrian Chadd int ret; 1156b9f07b86SLuiz Otavio O Souza 1157b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev); 1158b9f07b86SLuiz Otavio O Souza 1159b9f07b86SLuiz Otavio O Souza /* Return the VLAN mode. */ 1160b9f07b86SLuiz Otavio O Souza conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 1161b9f07b86SLuiz Otavio O Souza conf->vlan_mode = sc->vlan_mode; 1162b9f07b86SLuiz Otavio O Souza 11632ba4bf8fSAdrian Chadd /* Return the switch ethernet address. */ 11642ba4bf8fSAdrian Chadd ret = sc->hal.arswitch_hw_get_switch_macaddr(sc, 11652ba4bf8fSAdrian Chadd &conf->switch_macaddr); 11662ba4bf8fSAdrian Chadd if (ret == 0) { 11672ba4bf8fSAdrian Chadd conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR; 11682ba4bf8fSAdrian Chadd } 11692ba4bf8fSAdrian Chadd 1170b9f07b86SLuiz Otavio O Souza return (0); 1171b9f07b86SLuiz Otavio O Souza } 1172b9f07b86SLuiz Otavio O Souza 1173b9f07b86SLuiz Otavio O Souza static int 1174b9f07b86SLuiz Otavio O Souza arswitch_setconf(device_t dev, etherswitch_conf_t *conf) 1175b9f07b86SLuiz Otavio O Souza { 1176b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc; 1177b9f07b86SLuiz Otavio O Souza int err; 1178b9f07b86SLuiz Otavio O Souza 1179b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev); 1180b9f07b86SLuiz Otavio O Souza 1181b9f07b86SLuiz Otavio O Souza /* Set the VLAN mode. */ 1182b9f07b86SLuiz Otavio O Souza if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { 1183b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, conf->vlan_mode); 1184b9f07b86SLuiz Otavio O Souza if (err != 0) 1185b9f07b86SLuiz Otavio O Souza return (err); 1186b9f07b86SLuiz Otavio O Souza } 1187b9f07b86SLuiz Otavio O Souza 11882ba4bf8fSAdrian Chadd /* TODO: Set the switch ethernet address. */ 11892ba4bf8fSAdrian Chadd 1190b9f07b86SLuiz Otavio O Souza return (0); 1191b9f07b86SLuiz Otavio O Souza } 1192b9f07b86SLuiz Otavio O Souza 11936dcbabd7SAdrian Chadd static int 119462042c97SAdrian Chadd arswitch_atu_flush_all(device_t dev) 119562042c97SAdrian Chadd { 119662042c97SAdrian Chadd struct arswitch_softc *sc; 119762042c97SAdrian Chadd int err; 119862042c97SAdrian Chadd 119962042c97SAdrian Chadd sc = device_get_softc(dev); 120062042c97SAdrian Chadd ARSWITCH_LOCK(sc); 120162042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush(sc); 120262042c97SAdrian Chadd /* Invalidate cached ATU */ 120362042c97SAdrian Chadd sc->atu.count = 0; 120462042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 120562042c97SAdrian Chadd return (err); 120662042c97SAdrian Chadd } 120762042c97SAdrian Chadd 120862042c97SAdrian Chadd static int 120962042c97SAdrian Chadd arswitch_atu_flush_port(device_t dev, int port) 121062042c97SAdrian Chadd { 121162042c97SAdrian Chadd struct arswitch_softc *sc; 121262042c97SAdrian Chadd int err; 121362042c97SAdrian Chadd 121462042c97SAdrian Chadd sc = device_get_softc(dev); 121562042c97SAdrian Chadd ARSWITCH_LOCK(sc); 121662042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush_port(sc, port); 121762042c97SAdrian Chadd /* Invalidate cached ATU */ 121862042c97SAdrian Chadd sc->atu.count = 0; 121962042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 122062042c97SAdrian Chadd return (err); 122162042c97SAdrian Chadd } 122262042c97SAdrian Chadd 122362042c97SAdrian Chadd static int 122462042c97SAdrian Chadd arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table) 122562042c97SAdrian Chadd { 122662042c97SAdrian Chadd struct arswitch_softc *sc; 122762042c97SAdrian Chadd int err, nitems; 122862042c97SAdrian Chadd 122962042c97SAdrian Chadd sc = device_get_softc(dev); 123062042c97SAdrian Chadd 123162042c97SAdrian Chadd ARSWITCH_LOCK(sc); 123262042c97SAdrian Chadd /* Initial setup */ 123362042c97SAdrian Chadd nitems = 0; 123462042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0); 123562042c97SAdrian Chadd 123662042c97SAdrian Chadd /* fetch - ideally yes we'd fetch into a separate table then switch */ 12377ed08319SAdrian Chadd while (err == 0 && nitems < sc->atu.size) { 123862042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, 123962042c97SAdrian Chadd &sc->atu.entries[nitems], 1); 124062042c97SAdrian Chadd if (err == 0) { 124162042c97SAdrian Chadd sc->atu.entries[nitems].id = nitems; 124262042c97SAdrian Chadd nitems++; 124362042c97SAdrian Chadd } 124462042c97SAdrian Chadd } 124562042c97SAdrian Chadd sc->atu.count = nitems; 124662042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 124762042c97SAdrian Chadd 124862042c97SAdrian Chadd table->es_nitems = nitems; 124962042c97SAdrian Chadd 125062042c97SAdrian Chadd return (0); 125162042c97SAdrian Chadd } 125262042c97SAdrian Chadd 125362042c97SAdrian Chadd static int 125462042c97SAdrian Chadd arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e) 125562042c97SAdrian Chadd { 125662042c97SAdrian Chadd struct arswitch_softc *sc; 125762042c97SAdrian Chadd int id; 125862042c97SAdrian Chadd 125962042c97SAdrian Chadd sc = device_get_softc(dev); 126062042c97SAdrian Chadd id = e->id; 126162042c97SAdrian Chadd 126262042c97SAdrian Chadd ARSWITCH_LOCK(sc); 126362042c97SAdrian Chadd if (id > sc->atu.count) { 126462042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 126562042c97SAdrian Chadd return (ENOENT); 126662042c97SAdrian Chadd } 126762042c97SAdrian Chadd 126862042c97SAdrian Chadd memcpy(e, &sc->atu.entries[id], sizeof(*e)); 126962042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 127062042c97SAdrian Chadd return (0); 127162042c97SAdrian Chadd } 127262042c97SAdrian Chadd 127362042c97SAdrian Chadd static int 12746dcbabd7SAdrian Chadd arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e) 12756dcbabd7SAdrian Chadd { 12766dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 12776dcbabd7SAdrian Chadd 12786dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_getvgroup(sc, e)); 12796dcbabd7SAdrian Chadd } 12806dcbabd7SAdrian Chadd 12816dcbabd7SAdrian Chadd static int 12826dcbabd7SAdrian Chadd arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e) 12836dcbabd7SAdrian Chadd { 12846dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 12856dcbabd7SAdrian Chadd 12866dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_setvgroup(sc, e)); 12876dcbabd7SAdrian Chadd } 12886dcbabd7SAdrian Chadd 128978549b94SAdrian Chadd static int 129078549b94SAdrian Chadd arswitch_readphy(device_t dev, int phy, int reg) 129178549b94SAdrian Chadd { 129278549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 129378549b94SAdrian Chadd 129478549b94SAdrian Chadd return (sc->hal.arswitch_phy_read(dev, phy, reg)); 129578549b94SAdrian Chadd } 129678549b94SAdrian Chadd 129778549b94SAdrian Chadd static int 129878549b94SAdrian Chadd arswitch_writephy(device_t dev, int phy, int reg, int val) 129978549b94SAdrian Chadd { 130078549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 130178549b94SAdrian Chadd 130278549b94SAdrian Chadd return (sc->hal.arswitch_phy_write(dev, phy, reg, val)); 130378549b94SAdrian Chadd } 130478549b94SAdrian Chadd 1305a043e8c7SAdrian Chadd static device_method_t arswitch_methods[] = { 1306a043e8c7SAdrian Chadd /* Device interface */ 1307a043e8c7SAdrian Chadd DEVMETHOD(device_probe, arswitch_probe), 1308a043e8c7SAdrian Chadd DEVMETHOD(device_attach, arswitch_attach), 1309a043e8c7SAdrian Chadd DEVMETHOD(device_detach, arswitch_detach), 1310a043e8c7SAdrian Chadd 1311a043e8c7SAdrian Chadd /* bus interface */ 1312a043e8c7SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 1313a043e8c7SAdrian Chadd 1314a043e8c7SAdrian Chadd /* MII interface */ 1315a043e8c7SAdrian Chadd DEVMETHOD(miibus_readreg, arswitch_readphy), 1316a043e8c7SAdrian Chadd DEVMETHOD(miibus_writereg, arswitch_writephy), 1317a043e8c7SAdrian Chadd DEVMETHOD(miibus_statchg, arswitch_statchg), 1318a043e8c7SAdrian Chadd 1319a043e8c7SAdrian Chadd /* MDIO interface */ 1320a043e8c7SAdrian Chadd DEVMETHOD(mdio_readreg, arswitch_readphy), 1321a043e8c7SAdrian Chadd DEVMETHOD(mdio_writereg, arswitch_writephy), 1322a043e8c7SAdrian Chadd 1323a043e8c7SAdrian Chadd /* etherswitch interface */ 1324454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_lock, arswitch_lock), 1325454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_unlock, arswitch_unlock), 1326a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getinfo, arswitch_getinfo), 1327a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readreg, arswitch_readreg), 1328a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writereg, arswitch_writereg), 1329a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, arswitch_readphy), 1330a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, arswitch_writephy), 1331a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getport, arswitch_getport), 1332a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setport, arswitch_setport), 1333a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, arswitch_getvgroup), 1334a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, arswitch_setvgroup), 1335b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_getconf, arswitch_getconf), 1336b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_setconf, arswitch_setconf), 133762042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all), 133862042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port), 133962042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table), 134062042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry), 1341a043e8c7SAdrian Chadd 1342a043e8c7SAdrian Chadd DEVMETHOD_END 1343a043e8c7SAdrian Chadd }; 1344a043e8c7SAdrian Chadd 1345a043e8c7SAdrian Chadd DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods, 1346a043e8c7SAdrian Chadd sizeof(struct arswitch_softc)); 1347a043e8c7SAdrian Chadd static devclass_t arswitch_devclass; 1348a043e8c7SAdrian Chadd 1349a043e8c7SAdrian Chadd DRIVER_MODULE(arswitch, mdio, arswitch_driver, arswitch_devclass, 0, 0); 13503e38757dSJohn Baldwin DRIVER_MODULE(miibus, arswitch, miibus_driver, 0, 0); 1351*8933f7d6SJohn Baldwin DRIVER_MODULE(mdio, arswitch, mdio_driver, 0, 0); 1352a043e8c7SAdrian Chadd DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, etherswitch_devclass, 0, 0); 1353a043e8c7SAdrian Chadd MODULE_VERSION(arswitch, 1); 1354a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */ 1355a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */ 1356