1a043e8c7SAdrian Chadd /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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 30a043e8c7SAdrian Chadd #include <sys/param.h> 31a043e8c7SAdrian Chadd #include <sys/bus.h> 32a043e8c7SAdrian Chadd #include <sys/errno.h> 33a043e8c7SAdrian Chadd #include <sys/kernel.h> 34104dc214SGleb Smirnoff #include <sys/malloc.h> 35a043e8c7SAdrian Chadd #include <sys/module.h> 36a043e8c7SAdrian Chadd #include <sys/socket.h> 37a043e8c7SAdrian Chadd #include <sys/sockio.h> 38a043e8c7SAdrian Chadd #include <sys/sysctl.h> 39a043e8c7SAdrian Chadd #include <sys/systm.h> 40a043e8c7SAdrian Chadd 41a043e8c7SAdrian Chadd #include <net/if.h> 42104dc214SGleb Smirnoff #include <net/if_var.h> 43a043e8c7SAdrian Chadd #include <net/if_arp.h> 44a043e8c7SAdrian Chadd #include <net/ethernet.h> 45a043e8c7SAdrian Chadd #include <net/if_dl.h> 46a043e8c7SAdrian Chadd #include <net/if_media.h> 47a043e8c7SAdrian Chadd #include <net/if_types.h> 48a043e8c7SAdrian Chadd 49a043e8c7SAdrian Chadd #include <machine/bus.h> 50efce3748SRui Paulo #include <dev/iicbus/iic.h> 51a043e8c7SAdrian Chadd #include <dev/iicbus/iiconf.h> 52a043e8c7SAdrian Chadd #include <dev/iicbus/iicbus.h> 53a043e8c7SAdrian Chadd #include <dev/mii/mii.h> 54a043e8c7SAdrian Chadd #include <dev/mii/miivar.h> 5571e8eac4SAdrian Chadd #include <dev/mdio/mdio.h> 56a043e8c7SAdrian Chadd 57a043e8c7SAdrian Chadd #include <dev/etherswitch/etherswitch.h> 58a043e8c7SAdrian Chadd 59a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchreg.h> 60a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchvar.h> 61a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_reg.h> 62a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_phy.h> 63b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_vlans.h> 64a043e8c7SAdrian Chadd 65a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8216.h> 66a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8226.h> 67a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8316.h> 68482d268dSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8327.h> 69a043e8c7SAdrian Chadd 70a043e8c7SAdrian Chadd #include "mdio_if.h" 71a043e8c7SAdrian Chadd #include "miibus_if.h" 72a043e8c7SAdrian Chadd #include "etherswitch_if.h" 73a043e8c7SAdrian Chadd 74c94dc808SAdrian Chadd /* Map ETHERSWITCH_PORT_LED_* to Atheros pattern codes */ 75c94dc808SAdrian Chadd static int led_pattern_table[] = { 76c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_DEFAULT] = 0x3, 77c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_ON] = 0x2, 78c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_OFF] = 0x0, 79c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_BLINK] = 0x1 80c94dc808SAdrian Chadd }; 81c94dc808SAdrian Chadd 82a043e8c7SAdrian Chadd static inline int arswitch_portforphy(int phy); 83a043e8c7SAdrian Chadd static void arswitch_tick(void *arg); 842e6a8c1aSJustin Hibbits static int arswitch_ifmedia_upd(if_t); 852e6a8c1aSJustin Hibbits static void arswitch_ifmedia_sts(if_t, struct ifmediareq *); 86a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_setup(struct arswitch_softc *sc, 87a9ad4222SAdrian Chadd etherswitch_port_t *p); 88a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_get(struct arswitch_softc *sc, 89a9ad4222SAdrian Chadd etherswitch_port_t *p); 90c94dc808SAdrian Chadd static int arswitch_setled(struct arswitch_softc *sc, int phy, int led, 91c94dc808SAdrian Chadd int style); 92a043e8c7SAdrian Chadd 93a043e8c7SAdrian Chadd static int 94a043e8c7SAdrian Chadd arswitch_probe(device_t dev) 95a043e8c7SAdrian Chadd { 96a043e8c7SAdrian Chadd struct arswitch_softc *sc; 97a043e8c7SAdrian Chadd uint32_t id; 98*54482989SMark Johnston char *chipname; 99a043e8c7SAdrian Chadd 100a043e8c7SAdrian Chadd sc = device_get_softc(dev); 101a043e8c7SAdrian Chadd bzero(sc, sizeof(*sc)); 102a043e8c7SAdrian Chadd sc->page = -1; 10327a2ecaaSAdrian Chadd 10427a2ecaaSAdrian Chadd /* AR8xxx probe */ 105a043e8c7SAdrian Chadd id = arswitch_readreg(dev, AR8X16_REG_MASK_CTRL); 106dd843f87SAdrian Chadd sc->chip_rev = (id & AR8X16_MASK_CTRL_REV_MASK); 107e0bc8f8dSMichael Zhilin sc->chip_ver = (id & AR8X16_MASK_CTRL_VER_MASK) >> AR8X16_MASK_CTRL_VER_SHIFT; 108b2152161SAdrian Chadd switch (id & (AR8X16_MASK_CTRL_VER_MASK | AR8X16_MASK_CTRL_REV_MASK)) { 109b2152161SAdrian Chadd case 0x0101: 110a043e8c7SAdrian Chadd chipname = "AR8216"; 111a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8216; 112a043e8c7SAdrian Chadd break; 113b2152161SAdrian Chadd case 0x0201: 114a043e8c7SAdrian Chadd chipname = "AR8226"; 115a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8226; 116a043e8c7SAdrian Chadd break; 117b2152161SAdrian Chadd /* 0x0301 - AR8236 */ 118b2152161SAdrian Chadd case 0x1000: 119b2152161SAdrian Chadd case 0x1001: 120a043e8c7SAdrian Chadd chipname = "AR8316"; 121a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8316; 122a043e8c7SAdrian Chadd break; 1230e67bf94SAdrian Chadd case 0x1202: 1249682e347SAdrian Chadd case 0x1204: 1250e67bf94SAdrian Chadd chipname = "AR8327"; 1260e67bf94SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8327; 1270e67bf94SAdrian Chadd sc->mii_lo_first = 1; 1280e67bf94SAdrian Chadd break; 129a043e8c7SAdrian Chadd default: 130a043e8c7SAdrian Chadd chipname = NULL; 131a043e8c7SAdrian Chadd } 13227a2ecaaSAdrian Chadd 1331b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, "chipname=%s, id=%08x\n", chipname, id); 134a043e8c7SAdrian Chadd if (chipname != NULL) { 135*54482989SMark Johnston device_set_descf(dev, 13678549b94SAdrian Chadd "Atheros %s Ethernet Switch (ver %d rev %d)", 137*54482989SMark Johnston chipname, sc->chip_ver, sc->chip_rev); 138a043e8c7SAdrian Chadd return (BUS_PROBE_DEFAULT); 139a043e8c7SAdrian Chadd } 140a043e8c7SAdrian Chadd return (ENXIO); 141a043e8c7SAdrian Chadd } 142a043e8c7SAdrian Chadd 143a043e8c7SAdrian Chadd static int 144a043e8c7SAdrian Chadd arswitch_attach_phys(struct arswitch_softc *sc) 145a043e8c7SAdrian Chadd { 146a043e8c7SAdrian Chadd int phy, err = 0; 147a043e8c7SAdrian Chadd char name[IFNAMSIZ]; 148a043e8c7SAdrian Chadd 149a043e8c7SAdrian Chadd /* PHYs need an interface, so we generate a dummy one */ 150a043e8c7SAdrian Chadd snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 151a043e8c7SAdrian Chadd for (phy = 0; phy < sc->numphys; phy++) { 152a043e8c7SAdrian Chadd sc->ifp[phy] = if_alloc(IFT_ETHER); 1530774131eSMichael Zhilin if (sc->ifp[phy] == NULL) { 1540774131eSMichael Zhilin device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n"); 1550774131eSMichael Zhilin err = ENOMEM; 1560774131eSMichael Zhilin break; 1570774131eSMichael Zhilin } 1580774131eSMichael Zhilin 1592e6a8c1aSJustin Hibbits if_setsoftc(sc->ifp[phy], sc); 1602e6a8c1aSJustin Hibbits if_setflagbits(sc->ifp[phy], IFF_UP | IFF_BROADCAST | 1612e6a8c1aSJustin Hibbits IFF_DRV_RUNNING | IFF_SIMPLEX, 0); 162a043e8c7SAdrian Chadd sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK); 163a043e8c7SAdrian Chadd bcopy(name, sc->ifname[phy], strlen(name)+1); 164a043e8c7SAdrian Chadd if_initname(sc->ifp[phy], sc->ifname[phy], 165a043e8c7SAdrian Chadd arswitch_portforphy(phy)); 166a043e8c7SAdrian Chadd err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy], 167a043e8c7SAdrian Chadd arswitch_ifmedia_upd, arswitch_ifmedia_sts, \ 168a043e8c7SAdrian Chadd BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 16978549b94SAdrian Chadd #if 0 170a043e8c7SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 171a043e8c7SAdrian Chadd device_get_nameunit(sc->miibus[phy]), 172a043e8c7SAdrian Chadd sc->ifp[phy]->if_xname); 17378549b94SAdrian Chadd #endif 174a043e8c7SAdrian Chadd if (err != 0) { 175a043e8c7SAdrian Chadd device_printf(sc->sc_dev, 176a043e8c7SAdrian Chadd "attaching PHY %d failed\n", 177a043e8c7SAdrian Chadd phy); 178a043e8c7SAdrian Chadd return (err); 179a043e8c7SAdrian Chadd } 180a043e8c7SAdrian Chadd 181c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) { 182c94dc808SAdrian Chadd int led; 183c94dc808SAdrian Chadd char ledname[IFNAMSIZ+4]; 184c94dc808SAdrian Chadd 185c94dc808SAdrian Chadd for (led = 0; led < 3; led++) { 186c94dc808SAdrian Chadd sprintf(ledname, "%s%dled%d", name, 187c94dc808SAdrian Chadd arswitch_portforphy(phy), led+1); 188c94dc808SAdrian Chadd sc->dev_led[phy][led].sc = sc; 189c94dc808SAdrian Chadd sc->dev_led[phy][led].phy = phy; 190c94dc808SAdrian Chadd sc->dev_led[phy][led].lednum = led; 191c94dc808SAdrian Chadd } 192c94dc808SAdrian Chadd } 193c94dc808SAdrian Chadd } 194c94dc808SAdrian Chadd return (0); 195c94dc808SAdrian Chadd } 196c94dc808SAdrian Chadd 197a043e8c7SAdrian Chadd static int 198b9f07b86SLuiz Otavio O Souza arswitch_reset(device_t dev) 199b9f07b86SLuiz Otavio O Souza { 200b9f07b86SLuiz Otavio O Souza 201b9f07b86SLuiz Otavio O Souza arswitch_writereg(dev, AR8X16_REG_MASK_CTRL, 202b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET); 203b9f07b86SLuiz Otavio O Souza DELAY(1000); 204b9f07b86SLuiz Otavio O Souza if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) & 205b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET) { 206b9f07b86SLuiz Otavio O Souza device_printf(dev, "unable to reset switch\n"); 207b9f07b86SLuiz Otavio O Souza return (-1); 208b9f07b86SLuiz Otavio O Souza } 209b9f07b86SLuiz Otavio O Souza return (0); 210b9f07b86SLuiz Otavio O Souza } 211b9f07b86SLuiz Otavio O Souza 212b9f07b86SLuiz Otavio O Souza static int 213b9f07b86SLuiz Otavio O Souza arswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode) 214b9f07b86SLuiz Otavio O Souza { 215b9f07b86SLuiz Otavio O Souza 216b9f07b86SLuiz Otavio O Souza /* Check for invalid modes. */ 217b9f07b86SLuiz Otavio O Souza if ((mode & sc->info.es_vlan_caps) != mode) 218b9f07b86SLuiz Otavio O Souza return (EINVAL); 219b9f07b86SLuiz Otavio O Souza 220b9f07b86SLuiz Otavio O Souza switch (mode) { 221b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_DOT1Q: 222b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 223b9f07b86SLuiz Otavio O Souza break; 224b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_PORT: 225b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_PORT; 226b9f07b86SLuiz Otavio O Souza break; 227b9f07b86SLuiz Otavio O Souza default: 228b9f07b86SLuiz Otavio O Souza sc->vlan_mode = 0; 22974b8d63dSPedro F. Giffuni } 230b9f07b86SLuiz Otavio O Souza 231b9f07b86SLuiz Otavio O Souza /* Reset VLANs. */ 2326dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw(sc); 233b9f07b86SLuiz Otavio O Souza 234b9f07b86SLuiz Otavio O Souza return (0); 235b9f07b86SLuiz Otavio O Souza } 236b9f07b86SLuiz Otavio O Souza 237b9f07b86SLuiz Otavio O Souza static void 238a9ad4222SAdrian Chadd ar8xxx_port_init(struct arswitch_softc *sc, int port) 239b9f07b86SLuiz Otavio O Souza { 240b9f07b86SLuiz Otavio O Souza 241b9f07b86SLuiz Otavio O Souza /* Port0 - CPU */ 242df892897SAdrian Chadd if (port == AR8X16_PORT_CPU) { 243b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(0), 244b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 245b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_SPEED_100 : AR8X16_PORT_STS_SPEED_1000) | 246b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_RXFLOW) | 247b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_TXFLOW) | 248b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_RXMAC | 249b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_TXMAC | 250b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_DUPLEX); 251b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0), 252b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0)) & 253b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER); 254df892897SAdrian Chadd } else { 255b9f07b86SLuiz Otavio O Souza /* Set ports to auto negotiation. */ 256b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(port), 257b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_LINK_AUTO); 258b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port), 259b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port)) & 260b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER); 261b9f07b86SLuiz Otavio O Souza } 262b9f07b86SLuiz Otavio O Souza } 263b9f07b86SLuiz Otavio O Souza 264b9f07b86SLuiz Otavio O Souza static int 26562042c97SAdrian Chadd ar8xxx_atu_wait_ready(struct arswitch_softc *sc) 2664ff2f60dSAdrian Chadd { 2674ff2f60dSAdrian Chadd int ret; 2684ff2f60dSAdrian Chadd 26962042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 27062042c97SAdrian Chadd 2714ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev, 2724ff2f60dSAdrian Chadd AR8216_REG_ATU, 2734ff2f60dSAdrian Chadd AR8216_ATU_ACTIVE, 2744ff2f60dSAdrian Chadd 0, 2754ff2f60dSAdrian Chadd 1000); 2764ff2f60dSAdrian Chadd 27762042c97SAdrian Chadd return (ret); 27862042c97SAdrian Chadd } 27962042c97SAdrian Chadd 28062042c97SAdrian Chadd /* 28162042c97SAdrian Chadd * Flush all ATU entries. 28262042c97SAdrian Chadd */ 28362042c97SAdrian Chadd static int 28462042c97SAdrian Chadd ar8xxx_atu_flush(struct arswitch_softc *sc) 28562042c97SAdrian Chadd { 28662042c97SAdrian Chadd int ret; 28762042c97SAdrian Chadd 28862042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 28962042c97SAdrian Chadd 29062042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing all ports\n", __func__); 29162042c97SAdrian Chadd 29262042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc); 2934ff2f60dSAdrian Chadd if (ret) 2944ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 2954ff2f60dSAdrian Chadd 2964ff2f60dSAdrian Chadd if (!ret) 2974ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev, 2984ff2f60dSAdrian Chadd AR8216_REG_ATU, 2992c6ceccaSAdrian Chadd AR8216_ATU_OP_FLUSH | AR8216_ATU_ACTIVE); 3004ff2f60dSAdrian Chadd 3014ff2f60dSAdrian Chadd return (ret); 3024ff2f60dSAdrian Chadd } 3034ff2f60dSAdrian Chadd 30462042c97SAdrian Chadd /* 30562042c97SAdrian Chadd * Flush ATU entries for a single port. 30662042c97SAdrian Chadd */ 30762042c97SAdrian Chadd static int 30862042c97SAdrian Chadd ar8xxx_atu_flush_port(struct arswitch_softc *sc, int port) 30962042c97SAdrian Chadd { 31062042c97SAdrian Chadd int ret, val; 31162042c97SAdrian Chadd 31262042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing port %d\n", __func__, 31362042c97SAdrian Chadd port); 31462042c97SAdrian Chadd 31562042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 31662042c97SAdrian Chadd 31762042c97SAdrian Chadd /* Flush unicast entries on port */ 31862042c97SAdrian Chadd val = AR8216_ATU_OP_FLUSH_UNICAST; 31962042c97SAdrian Chadd 32062042c97SAdrian Chadd /* TODO: bit 4 indicates whether to flush dynamic (0) or static (1) */ 32162042c97SAdrian Chadd 32262042c97SAdrian Chadd /* Which port */ 32362042c97SAdrian Chadd val |= SM(port, AR8216_ATU_PORT_NUM); 32462042c97SAdrian Chadd 32562042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc); 32662042c97SAdrian Chadd if (ret) 32762042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 32862042c97SAdrian Chadd 32962042c97SAdrian Chadd if (!ret) 33062042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 33162042c97SAdrian Chadd AR8216_REG_ATU, 33262042c97SAdrian Chadd val | AR8216_ATU_ACTIVE); 33362042c97SAdrian Chadd 33462042c97SAdrian Chadd return (ret); 33562042c97SAdrian Chadd } 33662042c97SAdrian Chadd 33762042c97SAdrian Chadd /* 33862042c97SAdrian Chadd * XXX TODO: flush a single MAC address. 33962042c97SAdrian Chadd */ 34062042c97SAdrian Chadd 34162042c97SAdrian Chadd /* 34262042c97SAdrian Chadd * Fetch a single entry from the ATU. 34362042c97SAdrian Chadd */ 34462042c97SAdrian Chadd static int 34562042c97SAdrian Chadd ar8xxx_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e, 34662042c97SAdrian Chadd int atu_fetch_op) 34762042c97SAdrian Chadd { 34862042c97SAdrian Chadd uint32_t ret0, ret1, ret2, val; 34962042c97SAdrian Chadd 35062042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 35162042c97SAdrian Chadd 35262042c97SAdrian Chadd switch (atu_fetch_op) { 35362042c97SAdrian Chadd case 0: 35462042c97SAdrian Chadd /* Initialise things for the first fetch */ 35562042c97SAdrian Chadd 35662042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__); 35762042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 35862042c97SAdrian Chadd 35962042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 36062042c97SAdrian Chadd AR8216_REG_ATU, AR8216_ATU_OP_GET_NEXT); 36162042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 36262042c97SAdrian Chadd AR8216_REG_ATU_DATA, 0); 36362042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 36462042c97SAdrian Chadd AR8216_REG_ATU_CTRL2, 0); 36562042c97SAdrian Chadd 36662042c97SAdrian Chadd return (0); 36762042c97SAdrian Chadd case 1: 36862042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__); 36962042c97SAdrian Chadd /* 37062042c97SAdrian Chadd * Attempt to read the next address entry; don't modify what 37162042c97SAdrian Chadd * is there in AT_ADDR{4,5} as its used for the next fetch 37262042c97SAdrian Chadd */ 37362042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 37462042c97SAdrian Chadd 37562042c97SAdrian Chadd /* Begin the next read event; not modifying anything */ 37662042c97SAdrian Chadd val = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU); 37762042c97SAdrian Chadd val |= AR8216_ATU_ACTIVE; 37862042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8216_REG_ATU, val); 37962042c97SAdrian Chadd 38062042c97SAdrian Chadd /* Wait for it to complete */ 38162042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 38262042c97SAdrian Chadd 38362042c97SAdrian Chadd /* Fetch the ethernet address and ATU status */ 38462042c97SAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU); 38562042c97SAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_DATA); 38662042c97SAdrian Chadd ret2 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_CTRL2); 38762042c97SAdrian Chadd 38862042c97SAdrian Chadd /* If the status is zero, then we're done */ 38962042c97SAdrian Chadd if (MS(ret2, AR8216_ATU_CTRL2_AT_STATUS) == 0) 39062042c97SAdrian Chadd return (-1); 39162042c97SAdrian Chadd 39262042c97SAdrian Chadd /* MAC address */ 39362042c97SAdrian Chadd e->es_macaddr[5] = MS(ret0, AR8216_ATU_ADDR5); 39462042c97SAdrian Chadd e->es_macaddr[4] = MS(ret0, AR8216_ATU_ADDR4); 39562042c97SAdrian Chadd e->es_macaddr[3] = MS(ret1, AR8216_ATU_ADDR3); 39662042c97SAdrian Chadd e->es_macaddr[2] = MS(ret1, AR8216_ATU_ADDR2); 39762042c97SAdrian Chadd e->es_macaddr[1] = MS(ret1, AR8216_ATU_ADDR1); 39862042c97SAdrian Chadd e->es_macaddr[0] = MS(ret1, AR8216_ATU_ADDR0); 39962042c97SAdrian Chadd 40062042c97SAdrian Chadd /* Bitmask of ports this entry is for */ 40162042c97SAdrian Chadd e->es_portmask = MS(ret2, AR8216_ATU_CTRL2_DESPORT); 40262042c97SAdrian Chadd 40362042c97SAdrian Chadd /* TODO: other flags that are interesting */ 40462042c97SAdrian Chadd 40562042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n", 40662042c97SAdrian Chadd __func__, 40762042c97SAdrian Chadd e->es_macaddr, ":", e->es_portmask); 40862042c97SAdrian Chadd return (0); 40962042c97SAdrian Chadd default: 41062042c97SAdrian Chadd return (-1); 41162042c97SAdrian Chadd } 41262042c97SAdrian Chadd return (-1); 41362042c97SAdrian Chadd } 41462042c97SAdrian Chadd 41562042c97SAdrian Chadd /* 41662042c97SAdrian Chadd * Configure aging register defaults. 41762042c97SAdrian Chadd */ 41862042c97SAdrian Chadd static int 41962042c97SAdrian Chadd ar8xxx_atu_learn_default(struct arswitch_softc *sc) 42062042c97SAdrian Chadd { 42162042c97SAdrian Chadd int ret; 42262042c97SAdrian Chadd uint32_t val; 42362042c97SAdrian Chadd 42462042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: resetting learning\n", __func__); 42562042c97SAdrian Chadd 42662042c97SAdrian Chadd /* 42762042c97SAdrian Chadd * For now, configure the aging defaults: 42862042c97SAdrian Chadd * 42962042c97SAdrian Chadd * + ARP_EN - enable "acknowledgement" of ARP frames - they are 43062042c97SAdrian Chadd * forwarded to the CPU port 43162042c97SAdrian Chadd * + LEARN_CHANGE_EN - hash table violations when learning MAC addresses 43262042c97SAdrian Chadd * will force an entry to be expired/updated and a new one to be 43362042c97SAdrian Chadd * programmed in. 43462042c97SAdrian Chadd * + AGE_EN - enable address table aging 43562042c97SAdrian Chadd * + AGE_TIME - set to 5 minutes 43662042c97SAdrian Chadd */ 43762042c97SAdrian Chadd val = 0; 43862042c97SAdrian Chadd val |= AR8216_ATU_CTRL_ARP_EN; 43962042c97SAdrian Chadd val |= AR8216_ATU_CTRL_LEARN_CHANGE; 44062042c97SAdrian Chadd val |= AR8216_ATU_CTRL_AGE_EN; 44162042c97SAdrian Chadd val |= 0x2b; /* 5 minutes; bits 15:0 */ 44262042c97SAdrian Chadd 44362042c97SAdrian Chadd ret = arswitch_writereg(sc->sc_dev, 44462042c97SAdrian Chadd AR8216_REG_ATU_CTRL, 44562042c97SAdrian Chadd val); 44662042c97SAdrian Chadd 44762042c97SAdrian Chadd if (ret) 44862042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: writereg failed\n", __func__); 44962042c97SAdrian Chadd 45062042c97SAdrian Chadd return (ret); 45162042c97SAdrian Chadd } 45262042c97SAdrian Chadd 45362042c97SAdrian Chadd /* 45462042c97SAdrian Chadd * XXX TODO: add another routine to configure the leaky behaviour 45562042c97SAdrian Chadd * when unknown frames are received. These must be consistent 45662042c97SAdrian Chadd * between ethernet switches. 45762042c97SAdrian Chadd */ 45862042c97SAdrian Chadd 45962042c97SAdrian Chadd /* 4602ba4bf8fSAdrian Chadd * Fetch the configured switch MAC address. 4612ba4bf8fSAdrian Chadd */ 4622ba4bf8fSAdrian Chadd static int 4632ba4bf8fSAdrian Chadd ar8xxx_hw_get_switch_macaddr(struct arswitch_softc *sc, struct ether_addr *ea) 4642ba4bf8fSAdrian Chadd { 4652ba4bf8fSAdrian Chadd uint32_t ret0, ret1; 4662ba4bf8fSAdrian Chadd char *s; 4672ba4bf8fSAdrian Chadd 4682ba4bf8fSAdrian Chadd s = (void *) ea; 4692ba4bf8fSAdrian Chadd 4702ba4bf8fSAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR0); 4712ba4bf8fSAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR1); 4722ba4bf8fSAdrian Chadd 4732ba4bf8fSAdrian Chadd s[5] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE5); 4742ba4bf8fSAdrian Chadd s[4] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE4); 4752ba4bf8fSAdrian Chadd s[3] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE3); 4762ba4bf8fSAdrian Chadd s[2] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE2); 4772ba4bf8fSAdrian Chadd s[1] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE1); 4782ba4bf8fSAdrian Chadd s[0] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE0); 4792ba4bf8fSAdrian Chadd 4802ba4bf8fSAdrian Chadd return (0); 4812ba4bf8fSAdrian Chadd } 4822ba4bf8fSAdrian Chadd 4832ba4bf8fSAdrian Chadd /* 4842ba4bf8fSAdrian Chadd * Set the switch mac address. 4852ba4bf8fSAdrian Chadd */ 4862ba4bf8fSAdrian Chadd static int 4872ba4bf8fSAdrian Chadd ar8xxx_hw_set_switch_macaddr(struct arswitch_softc *sc, 4882ba4bf8fSAdrian Chadd const struct ether_addr *ea) 4892ba4bf8fSAdrian Chadd { 4902ba4bf8fSAdrian Chadd 4912ba4bf8fSAdrian Chadd return (ENXIO); 4922ba4bf8fSAdrian Chadd } 4932ba4bf8fSAdrian Chadd 4942ba4bf8fSAdrian Chadd /* 49562042c97SAdrian Chadd * XXX TODO: this attach routine does NOT free all memory, resources 49662042c97SAdrian Chadd * upon failure! 49762042c97SAdrian Chadd */ 4984ff2f60dSAdrian Chadd static int 499a043e8c7SAdrian Chadd arswitch_attach(device_t dev) 500a043e8c7SAdrian Chadd { 5011b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 5021b334c8bSAdrian Chadd struct sysctl_ctx_list *ctx; 5031b334c8bSAdrian Chadd struct sysctl_oid *tree; 504a043e8c7SAdrian Chadd int err = 0; 505df892897SAdrian Chadd int port; 506a043e8c7SAdrian Chadd 507a043e8c7SAdrian Chadd /* sc->sc_switchtype is already decided in arswitch_probe() */ 508a043e8c7SAdrian Chadd sc->sc_dev = dev; 509a043e8c7SAdrian Chadd mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF); 510a043e8c7SAdrian Chadd sc->page = -1; 511a043e8c7SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev), 512a043e8c7SAdrian Chadd sizeof(sc->info.es_name)); 513a043e8c7SAdrian Chadd 5141b334c8bSAdrian Chadd /* Debugging */ 5151b334c8bSAdrian Chadd ctx = device_get_sysctl_ctx(sc->sc_dev); 5161b334c8bSAdrian Chadd tree = device_get_sysctl_tree(sc->sc_dev); 5171b334c8bSAdrian Chadd SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5181b334c8bSAdrian Chadd "debug", CTLFLAG_RW, &sc->sc_debug, 0, 5191b334c8bSAdrian Chadd "control debugging printfs"); 5201b334c8bSAdrian Chadd 52162042c97SAdrian Chadd /* Allocate a 128 entry ATU table; hopefully its big enough! */ 52262042c97SAdrian Chadd /* XXX TODO: make this per chip */ 52362042c97SAdrian Chadd sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128, 52462042c97SAdrian Chadd M_DEVBUF, M_NOWAIT); 52562042c97SAdrian Chadd if (sc->atu.entries == NULL) { 52662042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n", 52762042c97SAdrian Chadd __func__); 52862042c97SAdrian Chadd return (ENXIO); 52962042c97SAdrian Chadd } 53062042c97SAdrian Chadd sc->atu.count = 0; 53162042c97SAdrian Chadd sc->atu.size = 128; 53262042c97SAdrian Chadd 533ddbc4420SAdrian Chadd /* Default HAL methods */ 534a9ad4222SAdrian Chadd sc->hal.arswitch_port_init = ar8xxx_port_init; 535a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup; 536a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get; 5376dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans; 5382ba4bf8fSAdrian Chadd sc->hal.arswitch_hw_get_switch_macaddr = ar8xxx_hw_get_switch_macaddr; 5392ba4bf8fSAdrian Chadd sc->hal.arswitch_hw_set_switch_macaddr = ar8xxx_hw_set_switch_macaddr; 540749cac13SAdrian Chadd 5416dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup; 5426dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup; 543749cac13SAdrian Chadd 5446dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid; 5456dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid; 546749cac13SAdrian Chadd 547749cac13SAdrian Chadd sc->hal.arswitch_get_dot1q_vlan = ar8xxx_get_dot1q_vlan; 548749cac13SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan = ar8xxx_set_dot1q_vlan; 549f35f94f4SAdrian Chadd sc->hal.arswitch_flush_dot1q_vlan = ar8xxx_flush_dot1q_vlan; 550f35f94f4SAdrian Chadd sc->hal.arswitch_purge_dot1q_vlan = ar8xxx_purge_dot1q_vlan; 551749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8xxx_get_port_vlan; 552749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan; 553749cac13SAdrian Chadd 5544ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8xxx_atu_flush; 55562042c97SAdrian Chadd sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port; 55662042c97SAdrian Chadd sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default; 55762042c97SAdrian Chadd sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table; 558749cac13SAdrian Chadd 55978549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_internal; 56078549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_internal; 561ddbc4420SAdrian Chadd 562a043e8c7SAdrian Chadd /* 563a043e8c7SAdrian Chadd * Attach switch related functions 564a043e8c7SAdrian Chadd */ 5658f5dbc22SMarius Strobl if (AR8X16_IS_SWITCH(sc, AR8216)) 566a043e8c7SAdrian Chadd ar8216_attach(sc); 567a043e8c7SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8226)) 568a043e8c7SAdrian Chadd ar8226_attach(sc); 569a043e8c7SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8316)) 570a043e8c7SAdrian Chadd ar8316_attach(sc); 571482d268dSAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8327)) 572482d268dSAdrian Chadd ar8327_attach(sc); 57378549b94SAdrian Chadd else { 5741b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 5751b334c8bSAdrian Chadd "%s: unknown switch (%d)?\n", __func__, sc->sc_switchtype); 576a043e8c7SAdrian Chadd return (ENXIO); 57778549b94SAdrian Chadd } 578a043e8c7SAdrian Chadd 579b9f07b86SLuiz Otavio O Souza /* Common defaults. */ 580a043e8c7SAdrian Chadd sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */ 581a043e8c7SAdrian Chadd 582a043e8c7SAdrian Chadd /* XXX Defaults for externally connected AR8316 */ 583a043e8c7SAdrian Chadd sc->numphys = 4; 584a043e8c7SAdrian Chadd sc->phy4cpu = 1; 585a043e8c7SAdrian Chadd sc->is_rgmii = 1; 586a043e8c7SAdrian Chadd sc->is_gmii = 0; 587b2152161SAdrian Chadd sc->is_mii = 0; 588a043e8c7SAdrian Chadd 589a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 590a043e8c7SAdrian Chadd "numphys", &sc->numphys); 591a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 592a043e8c7SAdrian Chadd "phy4cpu", &sc->phy4cpu); 593a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 594a043e8c7SAdrian Chadd "is_rgmii", &sc->is_rgmii); 595a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 596a043e8c7SAdrian Chadd "is_gmii", &sc->is_gmii); 597b2152161SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 598b2152161SAdrian Chadd "is_mii", &sc->is_mii); 599a043e8c7SAdrian Chadd 600b9f07b86SLuiz Otavio O Souza if (sc->numphys > AR8X16_NUM_PHYS) 601b9f07b86SLuiz Otavio O Souza sc->numphys = AR8X16_NUM_PHYS; 602a043e8c7SAdrian Chadd 603b9f07b86SLuiz Otavio O Souza /* Reset the switch. */ 60478549b94SAdrian Chadd if (arswitch_reset(dev)) { 6051b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6061b334c8bSAdrian Chadd "%s: arswitch_reset: failed\n", __func__); 607b9f07b86SLuiz Otavio O Souza return (ENXIO); 60878549b94SAdrian Chadd } 609a043e8c7SAdrian Chadd 610b2152161SAdrian Chadd err = sc->hal.arswitch_hw_setup(sc); 6111b334c8bSAdrian Chadd if (err != 0) { 6121b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6131b334c8bSAdrian Chadd "%s: hw_setup: err=%d\n", __func__, err); 614b2152161SAdrian Chadd return (err); 6151b334c8bSAdrian Chadd } 616b2152161SAdrian Chadd 617a043e8c7SAdrian Chadd err = sc->hal.arswitch_hw_global_setup(sc); 6181b334c8bSAdrian Chadd if (err != 0) { 6191b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6201b334c8bSAdrian Chadd "%s: hw_global_setup: err=%d\n", __func__, err); 621a043e8c7SAdrian Chadd return (err); 6221b334c8bSAdrian Chadd } 623a043e8c7SAdrian Chadd 62462042c97SAdrian Chadd /* 62562042c97SAdrian Chadd * Configure the default address table learning parameters for this 62662042c97SAdrian Chadd * switch. 62762042c97SAdrian Chadd */ 62862042c97SAdrian Chadd err = sc->hal.arswitch_atu_learn_default(sc); 62962042c97SAdrian Chadd if (err != 0) { 63062042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 63162042c97SAdrian Chadd "%s: atu_learn_default: err=%d\n", __func__, err); 63262042c97SAdrian Chadd return (err); 63362042c97SAdrian Chadd } 63462042c97SAdrian Chadd 635b9f07b86SLuiz Otavio O Souza /* Initialize the switch ports. */ 636df892897SAdrian Chadd for (port = 0; port <= sc->numphys; port++) { 637ddbc4420SAdrian Chadd sc->hal.arswitch_port_init(sc, port); 638df892897SAdrian Chadd } 639b9f07b86SLuiz Otavio O Souza 640a043e8c7SAdrian Chadd /* 641a043e8c7SAdrian Chadd * Attach the PHYs and complete the bus enumeration. 642a043e8c7SAdrian Chadd */ 643a043e8c7SAdrian Chadd err = arswitch_attach_phys(sc); 6441b334c8bSAdrian Chadd if (err != 0) { 6451b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6461b334c8bSAdrian Chadd "%s: attach_phys: err=%d\n", __func__, err); 647a043e8c7SAdrian Chadd return (err); 6481b334c8bSAdrian Chadd } 649a043e8c7SAdrian Chadd 650b9f07b86SLuiz Otavio O Souza /* Default to ingress filters off. */ 651b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, 0); 6521b334c8bSAdrian Chadd if (err != 0) { 6531b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6541b334c8bSAdrian Chadd "%s: set_vlan_mode: err=%d\n", __func__, err); 655b9f07b86SLuiz Otavio O Souza return (err); 6561b334c8bSAdrian Chadd } 657b9f07b86SLuiz Otavio O Souza 658a043e8c7SAdrian Chadd bus_generic_probe(dev); 659a043e8c7SAdrian Chadd bus_enumerate_hinted_children(dev); 660a043e8c7SAdrian Chadd err = bus_generic_attach(dev); 6611b334c8bSAdrian Chadd if (err != 0) { 6621b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6631b334c8bSAdrian Chadd "%s: bus_generic_attach: err=%d\n", __func__, err); 664a043e8c7SAdrian Chadd return (err); 6651b334c8bSAdrian Chadd } 666a043e8c7SAdrian Chadd 667a043e8c7SAdrian Chadd callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0); 668454d507aSAleksandr Rybalko 669454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc); 670a043e8c7SAdrian Chadd arswitch_tick(sc); 671454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc); 672a043e8c7SAdrian Chadd 673a043e8c7SAdrian Chadd return (err); 674a043e8c7SAdrian Chadd } 675a043e8c7SAdrian Chadd 676a043e8c7SAdrian Chadd static int 677a043e8c7SAdrian Chadd arswitch_detach(device_t dev) 678a043e8c7SAdrian Chadd { 679a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 680a043e8c7SAdrian Chadd int i; 681a043e8c7SAdrian Chadd 682a043e8c7SAdrian Chadd callout_drain(&sc->callout_tick); 683a043e8c7SAdrian Chadd 684a043e8c7SAdrian Chadd for (i=0; i < sc->numphys; i++) { 685a043e8c7SAdrian Chadd if (sc->miibus[i] != NULL) 686a043e8c7SAdrian Chadd device_delete_child(dev, sc->miibus[i]); 687a043e8c7SAdrian Chadd if (sc->ifp[i] != NULL) 688a043e8c7SAdrian Chadd if_free(sc->ifp[i]); 689a043e8c7SAdrian Chadd free(sc->ifname[i], M_DEVBUF); 690a043e8c7SAdrian Chadd } 691a043e8c7SAdrian Chadd 69262042c97SAdrian Chadd free(sc->atu.entries, M_DEVBUF); 69362042c97SAdrian Chadd 694a043e8c7SAdrian Chadd bus_generic_detach(dev); 695a043e8c7SAdrian Chadd mtx_destroy(&sc->sc_mtx); 696a043e8c7SAdrian Chadd 697a043e8c7SAdrian Chadd return (0); 698a043e8c7SAdrian Chadd } 699a043e8c7SAdrian Chadd 700a043e8c7SAdrian Chadd /* 701a043e8c7SAdrian Chadd * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to 702a043e8c7SAdrian Chadd * port 2, etc. 703a043e8c7SAdrian Chadd */ 704a043e8c7SAdrian Chadd static inline int 705a043e8c7SAdrian Chadd arswitch_portforphy(int phy) 706a043e8c7SAdrian Chadd { 707a043e8c7SAdrian Chadd return (phy+1); 708a043e8c7SAdrian Chadd } 709a043e8c7SAdrian Chadd 710a043e8c7SAdrian Chadd static inline struct mii_data * 711a043e8c7SAdrian Chadd arswitch_miiforport(struct arswitch_softc *sc, int port) 712a043e8c7SAdrian Chadd { 713a043e8c7SAdrian Chadd int phy = port-1; 714a043e8c7SAdrian Chadd 715a043e8c7SAdrian Chadd if (phy < 0 || phy >= sc->numphys) 716a043e8c7SAdrian Chadd return (NULL); 717a043e8c7SAdrian Chadd return (device_get_softc(sc->miibus[phy])); 718a043e8c7SAdrian Chadd } 719a043e8c7SAdrian Chadd 7202e6a8c1aSJustin Hibbits static inline if_t 721a043e8c7SAdrian Chadd arswitch_ifpforport(struct arswitch_softc *sc, int port) 722a043e8c7SAdrian Chadd { 723a043e8c7SAdrian Chadd int phy = port-1; 724a043e8c7SAdrian Chadd 725a043e8c7SAdrian Chadd if (phy < 0 || phy >= sc->numphys) 726a043e8c7SAdrian Chadd return (NULL); 727a043e8c7SAdrian Chadd return (sc->ifp[phy]); 728a043e8c7SAdrian Chadd } 729a043e8c7SAdrian Chadd 730a043e8c7SAdrian Chadd /* 731a043e8c7SAdrian Chadd * Convert port status to ifmedia. 732a043e8c7SAdrian Chadd */ 733a043e8c7SAdrian Chadd static void 734a043e8c7SAdrian Chadd arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active) 735a043e8c7SAdrian Chadd { 736a043e8c7SAdrian Chadd *media_active = IFM_ETHER; 737a043e8c7SAdrian Chadd *media_status = IFM_AVALID; 738a043e8c7SAdrian Chadd 739a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0) 740a043e8c7SAdrian Chadd *media_status |= IFM_ACTIVE; 741a043e8c7SAdrian Chadd else { 742a043e8c7SAdrian Chadd *media_active |= IFM_NONE; 743a043e8c7SAdrian Chadd return; 744a043e8c7SAdrian Chadd } 745a043e8c7SAdrian Chadd switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) { 746a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_10: 747a043e8c7SAdrian Chadd *media_active |= IFM_10_T; 748a043e8c7SAdrian Chadd break; 749a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_100: 750a043e8c7SAdrian Chadd *media_active |= IFM_100_TX; 751a043e8c7SAdrian Chadd break; 752a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_1000: 753a043e8c7SAdrian Chadd *media_active |= IFM_1000_T; 754a043e8c7SAdrian Chadd break; 755a043e8c7SAdrian Chadd } 756a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0) 757a043e8c7SAdrian Chadd *media_active |= IFM_FDX; 758a043e8c7SAdrian Chadd else 759a043e8c7SAdrian Chadd *media_active |= IFM_HDX; 760a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0) 761a043e8c7SAdrian Chadd *media_active |= IFM_ETH_TXPAUSE; 762a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0) 763a043e8c7SAdrian Chadd *media_active |= IFM_ETH_RXPAUSE; 764a043e8c7SAdrian Chadd } 765a043e8c7SAdrian Chadd 766a043e8c7SAdrian Chadd /* 767a043e8c7SAdrian Chadd * Poll the status for all PHYs. We're using the switch port status because 768a043e8c7SAdrian Chadd * thats a lot quicker to read than talking to all the PHYs. Care must be 769a043e8c7SAdrian Chadd * taken that the resulting ifmedia_active is identical to what the PHY will 770a043e8c7SAdrian Chadd * compute, or gratuitous link status changes will occur whenever the PHYs 771a043e8c7SAdrian Chadd * update function is called. 772a043e8c7SAdrian Chadd */ 773a043e8c7SAdrian Chadd static void 774a043e8c7SAdrian Chadd arswitch_miipollstat(struct arswitch_softc *sc) 775a043e8c7SAdrian Chadd { 776a043e8c7SAdrian Chadd int i; 777a043e8c7SAdrian Chadd struct mii_data *mii; 778a043e8c7SAdrian Chadd struct mii_softc *miisc; 779a043e8c7SAdrian Chadd int portstatus; 7804ff2f60dSAdrian Chadd int port_flap = 0; 781a043e8c7SAdrian Chadd 782454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 783454d507aSAleksandr Rybalko 784a043e8c7SAdrian Chadd for (i = 0; i < sc->numphys; i++) { 785a043e8c7SAdrian Chadd if (sc->miibus[i] == NULL) 786a043e8c7SAdrian Chadd continue; 787a043e8c7SAdrian Chadd mii = device_get_softc(sc->miibus[i]); 788ddbc4420SAdrian Chadd /* XXX This would be nice to have abstracted out to be per-chip */ 789ddbc4420SAdrian Chadd /* AR8327/AR8337 has a different register base */ 790ddbc4420SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) 791ddbc4420SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev, 792ddbc4420SAdrian Chadd AR8327_REG_PORT_STATUS(arswitch_portforphy(i))); 793ddbc4420SAdrian Chadd else 794a043e8c7SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev, 795a043e8c7SAdrian Chadd AR8X16_REG_PORT_STS(arswitch_portforphy(i))); 7961b334c8bSAdrian Chadd #if 1 7971b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "p[%d]=0x%08x (%b)\n", 798b2152161SAdrian Chadd i, 799a043e8c7SAdrian Chadd portstatus, 8001b334c8bSAdrian Chadd portstatus, 801a043e8c7SAdrian Chadd "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7" 802a043e8c7SAdrian Chadd "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE"); 803a043e8c7SAdrian Chadd #endif 8044ff2f60dSAdrian Chadd /* 8054ff2f60dSAdrian Chadd * If the current status is down, but we have a link 8064ff2f60dSAdrian Chadd * status showing up, we need to do an ATU flush. 8074ff2f60dSAdrian Chadd */ 8084ff2f60dSAdrian Chadd if ((mii->mii_media_status & IFM_ACTIVE) == 0 && 8094ff2f60dSAdrian Chadd (portstatus & AR8X16_PORT_STS_LINK_UP) != 0) { 8104ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: port %d: port -> UP\n", 8114ff2f60dSAdrian Chadd __func__, 8124ff2f60dSAdrian Chadd i); 8134ff2f60dSAdrian Chadd port_flap = 1; 8144ff2f60dSAdrian Chadd } 8154ff2f60dSAdrian Chadd /* 8164ff2f60dSAdrian Chadd * and maybe if a port goes up->down? 8174ff2f60dSAdrian Chadd */ 8184ff2f60dSAdrian Chadd if ((mii->mii_media_status & IFM_ACTIVE) != 0 && 8194ff2f60dSAdrian Chadd (portstatus & AR8X16_PORT_STS_LINK_UP) == 0) { 8204ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: port %d: port -> DOWN\n", 8214ff2f60dSAdrian Chadd __func__, 8224ff2f60dSAdrian Chadd i); 8234ff2f60dSAdrian Chadd port_flap = 1; 8244ff2f60dSAdrian Chadd } 825a043e8c7SAdrian Chadd arswitch_update_ifmedia(portstatus, &mii->mii_media_status, 826a043e8c7SAdrian Chadd &mii->mii_media_active); 827a043e8c7SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 828a043e8c7SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 829a043e8c7SAdrian Chadd miisc->mii_inst) 830a043e8c7SAdrian Chadd continue; 831a043e8c7SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT); 832a043e8c7SAdrian Chadd } 833a043e8c7SAdrian Chadd } 8344ff2f60dSAdrian Chadd 8354ff2f60dSAdrian Chadd /* If a port went from down->up, flush the ATU */ 8364ff2f60dSAdrian Chadd if (port_flap) 8374ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush(sc); 838a043e8c7SAdrian Chadd } 839a043e8c7SAdrian Chadd 840a043e8c7SAdrian Chadd static void 841a043e8c7SAdrian Chadd arswitch_tick(void *arg) 842a043e8c7SAdrian Chadd { 843a043e8c7SAdrian Chadd struct arswitch_softc *sc = arg; 844a043e8c7SAdrian Chadd 845a043e8c7SAdrian Chadd arswitch_miipollstat(sc); 846a043e8c7SAdrian Chadd callout_reset(&sc->callout_tick, hz, arswitch_tick, sc); 847a043e8c7SAdrian Chadd } 848a043e8c7SAdrian Chadd 849454d507aSAleksandr Rybalko static void 850454d507aSAleksandr Rybalko arswitch_lock(device_t dev) 851454d507aSAleksandr Rybalko { 852454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev); 853454d507aSAleksandr Rybalko 854454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 855454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc); 856454d507aSAleksandr Rybalko } 857454d507aSAleksandr Rybalko 858454d507aSAleksandr Rybalko static void 859454d507aSAleksandr Rybalko arswitch_unlock(device_t dev) 860454d507aSAleksandr Rybalko { 861454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev); 862454d507aSAleksandr Rybalko 863454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 864454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc); 865454d507aSAleksandr Rybalko } 866454d507aSAleksandr Rybalko 867a043e8c7SAdrian Chadd static etherswitch_info_t * 868a043e8c7SAdrian Chadd arswitch_getinfo(device_t dev) 869a043e8c7SAdrian Chadd { 870a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 871a043e8c7SAdrian Chadd 872a043e8c7SAdrian Chadd return (&sc->info); 873a043e8c7SAdrian Chadd } 874a043e8c7SAdrian Chadd 875a043e8c7SAdrian Chadd static int 876a9ad4222SAdrian Chadd ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p) 877a043e8c7SAdrian Chadd { 878b9f07b86SLuiz Otavio O Souza uint32_t reg; 879b9f07b86SLuiz Otavio O Souza 880b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc); 881b9f07b86SLuiz Otavio O Souza 882b9f07b86SLuiz Otavio O Souza /* Retrieve the PVID. */ 8836dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid); 884b9f07b86SLuiz Otavio O Souza 885b9f07b86SLuiz Otavio O Souza /* Port flags. */ 886b9f07b86SLuiz Otavio O Souza reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port)); 887b9f07b86SLuiz Otavio O Souza if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG) 888b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG; 889b9f07b86SLuiz Otavio O Souza reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 890b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD) 891b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 892b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP) 893b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_STRIPTAG; 894b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 895a043e8c7SAdrian Chadd 896a9ad4222SAdrian Chadd return (0); 897a9ad4222SAdrian Chadd } 898a9ad4222SAdrian Chadd 899a9ad4222SAdrian Chadd static int 900749cac13SAdrian Chadd arswitch_is_cpuport(struct arswitch_softc *sc, int port) 901749cac13SAdrian Chadd { 902749cac13SAdrian Chadd 903749cac13SAdrian Chadd return ((port == AR8X16_PORT_CPU) || 904749cac13SAdrian Chadd ((AR8X16_IS_SWITCH(sc, AR8327) && 905749cac13SAdrian Chadd port == AR8327_PORT_GMAC6))); 906749cac13SAdrian Chadd } 907749cac13SAdrian Chadd 908749cac13SAdrian Chadd static int 909a9ad4222SAdrian Chadd arswitch_getport(device_t dev, etherswitch_port_t *p) 910a9ad4222SAdrian Chadd { 911a9ad4222SAdrian Chadd struct arswitch_softc *sc; 912a9ad4222SAdrian Chadd struct mii_data *mii; 913a9ad4222SAdrian Chadd struct ifmediareq *ifmr; 914a9ad4222SAdrian Chadd int err; 915a9ad4222SAdrian Chadd 916a9ad4222SAdrian Chadd sc = device_get_softc(dev); 917749cac13SAdrian Chadd /* XXX +1 is for AR8327; should make this configurable! */ 918749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports) 919a9ad4222SAdrian Chadd return (ENXIO); 920a9ad4222SAdrian Chadd 921a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_get(sc, p); 922a9ad4222SAdrian Chadd if (err != 0) 923a9ad4222SAdrian Chadd return (err); 924a9ad4222SAdrian Chadd 925a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port); 926749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) { 927a043e8c7SAdrian Chadd /* fill in fixed values for CPU port */ 928a9ad4222SAdrian Chadd /* XXX is this valid in all cases? */ 929f47857dcSAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 930b9f07b86SLuiz Otavio O Souza ifmr = &p->es_ifmr; 931a043e8c7SAdrian Chadd ifmr->ifm_count = 0; 932a043e8c7SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 933a043e8c7SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 934a043e8c7SAdrian Chadd ifmr->ifm_mask = 0; 935a043e8c7SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 936a043e8c7SAdrian Chadd } else if (mii != NULL) { 937a043e8c7SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 938a043e8c7SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 939a043e8c7SAdrian Chadd if (err) 940a043e8c7SAdrian Chadd return (err); 941a043e8c7SAdrian Chadd } else { 942a043e8c7SAdrian Chadd return (ENXIO); 943a043e8c7SAdrian Chadd } 944c94dc808SAdrian Chadd 945c94dc808SAdrian Chadd if (!arswitch_is_cpuport(sc, p->es_port) && 946c94dc808SAdrian Chadd AR8X16_IS_SWITCH(sc, AR8327)) { 947c94dc808SAdrian Chadd int led; 948c94dc808SAdrian Chadd p->es_nleds = 3; 949c94dc808SAdrian Chadd 950c94dc808SAdrian Chadd for (led = 0; led < p->es_nleds; led++) 951c94dc808SAdrian Chadd { 952c94dc808SAdrian Chadd int style; 953c94dc808SAdrian Chadd uint32_t val; 954c94dc808SAdrian Chadd 955c94dc808SAdrian Chadd /* Find the right style enum for our pattern */ 956c94dc808SAdrian Chadd val = arswitch_readreg(dev, 957c94dc808SAdrian Chadd ar8327_led_mapping[p->es_port-1][led].reg); 958c94dc808SAdrian Chadd val = (val>>ar8327_led_mapping[p->es_port-1][led].shift)&0x03; 959c94dc808SAdrian Chadd 960c94dc808SAdrian Chadd for (style = 0; style < ETHERSWITCH_PORT_LED_MAX; style++) 961c94dc808SAdrian Chadd { 962c94dc808SAdrian Chadd if (led_pattern_table[style] == val) break; 963c94dc808SAdrian Chadd } 964c94dc808SAdrian Chadd 965c94dc808SAdrian Chadd /* can't happen */ 966c94dc808SAdrian Chadd if (style == ETHERSWITCH_PORT_LED_MAX) 967c94dc808SAdrian Chadd style = ETHERSWITCH_PORT_LED_DEFAULT; 968c94dc808SAdrian Chadd 969c94dc808SAdrian Chadd p->es_led[led] = style; 970c94dc808SAdrian Chadd } 971c94dc808SAdrian Chadd } else 972c94dc808SAdrian Chadd { 973c94dc808SAdrian Chadd p->es_nleds = 0; 974c94dc808SAdrian Chadd } 975c94dc808SAdrian Chadd 976a043e8c7SAdrian Chadd return (0); 977a043e8c7SAdrian Chadd } 978a043e8c7SAdrian Chadd 979a043e8c7SAdrian Chadd static int 980a9ad4222SAdrian Chadd ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p) 981a043e8c7SAdrian Chadd { 982b9f07b86SLuiz Otavio O Souza uint32_t reg; 983a9ad4222SAdrian Chadd int err; 984a043e8c7SAdrian Chadd 985b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc); 986a9ad4222SAdrian Chadd 987b9f07b86SLuiz Otavio O Souza /* Set the PVID. */ 988b9f07b86SLuiz Otavio O Souza if (p->es_pvid != 0) 9896dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid); 990b9f07b86SLuiz Otavio O Souza 991b9f07b86SLuiz Otavio O Souza /* Mutually exclusive. */ 992b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG && 993b9f07b86SLuiz Otavio O Souza p->es_flags & ETHERSWITCH_PORT_STRIPTAG) { 994b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 995b9f07b86SLuiz Otavio O Souza return (EINVAL); 996b9f07b86SLuiz Otavio O Souza } 997b9f07b86SLuiz Otavio O Souza 998b9f07b86SLuiz Otavio O Souza reg = 0; 999b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG) 1000b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_DOUBLE_TAG; 1001b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 1002b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD << 1003b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 1004b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 1005b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP << 1006b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 1007b9f07b86SLuiz Otavio O Souza 1008b9f07b86SLuiz Otavio O Souza err = arswitch_modifyreg(sc->sc_dev, 1009b9f07b86SLuiz Otavio O Souza AR8X16_REG_PORT_CTRL(p->es_port), 1010b9f07b86SLuiz Otavio O Souza 0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT | 1011b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_DOUBLE_TAG, reg); 1012b9f07b86SLuiz Otavio O Souza 1013b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 1014a9ad4222SAdrian Chadd return (err); 1015a9ad4222SAdrian Chadd } 1016a9ad4222SAdrian Chadd 1017a9ad4222SAdrian Chadd static int 1018a9ad4222SAdrian Chadd arswitch_setport(device_t dev, etherswitch_port_t *p) 1019a9ad4222SAdrian Chadd { 1020c94dc808SAdrian Chadd int err, i; 1021a9ad4222SAdrian Chadd struct arswitch_softc *sc; 1022a9ad4222SAdrian Chadd struct ifmedia *ifm; 1023a9ad4222SAdrian Chadd struct mii_data *mii; 10242e6a8c1aSJustin Hibbits if_t ifp; 1025a9ad4222SAdrian Chadd 1026a9ad4222SAdrian Chadd sc = device_get_softc(dev); 1027749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports) 1028a9ad4222SAdrian Chadd return (ENXIO); 1029a9ad4222SAdrian Chadd 1030a9ad4222SAdrian Chadd /* Port flags. */ 1031a9ad4222SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 1032a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_setup(sc, p); 1033b9f07b86SLuiz Otavio O Souza if (err) 1034b9f07b86SLuiz Otavio O Souza return (err); 1035b9f07b86SLuiz Otavio O Souza } 1036b9f07b86SLuiz Otavio O Souza 1037c94dc808SAdrian Chadd /* Do not allow media or led changes on CPU port. */ 1038749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) 1039b9f07b86SLuiz Otavio O Souza return (0); 1040a043e8c7SAdrian Chadd 1041c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) 1042c94dc808SAdrian Chadd { 1043c94dc808SAdrian Chadd for (i = 0; i < 3; i++) 1044c94dc808SAdrian Chadd { 1045c94dc808SAdrian Chadd int err; 1046c94dc808SAdrian Chadd err = arswitch_setled(sc, p->es_port-1, i, p->es_led[i]); 1047c94dc808SAdrian Chadd if (err) 1048c94dc808SAdrian Chadd return (err); 1049c94dc808SAdrian Chadd } 1050c94dc808SAdrian Chadd } 1051c94dc808SAdrian Chadd 1052a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port); 1053a043e8c7SAdrian Chadd if (mii == NULL) 1054a043e8c7SAdrian Chadd return (ENXIO); 1055a043e8c7SAdrian Chadd 1056a043e8c7SAdrian Chadd ifp = arswitch_ifpforport(sc, p->es_port); 1057a043e8c7SAdrian Chadd 1058a043e8c7SAdrian Chadd ifm = &mii->mii_media; 1059b9f07b86SLuiz Otavio O Souza return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); 1060a043e8c7SAdrian Chadd } 1061a043e8c7SAdrian Chadd 1062c94dc808SAdrian Chadd static int 1063c94dc808SAdrian Chadd arswitch_setled(struct arswitch_softc *sc, int phy, int led, int style) 1064c94dc808SAdrian Chadd { 1065c94dc808SAdrian Chadd int shift; 10666d011946SKristof Provost int err; 1067c94dc808SAdrian Chadd 1068c94dc808SAdrian Chadd if (phy < 0 || phy > sc->numphys) 1069c94dc808SAdrian Chadd return EINVAL; 1070c94dc808SAdrian Chadd 1071c94dc808SAdrian Chadd if (style < 0 || style > ETHERSWITCH_PORT_LED_MAX) 1072c94dc808SAdrian Chadd return (EINVAL); 1073c94dc808SAdrian Chadd 10746d011946SKristof Provost ARSWITCH_LOCK(sc); 10756d011946SKristof Provost 1076c94dc808SAdrian Chadd shift = ar8327_led_mapping[phy][led].shift; 10776d011946SKristof Provost err = (arswitch_modifyreg(sc->sc_dev, 1078c94dc808SAdrian Chadd ar8327_led_mapping[phy][led].reg, 1079c94dc808SAdrian Chadd 0x03 << shift, led_pattern_table[style] << shift)); 10806d011946SKristof Provost ARSWITCH_UNLOCK(sc); 10816d011946SKristof Provost 10826d011946SKristof Provost return (err); 1083c94dc808SAdrian Chadd } 1084c94dc808SAdrian Chadd 1085a043e8c7SAdrian Chadd static void 1086a043e8c7SAdrian Chadd arswitch_statchg(device_t dev) 1087a043e8c7SAdrian Chadd { 10881b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 1089a043e8c7SAdrian Chadd 10901b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__); 1091a043e8c7SAdrian Chadd } 1092a043e8c7SAdrian Chadd 1093a043e8c7SAdrian Chadd static int 10942e6a8c1aSJustin Hibbits arswitch_ifmedia_upd(if_t ifp) 1095a043e8c7SAdrian Chadd { 10962e6a8c1aSJustin Hibbits struct arswitch_softc *sc = if_getsoftc(ifp); 10972e6a8c1aSJustin Hibbits struct mii_data *mii = arswitch_miiforport(sc, if_getdunit(ifp)); 1098a043e8c7SAdrian Chadd 1099a043e8c7SAdrian Chadd if (mii == NULL) 1100a043e8c7SAdrian Chadd return (ENXIO); 1101a043e8c7SAdrian Chadd mii_mediachg(mii); 1102a043e8c7SAdrian Chadd return (0); 1103a043e8c7SAdrian Chadd } 1104a043e8c7SAdrian Chadd 1105a043e8c7SAdrian Chadd static void 11062e6a8c1aSJustin Hibbits arswitch_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr) 1107a043e8c7SAdrian Chadd { 11082e6a8c1aSJustin Hibbits struct arswitch_softc *sc = if_getsoftc(ifp); 11092e6a8c1aSJustin Hibbits struct mii_data *mii = arswitch_miiforport(sc, if_getdunit(ifp)); 1110a043e8c7SAdrian Chadd 11111b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__); 1112a043e8c7SAdrian Chadd 1113a043e8c7SAdrian Chadd if (mii == NULL) 1114a043e8c7SAdrian Chadd return; 1115a043e8c7SAdrian Chadd mii_pollstat(mii); 1116a043e8c7SAdrian Chadd ifmr->ifm_active = mii->mii_media_active; 1117a043e8c7SAdrian Chadd ifmr->ifm_status = mii->mii_media_status; 1118a043e8c7SAdrian Chadd } 1119a043e8c7SAdrian Chadd 1120b9f07b86SLuiz Otavio O Souza static int 1121b9f07b86SLuiz Otavio O Souza arswitch_getconf(device_t dev, etherswitch_conf_t *conf) 1122b9f07b86SLuiz Otavio O Souza { 1123b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc; 11242ba4bf8fSAdrian Chadd int ret; 1125b9f07b86SLuiz Otavio O Souza 1126b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev); 1127b9f07b86SLuiz Otavio O Souza 1128b9f07b86SLuiz Otavio O Souza /* Return the VLAN mode. */ 1129b9f07b86SLuiz Otavio O Souza conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 1130b9f07b86SLuiz Otavio O Souza conf->vlan_mode = sc->vlan_mode; 1131b9f07b86SLuiz Otavio O Souza 11322ba4bf8fSAdrian Chadd /* Return the switch ethernet address. */ 11332ba4bf8fSAdrian Chadd ret = sc->hal.arswitch_hw_get_switch_macaddr(sc, 11342ba4bf8fSAdrian Chadd &conf->switch_macaddr); 11352ba4bf8fSAdrian Chadd if (ret == 0) { 11362ba4bf8fSAdrian Chadd conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR; 11372ba4bf8fSAdrian Chadd } 11382ba4bf8fSAdrian Chadd 1139b9f07b86SLuiz Otavio O Souza return (0); 1140b9f07b86SLuiz Otavio O Souza } 1141b9f07b86SLuiz Otavio O Souza 1142b9f07b86SLuiz Otavio O Souza static int 1143b9f07b86SLuiz Otavio O Souza arswitch_setconf(device_t dev, etherswitch_conf_t *conf) 1144b9f07b86SLuiz Otavio O Souza { 1145b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc; 1146b9f07b86SLuiz Otavio O Souza int err; 1147b9f07b86SLuiz Otavio O Souza 1148b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev); 1149b9f07b86SLuiz Otavio O Souza 1150b9f07b86SLuiz Otavio O Souza /* Set the VLAN mode. */ 1151b9f07b86SLuiz Otavio O Souza if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { 1152b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, conf->vlan_mode); 1153b9f07b86SLuiz Otavio O Souza if (err != 0) 1154b9f07b86SLuiz Otavio O Souza return (err); 1155b9f07b86SLuiz Otavio O Souza } 1156b9f07b86SLuiz Otavio O Souza 11572ba4bf8fSAdrian Chadd /* TODO: Set the switch ethernet address. */ 11582ba4bf8fSAdrian Chadd 1159b9f07b86SLuiz Otavio O Souza return (0); 1160b9f07b86SLuiz Otavio O Souza } 1161b9f07b86SLuiz Otavio O Souza 11626dcbabd7SAdrian Chadd static int 116362042c97SAdrian Chadd arswitch_atu_flush_all(device_t dev) 116462042c97SAdrian Chadd { 116562042c97SAdrian Chadd struct arswitch_softc *sc; 116662042c97SAdrian Chadd int err; 116762042c97SAdrian Chadd 116862042c97SAdrian Chadd sc = device_get_softc(dev); 116962042c97SAdrian Chadd ARSWITCH_LOCK(sc); 117062042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush(sc); 117162042c97SAdrian Chadd /* Invalidate cached ATU */ 117262042c97SAdrian Chadd sc->atu.count = 0; 117362042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 117462042c97SAdrian Chadd return (err); 117562042c97SAdrian Chadd } 117662042c97SAdrian Chadd 117762042c97SAdrian Chadd static int 117862042c97SAdrian Chadd arswitch_atu_flush_port(device_t dev, int port) 117962042c97SAdrian Chadd { 118062042c97SAdrian Chadd struct arswitch_softc *sc; 118162042c97SAdrian Chadd int err; 118262042c97SAdrian Chadd 118362042c97SAdrian Chadd sc = device_get_softc(dev); 118462042c97SAdrian Chadd ARSWITCH_LOCK(sc); 118562042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush_port(sc, port); 118662042c97SAdrian Chadd /* Invalidate cached ATU */ 118762042c97SAdrian Chadd sc->atu.count = 0; 118862042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 118962042c97SAdrian Chadd return (err); 119062042c97SAdrian Chadd } 119162042c97SAdrian Chadd 119262042c97SAdrian Chadd static int 119362042c97SAdrian Chadd arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table) 119462042c97SAdrian Chadd { 119562042c97SAdrian Chadd struct arswitch_softc *sc; 119662042c97SAdrian Chadd int err, nitems; 119762042c97SAdrian Chadd 119862042c97SAdrian Chadd sc = device_get_softc(dev); 119962042c97SAdrian Chadd 120062042c97SAdrian Chadd ARSWITCH_LOCK(sc); 120162042c97SAdrian Chadd /* Initial setup */ 120262042c97SAdrian Chadd nitems = 0; 120362042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0); 120462042c97SAdrian Chadd 120562042c97SAdrian Chadd /* fetch - ideally yes we'd fetch into a separate table then switch */ 12067ed08319SAdrian Chadd while (err == 0 && nitems < sc->atu.size) { 120762042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, 120862042c97SAdrian Chadd &sc->atu.entries[nitems], 1); 120962042c97SAdrian Chadd if (err == 0) { 121062042c97SAdrian Chadd sc->atu.entries[nitems].id = nitems; 121162042c97SAdrian Chadd nitems++; 121262042c97SAdrian Chadd } 121362042c97SAdrian Chadd } 121462042c97SAdrian Chadd sc->atu.count = nitems; 121562042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 121662042c97SAdrian Chadd 121762042c97SAdrian Chadd table->es_nitems = nitems; 121862042c97SAdrian Chadd 121962042c97SAdrian Chadd return (0); 122062042c97SAdrian Chadd } 122162042c97SAdrian Chadd 122262042c97SAdrian Chadd static int 122362042c97SAdrian Chadd arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e) 122462042c97SAdrian Chadd { 122562042c97SAdrian Chadd struct arswitch_softc *sc; 122662042c97SAdrian Chadd int id; 122762042c97SAdrian Chadd 122862042c97SAdrian Chadd sc = device_get_softc(dev); 122962042c97SAdrian Chadd id = e->id; 123062042c97SAdrian Chadd 123162042c97SAdrian Chadd ARSWITCH_LOCK(sc); 123262042c97SAdrian Chadd if (id > sc->atu.count) { 123362042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 123462042c97SAdrian Chadd return (ENOENT); 123562042c97SAdrian Chadd } 123662042c97SAdrian Chadd 123762042c97SAdrian Chadd memcpy(e, &sc->atu.entries[id], sizeof(*e)); 123862042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 123962042c97SAdrian Chadd return (0); 124062042c97SAdrian Chadd } 124162042c97SAdrian Chadd 124262042c97SAdrian Chadd static int 12436dcbabd7SAdrian Chadd arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e) 12446dcbabd7SAdrian Chadd { 12456dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 12466dcbabd7SAdrian Chadd 12476dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_getvgroup(sc, e)); 12486dcbabd7SAdrian Chadd } 12496dcbabd7SAdrian Chadd 12506dcbabd7SAdrian Chadd static int 12516dcbabd7SAdrian Chadd arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e) 12526dcbabd7SAdrian Chadd { 12536dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 12546dcbabd7SAdrian Chadd 12556dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_setvgroup(sc, e)); 12566dcbabd7SAdrian Chadd } 12576dcbabd7SAdrian Chadd 125878549b94SAdrian Chadd static int 125978549b94SAdrian Chadd arswitch_readphy(device_t dev, int phy, int reg) 126078549b94SAdrian Chadd { 126178549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 126278549b94SAdrian Chadd 126378549b94SAdrian Chadd return (sc->hal.arswitch_phy_read(dev, phy, reg)); 126478549b94SAdrian Chadd } 126578549b94SAdrian Chadd 126678549b94SAdrian Chadd static int 126778549b94SAdrian Chadd arswitch_writephy(device_t dev, int phy, int reg, int val) 126878549b94SAdrian Chadd { 126978549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 127078549b94SAdrian Chadd 127178549b94SAdrian Chadd return (sc->hal.arswitch_phy_write(dev, phy, reg, val)); 127278549b94SAdrian Chadd } 127378549b94SAdrian Chadd 1274a043e8c7SAdrian Chadd static device_method_t arswitch_methods[] = { 1275a043e8c7SAdrian Chadd /* Device interface */ 1276a043e8c7SAdrian Chadd DEVMETHOD(device_probe, arswitch_probe), 1277a043e8c7SAdrian Chadd DEVMETHOD(device_attach, arswitch_attach), 1278a043e8c7SAdrian Chadd DEVMETHOD(device_detach, arswitch_detach), 1279a043e8c7SAdrian Chadd 1280a043e8c7SAdrian Chadd /* bus interface */ 1281a043e8c7SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 1282a043e8c7SAdrian Chadd 1283a043e8c7SAdrian Chadd /* MII interface */ 1284a043e8c7SAdrian Chadd DEVMETHOD(miibus_readreg, arswitch_readphy), 1285a043e8c7SAdrian Chadd DEVMETHOD(miibus_writereg, arswitch_writephy), 1286a043e8c7SAdrian Chadd DEVMETHOD(miibus_statchg, arswitch_statchg), 1287a043e8c7SAdrian Chadd 1288a043e8c7SAdrian Chadd /* MDIO interface */ 1289a043e8c7SAdrian Chadd DEVMETHOD(mdio_readreg, arswitch_readphy), 1290a043e8c7SAdrian Chadd DEVMETHOD(mdio_writereg, arswitch_writephy), 1291a043e8c7SAdrian Chadd 1292a043e8c7SAdrian Chadd /* etherswitch interface */ 1293454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_lock, arswitch_lock), 1294454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_unlock, arswitch_unlock), 1295a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getinfo, arswitch_getinfo), 1296a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readreg, arswitch_readreg), 1297a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writereg, arswitch_writereg), 1298a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, arswitch_readphy), 1299a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, arswitch_writephy), 1300a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getport, arswitch_getport), 1301a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setport, arswitch_setport), 1302a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, arswitch_getvgroup), 1303a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, arswitch_setvgroup), 1304b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_getconf, arswitch_getconf), 1305b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_setconf, arswitch_setconf), 130662042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all), 130762042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port), 130862042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table), 130962042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry), 1310a043e8c7SAdrian Chadd 1311a043e8c7SAdrian Chadd DEVMETHOD_END 1312a043e8c7SAdrian Chadd }; 1313a043e8c7SAdrian Chadd 1314a043e8c7SAdrian Chadd DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods, 1315a043e8c7SAdrian Chadd sizeof(struct arswitch_softc)); 1316a043e8c7SAdrian Chadd 131742726c2fSJohn Baldwin DRIVER_MODULE(arswitch, mdio, arswitch_driver, 0, 0); 13183e38757dSJohn Baldwin DRIVER_MODULE(miibus, arswitch, miibus_driver, 0, 0); 13198933f7d6SJohn Baldwin DRIVER_MODULE(mdio, arswitch, mdio_driver, 0, 0); 1320829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, 0, 0); 1321a043e8c7SAdrian Chadd MODULE_VERSION(arswitch, 1); 1322a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */ 1323a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */ 1324