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; 9854482989SMark 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) { 13554482989SMark Johnston device_set_descf(dev, 13678549b94SAdrian Chadd "Atheros %s Ethernet Switch (ver %d rev %d)", 13754482989SMark 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); 1532e6a8c1aSJustin Hibbits if_setsoftc(sc->ifp[phy], sc); 1542e6a8c1aSJustin Hibbits if_setflagbits(sc->ifp[phy], IFF_UP | IFF_BROADCAST | 1552e6a8c1aSJustin Hibbits IFF_DRV_RUNNING | IFF_SIMPLEX, 0); 156a043e8c7SAdrian Chadd sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK); 157a043e8c7SAdrian Chadd bcopy(name, sc->ifname[phy], strlen(name)+1); 158a043e8c7SAdrian Chadd if_initname(sc->ifp[phy], sc->ifname[phy], 159a043e8c7SAdrian Chadd arswitch_portforphy(phy)); 160a043e8c7SAdrian Chadd err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy], 161a043e8c7SAdrian Chadd arswitch_ifmedia_upd, arswitch_ifmedia_sts, \ 162a043e8c7SAdrian Chadd BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 16378549b94SAdrian Chadd #if 0 164a043e8c7SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 165a043e8c7SAdrian Chadd device_get_nameunit(sc->miibus[phy]), 166a043e8c7SAdrian Chadd sc->ifp[phy]->if_xname); 16778549b94SAdrian Chadd #endif 168a043e8c7SAdrian Chadd if (err != 0) { 169a043e8c7SAdrian Chadd device_printf(sc->sc_dev, 170a043e8c7SAdrian Chadd "attaching PHY %d failed\n", 171a043e8c7SAdrian Chadd phy); 172a043e8c7SAdrian Chadd return (err); 173a043e8c7SAdrian Chadd } 174a043e8c7SAdrian Chadd 175c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) { 176c94dc808SAdrian Chadd int led; 177c94dc808SAdrian Chadd char ledname[IFNAMSIZ+4]; 178c94dc808SAdrian Chadd 179c94dc808SAdrian Chadd for (led = 0; led < 3; led++) { 180c94dc808SAdrian Chadd sprintf(ledname, "%s%dled%d", name, 181c94dc808SAdrian Chadd arswitch_portforphy(phy), led+1); 182c94dc808SAdrian Chadd sc->dev_led[phy][led].sc = sc; 183c94dc808SAdrian Chadd sc->dev_led[phy][led].phy = phy; 184c94dc808SAdrian Chadd sc->dev_led[phy][led].lednum = led; 185c94dc808SAdrian Chadd } 186c94dc808SAdrian Chadd } 187c94dc808SAdrian Chadd } 188c94dc808SAdrian Chadd return (0); 189c94dc808SAdrian Chadd } 190c94dc808SAdrian Chadd 191a043e8c7SAdrian Chadd static int 192b9f07b86SLuiz Otavio O Souza arswitch_reset(device_t dev) 193b9f07b86SLuiz Otavio O Souza { 194b9f07b86SLuiz Otavio O Souza 195b9f07b86SLuiz Otavio O Souza arswitch_writereg(dev, AR8X16_REG_MASK_CTRL, 196b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET); 197b9f07b86SLuiz Otavio O Souza DELAY(1000); 198b9f07b86SLuiz Otavio O Souza if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) & 199b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET) { 200b9f07b86SLuiz Otavio O Souza device_printf(dev, "unable to reset switch\n"); 201b9f07b86SLuiz Otavio O Souza return (-1); 202b9f07b86SLuiz Otavio O Souza } 203b9f07b86SLuiz Otavio O Souza return (0); 204b9f07b86SLuiz Otavio O Souza } 205b9f07b86SLuiz Otavio O Souza 206b9f07b86SLuiz Otavio O Souza static int 207b9f07b86SLuiz Otavio O Souza arswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode) 208b9f07b86SLuiz Otavio O Souza { 209b9f07b86SLuiz Otavio O Souza 210b9f07b86SLuiz Otavio O Souza /* Check for invalid modes. */ 211b9f07b86SLuiz Otavio O Souza if ((mode & sc->info.es_vlan_caps) != mode) 212b9f07b86SLuiz Otavio O Souza return (EINVAL); 213b9f07b86SLuiz Otavio O Souza 214b9f07b86SLuiz Otavio O Souza switch (mode) { 215b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_DOT1Q: 216b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 217b9f07b86SLuiz Otavio O Souza break; 218b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_PORT: 219b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_PORT; 220b9f07b86SLuiz Otavio O Souza break; 221b9f07b86SLuiz Otavio O Souza default: 222b9f07b86SLuiz Otavio O Souza sc->vlan_mode = 0; 22374b8d63dSPedro F. Giffuni } 224b9f07b86SLuiz Otavio O Souza 225b9f07b86SLuiz Otavio O Souza /* Reset VLANs. */ 2266dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw(sc); 227b9f07b86SLuiz Otavio O Souza 228b9f07b86SLuiz Otavio O Souza return (0); 229b9f07b86SLuiz Otavio O Souza } 230b9f07b86SLuiz Otavio O Souza 231b9f07b86SLuiz Otavio O Souza static void 232a9ad4222SAdrian Chadd ar8xxx_port_init(struct arswitch_softc *sc, int port) 233b9f07b86SLuiz Otavio O Souza { 234b9f07b86SLuiz Otavio O Souza 235b9f07b86SLuiz Otavio O Souza /* Port0 - CPU */ 236df892897SAdrian Chadd if (port == AR8X16_PORT_CPU) { 237b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(0), 238b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 239b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_SPEED_100 : AR8X16_PORT_STS_SPEED_1000) | 240b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_RXFLOW) | 241b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_TXFLOW) | 242b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_RXMAC | 243b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_TXMAC | 244b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_DUPLEX); 245b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0), 246b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0)) & 247b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER); 248df892897SAdrian Chadd } else { 249b9f07b86SLuiz Otavio O Souza /* Set ports to auto negotiation. */ 250b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(port), 251b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_LINK_AUTO); 252b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port), 253b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port)) & 254b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER); 255b9f07b86SLuiz Otavio O Souza } 256b9f07b86SLuiz Otavio O Souza } 257b9f07b86SLuiz Otavio O Souza 258b9f07b86SLuiz Otavio O Souza static int 25962042c97SAdrian Chadd ar8xxx_atu_wait_ready(struct arswitch_softc *sc) 2604ff2f60dSAdrian Chadd { 2614ff2f60dSAdrian Chadd int ret; 2624ff2f60dSAdrian Chadd 26362042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 26462042c97SAdrian Chadd 2654ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev, 2664ff2f60dSAdrian Chadd AR8216_REG_ATU, 2674ff2f60dSAdrian Chadd AR8216_ATU_ACTIVE, 2684ff2f60dSAdrian Chadd 0, 2694ff2f60dSAdrian Chadd 1000); 2704ff2f60dSAdrian Chadd 27162042c97SAdrian Chadd return (ret); 27262042c97SAdrian Chadd } 27362042c97SAdrian Chadd 27462042c97SAdrian Chadd /* 27562042c97SAdrian Chadd * Flush all ATU entries. 27662042c97SAdrian Chadd */ 27762042c97SAdrian Chadd static int 27862042c97SAdrian Chadd ar8xxx_atu_flush(struct arswitch_softc *sc) 27962042c97SAdrian Chadd { 28062042c97SAdrian Chadd int ret; 28162042c97SAdrian Chadd 28262042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 28362042c97SAdrian Chadd 28462042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing all ports\n", __func__); 28562042c97SAdrian Chadd 28662042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc); 2874ff2f60dSAdrian Chadd if (ret) 2884ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 2894ff2f60dSAdrian Chadd 2904ff2f60dSAdrian Chadd if (!ret) 2914ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev, 2924ff2f60dSAdrian Chadd AR8216_REG_ATU, 2932c6ceccaSAdrian Chadd AR8216_ATU_OP_FLUSH | AR8216_ATU_ACTIVE); 2944ff2f60dSAdrian Chadd 2954ff2f60dSAdrian Chadd return (ret); 2964ff2f60dSAdrian Chadd } 2974ff2f60dSAdrian Chadd 29862042c97SAdrian Chadd /* 29962042c97SAdrian Chadd * Flush ATU entries for a single port. 30062042c97SAdrian Chadd */ 30162042c97SAdrian Chadd static int 30262042c97SAdrian Chadd ar8xxx_atu_flush_port(struct arswitch_softc *sc, int port) 30362042c97SAdrian Chadd { 30462042c97SAdrian Chadd int ret, val; 30562042c97SAdrian Chadd 30662042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing port %d\n", __func__, 30762042c97SAdrian Chadd port); 30862042c97SAdrian Chadd 30962042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 31062042c97SAdrian Chadd 31162042c97SAdrian Chadd /* Flush unicast entries on port */ 31262042c97SAdrian Chadd val = AR8216_ATU_OP_FLUSH_UNICAST; 31362042c97SAdrian Chadd 31462042c97SAdrian Chadd /* TODO: bit 4 indicates whether to flush dynamic (0) or static (1) */ 31562042c97SAdrian Chadd 31662042c97SAdrian Chadd /* Which port */ 31762042c97SAdrian Chadd val |= SM(port, AR8216_ATU_PORT_NUM); 31862042c97SAdrian Chadd 31962042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc); 32062042c97SAdrian Chadd if (ret) 32162042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 32262042c97SAdrian Chadd 32362042c97SAdrian Chadd if (!ret) 32462042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 32562042c97SAdrian Chadd AR8216_REG_ATU, 32662042c97SAdrian Chadd val | AR8216_ATU_ACTIVE); 32762042c97SAdrian Chadd 32862042c97SAdrian Chadd return (ret); 32962042c97SAdrian Chadd } 33062042c97SAdrian Chadd 33162042c97SAdrian Chadd /* 33262042c97SAdrian Chadd * XXX TODO: flush a single MAC address. 33362042c97SAdrian Chadd */ 33462042c97SAdrian Chadd 33562042c97SAdrian Chadd /* 33662042c97SAdrian Chadd * Fetch a single entry from the ATU. 33762042c97SAdrian Chadd */ 33862042c97SAdrian Chadd static int 33962042c97SAdrian Chadd ar8xxx_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e, 34062042c97SAdrian Chadd int atu_fetch_op) 34162042c97SAdrian Chadd { 34262042c97SAdrian Chadd uint32_t ret0, ret1, ret2, val; 34362042c97SAdrian Chadd 34462042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 34562042c97SAdrian Chadd 34662042c97SAdrian Chadd switch (atu_fetch_op) { 34762042c97SAdrian Chadd case 0: 34862042c97SAdrian Chadd /* Initialise things for the first fetch */ 34962042c97SAdrian Chadd 35062042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__); 35162042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 35262042c97SAdrian Chadd 35362042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 35462042c97SAdrian Chadd AR8216_REG_ATU, AR8216_ATU_OP_GET_NEXT); 35562042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 35662042c97SAdrian Chadd AR8216_REG_ATU_DATA, 0); 35762042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 35862042c97SAdrian Chadd AR8216_REG_ATU_CTRL2, 0); 35962042c97SAdrian Chadd 36062042c97SAdrian Chadd return (0); 36162042c97SAdrian Chadd case 1: 36262042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__); 36362042c97SAdrian Chadd /* 36462042c97SAdrian Chadd * Attempt to read the next address entry; don't modify what 36562042c97SAdrian Chadd * is there in AT_ADDR{4,5} as its used for the next fetch 36662042c97SAdrian Chadd */ 36762042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 36862042c97SAdrian Chadd 36962042c97SAdrian Chadd /* Begin the next read event; not modifying anything */ 37062042c97SAdrian Chadd val = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU); 37162042c97SAdrian Chadd val |= AR8216_ATU_ACTIVE; 37262042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8216_REG_ATU, val); 37362042c97SAdrian Chadd 37462042c97SAdrian Chadd /* Wait for it to complete */ 37562042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 37662042c97SAdrian Chadd 37762042c97SAdrian Chadd /* Fetch the ethernet address and ATU status */ 37862042c97SAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU); 37962042c97SAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_DATA); 38062042c97SAdrian Chadd ret2 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_CTRL2); 38162042c97SAdrian Chadd 38262042c97SAdrian Chadd /* If the status is zero, then we're done */ 38362042c97SAdrian Chadd if (MS(ret2, AR8216_ATU_CTRL2_AT_STATUS) == 0) 38462042c97SAdrian Chadd return (-1); 38562042c97SAdrian Chadd 38662042c97SAdrian Chadd /* MAC address */ 38762042c97SAdrian Chadd e->es_macaddr[5] = MS(ret0, AR8216_ATU_ADDR5); 38862042c97SAdrian Chadd e->es_macaddr[4] = MS(ret0, AR8216_ATU_ADDR4); 38962042c97SAdrian Chadd e->es_macaddr[3] = MS(ret1, AR8216_ATU_ADDR3); 39062042c97SAdrian Chadd e->es_macaddr[2] = MS(ret1, AR8216_ATU_ADDR2); 39162042c97SAdrian Chadd e->es_macaddr[1] = MS(ret1, AR8216_ATU_ADDR1); 39262042c97SAdrian Chadd e->es_macaddr[0] = MS(ret1, AR8216_ATU_ADDR0); 39362042c97SAdrian Chadd 39462042c97SAdrian Chadd /* Bitmask of ports this entry is for */ 39562042c97SAdrian Chadd e->es_portmask = MS(ret2, AR8216_ATU_CTRL2_DESPORT); 39662042c97SAdrian Chadd 39762042c97SAdrian Chadd /* TODO: other flags that are interesting */ 39862042c97SAdrian Chadd 39962042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n", 40062042c97SAdrian Chadd __func__, 40162042c97SAdrian Chadd e->es_macaddr, ":", e->es_portmask); 40262042c97SAdrian Chadd return (0); 40362042c97SAdrian Chadd default: 40462042c97SAdrian Chadd return (-1); 40562042c97SAdrian Chadd } 40662042c97SAdrian Chadd return (-1); 40762042c97SAdrian Chadd } 40862042c97SAdrian Chadd 40962042c97SAdrian Chadd /* 41062042c97SAdrian Chadd * Configure aging register defaults. 41162042c97SAdrian Chadd */ 41262042c97SAdrian Chadd static int 41362042c97SAdrian Chadd ar8xxx_atu_learn_default(struct arswitch_softc *sc) 41462042c97SAdrian Chadd { 41562042c97SAdrian Chadd int ret; 41662042c97SAdrian Chadd uint32_t val; 41762042c97SAdrian Chadd 41862042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: resetting learning\n", __func__); 41962042c97SAdrian Chadd 42062042c97SAdrian Chadd /* 42162042c97SAdrian Chadd * For now, configure the aging defaults: 42262042c97SAdrian Chadd * 42362042c97SAdrian Chadd * + ARP_EN - enable "acknowledgement" of ARP frames - they are 42462042c97SAdrian Chadd * forwarded to the CPU port 42562042c97SAdrian Chadd * + LEARN_CHANGE_EN - hash table violations when learning MAC addresses 42662042c97SAdrian Chadd * will force an entry to be expired/updated and a new one to be 42762042c97SAdrian Chadd * programmed in. 42862042c97SAdrian Chadd * + AGE_EN - enable address table aging 42962042c97SAdrian Chadd * + AGE_TIME - set to 5 minutes 43062042c97SAdrian Chadd */ 43162042c97SAdrian Chadd val = 0; 43262042c97SAdrian Chadd val |= AR8216_ATU_CTRL_ARP_EN; 43362042c97SAdrian Chadd val |= AR8216_ATU_CTRL_LEARN_CHANGE; 43462042c97SAdrian Chadd val |= AR8216_ATU_CTRL_AGE_EN; 43562042c97SAdrian Chadd val |= 0x2b; /* 5 minutes; bits 15:0 */ 43662042c97SAdrian Chadd 43762042c97SAdrian Chadd ret = arswitch_writereg(sc->sc_dev, 43862042c97SAdrian Chadd AR8216_REG_ATU_CTRL, 43962042c97SAdrian Chadd val); 44062042c97SAdrian Chadd 44162042c97SAdrian Chadd if (ret) 44262042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: writereg failed\n", __func__); 44362042c97SAdrian Chadd 44462042c97SAdrian Chadd return (ret); 44562042c97SAdrian Chadd } 44662042c97SAdrian Chadd 44762042c97SAdrian Chadd /* 44862042c97SAdrian Chadd * XXX TODO: add another routine to configure the leaky behaviour 44962042c97SAdrian Chadd * when unknown frames are received. These must be consistent 45062042c97SAdrian Chadd * between ethernet switches. 45162042c97SAdrian Chadd */ 45262042c97SAdrian Chadd 45362042c97SAdrian Chadd /* 4542ba4bf8fSAdrian Chadd * Fetch the configured switch MAC address. 4552ba4bf8fSAdrian Chadd */ 4562ba4bf8fSAdrian Chadd static int 4572ba4bf8fSAdrian Chadd ar8xxx_hw_get_switch_macaddr(struct arswitch_softc *sc, struct ether_addr *ea) 4582ba4bf8fSAdrian Chadd { 4592ba4bf8fSAdrian Chadd uint32_t ret0, ret1; 4602ba4bf8fSAdrian Chadd char *s; 4612ba4bf8fSAdrian Chadd 4622ba4bf8fSAdrian Chadd s = (void *) ea; 4632ba4bf8fSAdrian Chadd 4642ba4bf8fSAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR0); 4652ba4bf8fSAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8X16_REG_SW_MAC_ADDR1); 4662ba4bf8fSAdrian Chadd 4672ba4bf8fSAdrian Chadd s[5] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE5); 4682ba4bf8fSAdrian Chadd s[4] = MS(ret0, AR8X16_REG_SW_MAC_ADDR0_BYTE4); 4692ba4bf8fSAdrian Chadd s[3] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE3); 4702ba4bf8fSAdrian Chadd s[2] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE2); 4712ba4bf8fSAdrian Chadd s[1] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE1); 4722ba4bf8fSAdrian Chadd s[0] = MS(ret1, AR8X16_REG_SW_MAC_ADDR1_BYTE0); 4732ba4bf8fSAdrian Chadd 4742ba4bf8fSAdrian Chadd return (0); 4752ba4bf8fSAdrian Chadd } 4762ba4bf8fSAdrian Chadd 4772ba4bf8fSAdrian Chadd /* 4782ba4bf8fSAdrian Chadd * Set the switch mac address. 4792ba4bf8fSAdrian Chadd */ 4802ba4bf8fSAdrian Chadd static int 4812ba4bf8fSAdrian Chadd ar8xxx_hw_set_switch_macaddr(struct arswitch_softc *sc, 4822ba4bf8fSAdrian Chadd const struct ether_addr *ea) 4832ba4bf8fSAdrian Chadd { 4842ba4bf8fSAdrian Chadd 4852ba4bf8fSAdrian Chadd return (ENXIO); 4862ba4bf8fSAdrian Chadd } 4872ba4bf8fSAdrian Chadd 4882ba4bf8fSAdrian Chadd /* 48962042c97SAdrian Chadd * XXX TODO: this attach routine does NOT free all memory, resources 49062042c97SAdrian Chadd * upon failure! 49162042c97SAdrian Chadd */ 4924ff2f60dSAdrian Chadd static int 493a043e8c7SAdrian Chadd arswitch_attach(device_t dev) 494a043e8c7SAdrian Chadd { 4951b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 4961b334c8bSAdrian Chadd struct sysctl_ctx_list *ctx; 4971b334c8bSAdrian Chadd struct sysctl_oid *tree; 498a043e8c7SAdrian Chadd int err = 0; 499df892897SAdrian Chadd int port; 500a043e8c7SAdrian Chadd 501a043e8c7SAdrian Chadd /* sc->sc_switchtype is already decided in arswitch_probe() */ 502a043e8c7SAdrian Chadd sc->sc_dev = dev; 503a043e8c7SAdrian Chadd mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF); 504a043e8c7SAdrian Chadd sc->page = -1; 505a043e8c7SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev), 506a043e8c7SAdrian Chadd sizeof(sc->info.es_name)); 507a043e8c7SAdrian Chadd 5081b334c8bSAdrian Chadd /* Debugging */ 5091b334c8bSAdrian Chadd ctx = device_get_sysctl_ctx(sc->sc_dev); 5101b334c8bSAdrian Chadd tree = device_get_sysctl_tree(sc->sc_dev); 5111b334c8bSAdrian Chadd SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5121b334c8bSAdrian Chadd "debug", CTLFLAG_RW, &sc->sc_debug, 0, 5131b334c8bSAdrian Chadd "control debugging printfs"); 5141b334c8bSAdrian Chadd 51562042c97SAdrian Chadd /* Allocate a 128 entry ATU table; hopefully its big enough! */ 51662042c97SAdrian Chadd /* XXX TODO: make this per chip */ 51762042c97SAdrian Chadd sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128, 51862042c97SAdrian Chadd M_DEVBUF, M_NOWAIT); 51962042c97SAdrian Chadd if (sc->atu.entries == NULL) { 52062042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n", 52162042c97SAdrian Chadd __func__); 52262042c97SAdrian Chadd return (ENXIO); 52362042c97SAdrian Chadd } 52462042c97SAdrian Chadd sc->atu.count = 0; 52562042c97SAdrian Chadd sc->atu.size = 128; 52662042c97SAdrian Chadd 527ddbc4420SAdrian Chadd /* Default HAL methods */ 528a9ad4222SAdrian Chadd sc->hal.arswitch_port_init = ar8xxx_port_init; 529a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup; 530a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get; 5316dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans; 5322ba4bf8fSAdrian Chadd sc->hal.arswitch_hw_get_switch_macaddr = ar8xxx_hw_get_switch_macaddr; 5332ba4bf8fSAdrian Chadd sc->hal.arswitch_hw_set_switch_macaddr = ar8xxx_hw_set_switch_macaddr; 534749cac13SAdrian Chadd 5356dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup; 5366dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup; 537749cac13SAdrian Chadd 5386dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid; 5396dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid; 540749cac13SAdrian Chadd 541749cac13SAdrian Chadd sc->hal.arswitch_get_dot1q_vlan = ar8xxx_get_dot1q_vlan; 542749cac13SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan = ar8xxx_set_dot1q_vlan; 543f35f94f4SAdrian Chadd sc->hal.arswitch_flush_dot1q_vlan = ar8xxx_flush_dot1q_vlan; 544f35f94f4SAdrian Chadd sc->hal.arswitch_purge_dot1q_vlan = ar8xxx_purge_dot1q_vlan; 545749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8xxx_get_port_vlan; 546749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan; 547749cac13SAdrian Chadd 5484ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8xxx_atu_flush; 54962042c97SAdrian Chadd sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port; 55062042c97SAdrian Chadd sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default; 55162042c97SAdrian Chadd sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table; 552749cac13SAdrian Chadd 55378549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_internal; 55478549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_internal; 555ddbc4420SAdrian Chadd 556a043e8c7SAdrian Chadd /* 557a043e8c7SAdrian Chadd * Attach switch related functions 558a043e8c7SAdrian Chadd */ 5598f5dbc22SMarius Strobl if (AR8X16_IS_SWITCH(sc, AR8216)) 560a043e8c7SAdrian Chadd ar8216_attach(sc); 561a043e8c7SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8226)) 562a043e8c7SAdrian Chadd ar8226_attach(sc); 563a043e8c7SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8316)) 564a043e8c7SAdrian Chadd ar8316_attach(sc); 565482d268dSAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR8327)) 566482d268dSAdrian Chadd ar8327_attach(sc); 56778549b94SAdrian Chadd else { 5681b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 5691b334c8bSAdrian Chadd "%s: unknown switch (%d)?\n", __func__, sc->sc_switchtype); 570a043e8c7SAdrian Chadd return (ENXIO); 57178549b94SAdrian Chadd } 572a043e8c7SAdrian Chadd 573b9f07b86SLuiz Otavio O Souza /* Common defaults. */ 574a043e8c7SAdrian Chadd sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */ 575a043e8c7SAdrian Chadd 576a043e8c7SAdrian Chadd /* XXX Defaults for externally connected AR8316 */ 577a043e8c7SAdrian Chadd sc->numphys = 4; 578a043e8c7SAdrian Chadd sc->phy4cpu = 1; 579a043e8c7SAdrian Chadd sc->is_rgmii = 1; 580a043e8c7SAdrian Chadd sc->is_gmii = 0; 581b2152161SAdrian Chadd sc->is_mii = 0; 582a043e8c7SAdrian Chadd 583a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 584a043e8c7SAdrian Chadd "numphys", &sc->numphys); 585a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 586a043e8c7SAdrian Chadd "phy4cpu", &sc->phy4cpu); 587a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 588a043e8c7SAdrian Chadd "is_rgmii", &sc->is_rgmii); 589a043e8c7SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 590a043e8c7SAdrian Chadd "is_gmii", &sc->is_gmii); 591b2152161SAdrian Chadd (void) resource_int_value(device_get_name(dev), device_get_unit(dev), 592b2152161SAdrian Chadd "is_mii", &sc->is_mii); 593a043e8c7SAdrian Chadd 594b9f07b86SLuiz Otavio O Souza if (sc->numphys > AR8X16_NUM_PHYS) 595b9f07b86SLuiz Otavio O Souza sc->numphys = AR8X16_NUM_PHYS; 596a043e8c7SAdrian Chadd 597b9f07b86SLuiz Otavio O Souza /* Reset the switch. */ 59878549b94SAdrian Chadd if (arswitch_reset(dev)) { 5991b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6001b334c8bSAdrian Chadd "%s: arswitch_reset: failed\n", __func__); 601b9f07b86SLuiz Otavio O Souza return (ENXIO); 60278549b94SAdrian Chadd } 603a043e8c7SAdrian Chadd 604b2152161SAdrian Chadd err = sc->hal.arswitch_hw_setup(sc); 6051b334c8bSAdrian Chadd if (err != 0) { 6061b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6071b334c8bSAdrian Chadd "%s: hw_setup: err=%d\n", __func__, err); 608b2152161SAdrian Chadd return (err); 6091b334c8bSAdrian Chadd } 610b2152161SAdrian Chadd 611a043e8c7SAdrian Chadd err = sc->hal.arswitch_hw_global_setup(sc); 6121b334c8bSAdrian Chadd if (err != 0) { 6131b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6141b334c8bSAdrian Chadd "%s: hw_global_setup: err=%d\n", __func__, err); 615a043e8c7SAdrian Chadd return (err); 6161b334c8bSAdrian Chadd } 617a043e8c7SAdrian Chadd 61862042c97SAdrian Chadd /* 61962042c97SAdrian Chadd * Configure the default address table learning parameters for this 62062042c97SAdrian Chadd * switch. 62162042c97SAdrian Chadd */ 62262042c97SAdrian Chadd err = sc->hal.arswitch_atu_learn_default(sc); 62362042c97SAdrian Chadd if (err != 0) { 62462042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 62562042c97SAdrian Chadd "%s: atu_learn_default: err=%d\n", __func__, err); 62662042c97SAdrian Chadd return (err); 62762042c97SAdrian Chadd } 62862042c97SAdrian Chadd 629b9f07b86SLuiz Otavio O Souza /* Initialize the switch ports. */ 630df892897SAdrian Chadd for (port = 0; port <= sc->numphys; port++) { 631ddbc4420SAdrian Chadd sc->hal.arswitch_port_init(sc, port); 632df892897SAdrian Chadd } 633b9f07b86SLuiz Otavio O Souza 634a043e8c7SAdrian Chadd /* 635a043e8c7SAdrian Chadd * Attach the PHYs and complete the bus enumeration. 636a043e8c7SAdrian Chadd */ 637a043e8c7SAdrian Chadd err = arswitch_attach_phys(sc); 6381b334c8bSAdrian Chadd if (err != 0) { 6391b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6401b334c8bSAdrian Chadd "%s: attach_phys: err=%d\n", __func__, err); 641a043e8c7SAdrian Chadd return (err); 6421b334c8bSAdrian Chadd } 643a043e8c7SAdrian Chadd 644b9f07b86SLuiz Otavio O Souza /* Default to ingress filters off. */ 645b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, 0); 6461b334c8bSAdrian Chadd if (err != 0) { 6471b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6481b334c8bSAdrian Chadd "%s: set_vlan_mode: err=%d\n", __func__, err); 649b9f07b86SLuiz Otavio O Souza return (err); 6501b334c8bSAdrian Chadd } 651b9f07b86SLuiz Otavio O Souza 652723da5d9SJohn Baldwin bus_identify_children(dev); 653a043e8c7SAdrian Chadd bus_enumerate_hinted_children(dev); 65418250ec6SJohn Baldwin bus_attach_children(dev); 655a043e8c7SAdrian Chadd 656a043e8c7SAdrian Chadd callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0); 657454d507aSAleksandr Rybalko 658454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc); 659a043e8c7SAdrian Chadd arswitch_tick(sc); 660454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc); 661a043e8c7SAdrian Chadd 662a043e8c7SAdrian Chadd return (err); 663a043e8c7SAdrian Chadd } 664a043e8c7SAdrian Chadd 665a043e8c7SAdrian Chadd static int 666a043e8c7SAdrian Chadd arswitch_detach(device_t dev) 667a043e8c7SAdrian Chadd { 668a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 669*aa48c1aeSJohn Baldwin int error, i; 670a043e8c7SAdrian Chadd 671a043e8c7SAdrian Chadd callout_drain(&sc->callout_tick); 672a043e8c7SAdrian Chadd 673*aa48c1aeSJohn Baldwin error = bus_generic_detach(dev); 674*aa48c1aeSJohn Baldwin if (error != 0) 675*aa48c1aeSJohn Baldwin return (error); 676*aa48c1aeSJohn Baldwin 677a043e8c7SAdrian Chadd for (i=0; i < sc->numphys; i++) { 678a043e8c7SAdrian Chadd if (sc->ifp[i] != NULL) 679a043e8c7SAdrian Chadd if_free(sc->ifp[i]); 680a043e8c7SAdrian Chadd free(sc->ifname[i], M_DEVBUF); 681a043e8c7SAdrian Chadd } 682a043e8c7SAdrian Chadd 68362042c97SAdrian Chadd free(sc->atu.entries, M_DEVBUF); 68462042c97SAdrian Chadd 685a043e8c7SAdrian Chadd mtx_destroy(&sc->sc_mtx); 686a043e8c7SAdrian Chadd 687a043e8c7SAdrian Chadd return (0); 688a043e8c7SAdrian Chadd } 689a043e8c7SAdrian Chadd 690a043e8c7SAdrian Chadd /* 691a043e8c7SAdrian Chadd * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to 692a043e8c7SAdrian Chadd * port 2, etc. 693a043e8c7SAdrian Chadd */ 694a043e8c7SAdrian Chadd static inline int 695a043e8c7SAdrian Chadd arswitch_portforphy(int phy) 696a043e8c7SAdrian Chadd { 697a043e8c7SAdrian Chadd return (phy+1); 698a043e8c7SAdrian Chadd } 699a043e8c7SAdrian Chadd 700a043e8c7SAdrian Chadd static inline struct mii_data * 701a043e8c7SAdrian Chadd arswitch_miiforport(struct arswitch_softc *sc, int port) 702a043e8c7SAdrian Chadd { 703a043e8c7SAdrian Chadd int phy = port-1; 704a043e8c7SAdrian Chadd 705a043e8c7SAdrian Chadd if (phy < 0 || phy >= sc->numphys) 706a043e8c7SAdrian Chadd return (NULL); 707a043e8c7SAdrian Chadd return (device_get_softc(sc->miibus[phy])); 708a043e8c7SAdrian Chadd } 709a043e8c7SAdrian Chadd 7102e6a8c1aSJustin Hibbits static inline if_t 711a043e8c7SAdrian Chadd arswitch_ifpforport(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 (sc->ifp[phy]); 718a043e8c7SAdrian Chadd } 719a043e8c7SAdrian Chadd 720a043e8c7SAdrian Chadd /* 721a043e8c7SAdrian Chadd * Convert port status to ifmedia. 722a043e8c7SAdrian Chadd */ 723a043e8c7SAdrian Chadd static void 724a043e8c7SAdrian Chadd arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active) 725a043e8c7SAdrian Chadd { 726a043e8c7SAdrian Chadd *media_active = IFM_ETHER; 727a043e8c7SAdrian Chadd *media_status = IFM_AVALID; 728a043e8c7SAdrian Chadd 729a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0) 730a043e8c7SAdrian Chadd *media_status |= IFM_ACTIVE; 731a043e8c7SAdrian Chadd else { 732a043e8c7SAdrian Chadd *media_active |= IFM_NONE; 733a043e8c7SAdrian Chadd return; 734a043e8c7SAdrian Chadd } 735a043e8c7SAdrian Chadd switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) { 736a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_10: 737a043e8c7SAdrian Chadd *media_active |= IFM_10_T; 738a043e8c7SAdrian Chadd break; 739a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_100: 740a043e8c7SAdrian Chadd *media_active |= IFM_100_TX; 741a043e8c7SAdrian Chadd break; 742a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_1000: 743a043e8c7SAdrian Chadd *media_active |= IFM_1000_T; 744a043e8c7SAdrian Chadd break; 745a043e8c7SAdrian Chadd } 746a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0) 747a043e8c7SAdrian Chadd *media_active |= IFM_FDX; 748a043e8c7SAdrian Chadd else 749a043e8c7SAdrian Chadd *media_active |= IFM_HDX; 750a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0) 751a043e8c7SAdrian Chadd *media_active |= IFM_ETH_TXPAUSE; 752a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0) 753a043e8c7SAdrian Chadd *media_active |= IFM_ETH_RXPAUSE; 754a043e8c7SAdrian Chadd } 755a043e8c7SAdrian Chadd 756a043e8c7SAdrian Chadd /* 757a043e8c7SAdrian Chadd * Poll the status for all PHYs. We're using the switch port status because 758a043e8c7SAdrian Chadd * thats a lot quicker to read than talking to all the PHYs. Care must be 759a043e8c7SAdrian Chadd * taken that the resulting ifmedia_active is identical to what the PHY will 760a043e8c7SAdrian Chadd * compute, or gratuitous link status changes will occur whenever the PHYs 761a043e8c7SAdrian Chadd * update function is called. 762a043e8c7SAdrian Chadd */ 763a043e8c7SAdrian Chadd static void 764a043e8c7SAdrian Chadd arswitch_miipollstat(struct arswitch_softc *sc) 765a043e8c7SAdrian Chadd { 766a043e8c7SAdrian Chadd int i; 767a043e8c7SAdrian Chadd struct mii_data *mii; 768a043e8c7SAdrian Chadd struct mii_softc *miisc; 769a043e8c7SAdrian Chadd int portstatus; 7704ff2f60dSAdrian Chadd int port_flap = 0; 771a043e8c7SAdrian Chadd 772454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 773454d507aSAleksandr Rybalko 774a043e8c7SAdrian Chadd for (i = 0; i < sc->numphys; i++) { 775a043e8c7SAdrian Chadd if (sc->miibus[i] == NULL) 776a043e8c7SAdrian Chadd continue; 777a043e8c7SAdrian Chadd mii = device_get_softc(sc->miibus[i]); 778ddbc4420SAdrian Chadd /* XXX This would be nice to have abstracted out to be per-chip */ 779ddbc4420SAdrian Chadd /* AR8327/AR8337 has a different register base */ 780ddbc4420SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) 781ddbc4420SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev, 782ddbc4420SAdrian Chadd AR8327_REG_PORT_STATUS(arswitch_portforphy(i))); 783ddbc4420SAdrian Chadd else 784a043e8c7SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev, 785a043e8c7SAdrian Chadd AR8X16_REG_PORT_STS(arswitch_portforphy(i))); 7861b334c8bSAdrian Chadd #if 1 7871b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "p[%d]=0x%08x (%b)\n", 788b2152161SAdrian Chadd i, 789a043e8c7SAdrian Chadd portstatus, 7901b334c8bSAdrian Chadd portstatus, 791a043e8c7SAdrian Chadd "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7" 792a043e8c7SAdrian Chadd "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE"); 793a043e8c7SAdrian Chadd #endif 7944ff2f60dSAdrian Chadd /* 7954ff2f60dSAdrian Chadd * If the current status is down, but we have a link 7964ff2f60dSAdrian Chadd * status showing up, we need to do an ATU flush. 7974ff2f60dSAdrian Chadd */ 7984ff2f60dSAdrian Chadd if ((mii->mii_media_status & IFM_ACTIVE) == 0 && 7994ff2f60dSAdrian Chadd (portstatus & AR8X16_PORT_STS_LINK_UP) != 0) { 8004ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: port %d: port -> UP\n", 8014ff2f60dSAdrian Chadd __func__, 8024ff2f60dSAdrian Chadd i); 8034ff2f60dSAdrian Chadd port_flap = 1; 8044ff2f60dSAdrian Chadd } 8054ff2f60dSAdrian Chadd /* 8064ff2f60dSAdrian Chadd * and maybe if a port goes up->down? 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 -> DOWN\n", 8114ff2f60dSAdrian Chadd __func__, 8124ff2f60dSAdrian Chadd i); 8134ff2f60dSAdrian Chadd port_flap = 1; 8144ff2f60dSAdrian Chadd } 815a043e8c7SAdrian Chadd arswitch_update_ifmedia(portstatus, &mii->mii_media_status, 816a043e8c7SAdrian Chadd &mii->mii_media_active); 817a043e8c7SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 818a043e8c7SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 819a043e8c7SAdrian Chadd miisc->mii_inst) 820a043e8c7SAdrian Chadd continue; 821a043e8c7SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT); 822a043e8c7SAdrian Chadd } 823a043e8c7SAdrian Chadd } 8244ff2f60dSAdrian Chadd 8254ff2f60dSAdrian Chadd /* If a port went from down->up, flush the ATU */ 8264ff2f60dSAdrian Chadd if (port_flap) 8274ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush(sc); 828a043e8c7SAdrian Chadd } 829a043e8c7SAdrian Chadd 830a043e8c7SAdrian Chadd static void 831a043e8c7SAdrian Chadd arswitch_tick(void *arg) 832a043e8c7SAdrian Chadd { 833a043e8c7SAdrian Chadd struct arswitch_softc *sc = arg; 834a043e8c7SAdrian Chadd 835a043e8c7SAdrian Chadd arswitch_miipollstat(sc); 836a043e8c7SAdrian Chadd callout_reset(&sc->callout_tick, hz, arswitch_tick, sc); 837a043e8c7SAdrian Chadd } 838a043e8c7SAdrian Chadd 839454d507aSAleksandr Rybalko static void 840454d507aSAleksandr Rybalko arswitch_lock(device_t dev) 841454d507aSAleksandr Rybalko { 842454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev); 843454d507aSAleksandr Rybalko 844454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 845454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc); 846454d507aSAleksandr Rybalko } 847454d507aSAleksandr Rybalko 848454d507aSAleksandr Rybalko static void 849454d507aSAleksandr Rybalko arswitch_unlock(device_t dev) 850454d507aSAleksandr Rybalko { 851454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev); 852454d507aSAleksandr Rybalko 853454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 854454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc); 855454d507aSAleksandr Rybalko } 856454d507aSAleksandr Rybalko 857a043e8c7SAdrian Chadd static etherswitch_info_t * 858a043e8c7SAdrian Chadd arswitch_getinfo(device_t dev) 859a043e8c7SAdrian Chadd { 860a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 861a043e8c7SAdrian Chadd 862a043e8c7SAdrian Chadd return (&sc->info); 863a043e8c7SAdrian Chadd } 864a043e8c7SAdrian Chadd 865a043e8c7SAdrian Chadd static int 866a9ad4222SAdrian Chadd ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p) 867a043e8c7SAdrian Chadd { 868b9f07b86SLuiz Otavio O Souza uint32_t reg; 869b9f07b86SLuiz Otavio O Souza 870b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc); 871b9f07b86SLuiz Otavio O Souza 872b9f07b86SLuiz Otavio O Souza /* Retrieve the PVID. */ 8736dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid); 874b9f07b86SLuiz Otavio O Souza 875b9f07b86SLuiz Otavio O Souza /* Port flags. */ 876b9f07b86SLuiz Otavio O Souza reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port)); 877b9f07b86SLuiz Otavio O Souza if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG) 878b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG; 879b9f07b86SLuiz Otavio O Souza reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 880b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD) 881b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 882b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP) 883b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_STRIPTAG; 884b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 885a043e8c7SAdrian Chadd 886a9ad4222SAdrian Chadd return (0); 887a9ad4222SAdrian Chadd } 888a9ad4222SAdrian Chadd 889a9ad4222SAdrian Chadd static int 890749cac13SAdrian Chadd arswitch_is_cpuport(struct arswitch_softc *sc, int port) 891749cac13SAdrian Chadd { 892749cac13SAdrian Chadd 893749cac13SAdrian Chadd return ((port == AR8X16_PORT_CPU) || 894749cac13SAdrian Chadd ((AR8X16_IS_SWITCH(sc, AR8327) && 895749cac13SAdrian Chadd port == AR8327_PORT_GMAC6))); 896749cac13SAdrian Chadd } 897749cac13SAdrian Chadd 898749cac13SAdrian Chadd static int 899a9ad4222SAdrian Chadd arswitch_getport(device_t dev, etherswitch_port_t *p) 900a9ad4222SAdrian Chadd { 901a9ad4222SAdrian Chadd struct arswitch_softc *sc; 902a9ad4222SAdrian Chadd struct mii_data *mii; 903a9ad4222SAdrian Chadd struct ifmediareq *ifmr; 904a9ad4222SAdrian Chadd int err; 905a9ad4222SAdrian Chadd 906a9ad4222SAdrian Chadd sc = device_get_softc(dev); 907749cac13SAdrian Chadd /* XXX +1 is for AR8327; should make this configurable! */ 908749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports) 909a9ad4222SAdrian Chadd return (ENXIO); 910a9ad4222SAdrian Chadd 911a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_get(sc, p); 912a9ad4222SAdrian Chadd if (err != 0) 913a9ad4222SAdrian Chadd return (err); 914a9ad4222SAdrian Chadd 915a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port); 916749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) { 917a043e8c7SAdrian Chadd /* fill in fixed values for CPU port */ 918a9ad4222SAdrian Chadd /* XXX is this valid in all cases? */ 919f47857dcSAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 920b9f07b86SLuiz Otavio O Souza ifmr = &p->es_ifmr; 921a043e8c7SAdrian Chadd ifmr->ifm_count = 0; 922a043e8c7SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 923a043e8c7SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 924a043e8c7SAdrian Chadd ifmr->ifm_mask = 0; 925a043e8c7SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 926a043e8c7SAdrian Chadd } else if (mii != NULL) { 927a043e8c7SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 928a043e8c7SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 929a043e8c7SAdrian Chadd if (err) 930a043e8c7SAdrian Chadd return (err); 931a043e8c7SAdrian Chadd } else { 932a043e8c7SAdrian Chadd return (ENXIO); 933a043e8c7SAdrian Chadd } 934c94dc808SAdrian Chadd 935c94dc808SAdrian Chadd if (!arswitch_is_cpuport(sc, p->es_port) && 936c94dc808SAdrian Chadd AR8X16_IS_SWITCH(sc, AR8327)) { 937c94dc808SAdrian Chadd int led; 938c94dc808SAdrian Chadd p->es_nleds = 3; 939c94dc808SAdrian Chadd 940c94dc808SAdrian Chadd for (led = 0; led < p->es_nleds; led++) 941c94dc808SAdrian Chadd { 942c94dc808SAdrian Chadd int style; 943c94dc808SAdrian Chadd uint32_t val; 944c94dc808SAdrian Chadd 945c94dc808SAdrian Chadd /* Find the right style enum for our pattern */ 946c94dc808SAdrian Chadd val = arswitch_readreg(dev, 947c94dc808SAdrian Chadd ar8327_led_mapping[p->es_port-1][led].reg); 948c94dc808SAdrian Chadd val = (val>>ar8327_led_mapping[p->es_port-1][led].shift)&0x03; 949c94dc808SAdrian Chadd 950c94dc808SAdrian Chadd for (style = 0; style < ETHERSWITCH_PORT_LED_MAX; style++) 951c94dc808SAdrian Chadd { 952c94dc808SAdrian Chadd if (led_pattern_table[style] == val) break; 953c94dc808SAdrian Chadd } 954c94dc808SAdrian Chadd 955c94dc808SAdrian Chadd /* can't happen */ 956c94dc808SAdrian Chadd if (style == ETHERSWITCH_PORT_LED_MAX) 957c94dc808SAdrian Chadd style = ETHERSWITCH_PORT_LED_DEFAULT; 958c94dc808SAdrian Chadd 959c94dc808SAdrian Chadd p->es_led[led] = style; 960c94dc808SAdrian Chadd } 961c94dc808SAdrian Chadd } else 962c94dc808SAdrian Chadd { 963c94dc808SAdrian Chadd p->es_nleds = 0; 964c94dc808SAdrian Chadd } 965c94dc808SAdrian Chadd 966a043e8c7SAdrian Chadd return (0); 967a043e8c7SAdrian Chadd } 968a043e8c7SAdrian Chadd 969a043e8c7SAdrian Chadd static int 970a9ad4222SAdrian Chadd ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p) 971a043e8c7SAdrian Chadd { 972b9f07b86SLuiz Otavio O Souza uint32_t reg; 973a9ad4222SAdrian Chadd int err; 974a043e8c7SAdrian Chadd 975b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc); 976a9ad4222SAdrian Chadd 977b9f07b86SLuiz Otavio O Souza /* Set the PVID. */ 978b9f07b86SLuiz Otavio O Souza if (p->es_pvid != 0) 9796dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid); 980b9f07b86SLuiz Otavio O Souza 981b9f07b86SLuiz Otavio O Souza /* Mutually exclusive. */ 982b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG && 983b9f07b86SLuiz Otavio O Souza p->es_flags & ETHERSWITCH_PORT_STRIPTAG) { 984b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 985b9f07b86SLuiz Otavio O Souza return (EINVAL); 986b9f07b86SLuiz Otavio O Souza } 987b9f07b86SLuiz Otavio O Souza 988b9f07b86SLuiz Otavio O Souza reg = 0; 989b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG) 990b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_DOUBLE_TAG; 991b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 992b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD << 993b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 994b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 995b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP << 996b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 997b9f07b86SLuiz Otavio O Souza 998b9f07b86SLuiz Otavio O Souza err = arswitch_modifyreg(sc->sc_dev, 999b9f07b86SLuiz Otavio O Souza AR8X16_REG_PORT_CTRL(p->es_port), 1000b9f07b86SLuiz Otavio O Souza 0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT | 1001b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_DOUBLE_TAG, reg); 1002b9f07b86SLuiz Otavio O Souza 1003b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 1004a9ad4222SAdrian Chadd return (err); 1005a9ad4222SAdrian Chadd } 1006a9ad4222SAdrian Chadd 1007a9ad4222SAdrian Chadd static int 1008a9ad4222SAdrian Chadd arswitch_setport(device_t dev, etherswitch_port_t *p) 1009a9ad4222SAdrian Chadd { 1010c94dc808SAdrian Chadd int err, i; 1011a9ad4222SAdrian Chadd struct arswitch_softc *sc; 1012a9ad4222SAdrian Chadd struct ifmedia *ifm; 1013a9ad4222SAdrian Chadd struct mii_data *mii; 10142e6a8c1aSJustin Hibbits if_t ifp; 1015a9ad4222SAdrian Chadd 1016a9ad4222SAdrian Chadd sc = device_get_softc(dev); 1017749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports) 1018a9ad4222SAdrian Chadd return (ENXIO); 1019a9ad4222SAdrian Chadd 1020a9ad4222SAdrian Chadd /* Port flags. */ 1021a9ad4222SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 1022a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_setup(sc, p); 1023b9f07b86SLuiz Otavio O Souza if (err) 1024b9f07b86SLuiz Otavio O Souza return (err); 1025b9f07b86SLuiz Otavio O Souza } 1026b9f07b86SLuiz Otavio O Souza 1027c94dc808SAdrian Chadd /* Do not allow media or led changes on CPU port. */ 1028749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) 1029b9f07b86SLuiz Otavio O Souza return (0); 1030a043e8c7SAdrian Chadd 1031c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) 1032c94dc808SAdrian Chadd { 1033c94dc808SAdrian Chadd for (i = 0; i < 3; i++) 1034c94dc808SAdrian Chadd { 1035c94dc808SAdrian Chadd int err; 1036c94dc808SAdrian Chadd err = arswitch_setled(sc, p->es_port-1, i, p->es_led[i]); 1037c94dc808SAdrian Chadd if (err) 1038c94dc808SAdrian Chadd return (err); 1039c94dc808SAdrian Chadd } 1040c94dc808SAdrian Chadd } 1041c94dc808SAdrian Chadd 1042a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port); 1043a043e8c7SAdrian Chadd if (mii == NULL) 1044a043e8c7SAdrian Chadd return (ENXIO); 1045a043e8c7SAdrian Chadd 1046a043e8c7SAdrian Chadd ifp = arswitch_ifpforport(sc, p->es_port); 1047a043e8c7SAdrian Chadd 1048a043e8c7SAdrian Chadd ifm = &mii->mii_media; 1049b9f07b86SLuiz Otavio O Souza return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); 1050a043e8c7SAdrian Chadd } 1051a043e8c7SAdrian Chadd 1052c94dc808SAdrian Chadd static int 1053c94dc808SAdrian Chadd arswitch_setled(struct arswitch_softc *sc, int phy, int led, int style) 1054c94dc808SAdrian Chadd { 1055c94dc808SAdrian Chadd int shift; 10566d011946SKristof Provost int err; 1057c94dc808SAdrian Chadd 1058c94dc808SAdrian Chadd if (phy < 0 || phy > sc->numphys) 1059c94dc808SAdrian Chadd return EINVAL; 1060c94dc808SAdrian Chadd 1061c94dc808SAdrian Chadd if (style < 0 || style > ETHERSWITCH_PORT_LED_MAX) 1062c94dc808SAdrian Chadd return (EINVAL); 1063c94dc808SAdrian Chadd 10646d011946SKristof Provost ARSWITCH_LOCK(sc); 10656d011946SKristof Provost 1066c94dc808SAdrian Chadd shift = ar8327_led_mapping[phy][led].shift; 10676d011946SKristof Provost err = (arswitch_modifyreg(sc->sc_dev, 1068c94dc808SAdrian Chadd ar8327_led_mapping[phy][led].reg, 1069c94dc808SAdrian Chadd 0x03 << shift, led_pattern_table[style] << shift)); 10706d011946SKristof Provost ARSWITCH_UNLOCK(sc); 10716d011946SKristof Provost 10726d011946SKristof Provost return (err); 1073c94dc808SAdrian Chadd } 1074c94dc808SAdrian Chadd 1075a043e8c7SAdrian Chadd static void 1076a043e8c7SAdrian Chadd arswitch_statchg(device_t dev) 1077a043e8c7SAdrian Chadd { 10781b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 1079a043e8c7SAdrian Chadd 10801b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__); 1081a043e8c7SAdrian Chadd } 1082a043e8c7SAdrian Chadd 1083a043e8c7SAdrian Chadd static int 10842e6a8c1aSJustin Hibbits arswitch_ifmedia_upd(if_t ifp) 1085a043e8c7SAdrian Chadd { 10862e6a8c1aSJustin Hibbits struct arswitch_softc *sc = if_getsoftc(ifp); 10872e6a8c1aSJustin Hibbits struct mii_data *mii = arswitch_miiforport(sc, if_getdunit(ifp)); 1088a043e8c7SAdrian Chadd 1089a043e8c7SAdrian Chadd if (mii == NULL) 1090a043e8c7SAdrian Chadd return (ENXIO); 1091a043e8c7SAdrian Chadd mii_mediachg(mii); 1092a043e8c7SAdrian Chadd return (0); 1093a043e8c7SAdrian Chadd } 1094a043e8c7SAdrian Chadd 1095a043e8c7SAdrian Chadd static void 10962e6a8c1aSJustin Hibbits arswitch_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr) 1097a043e8c7SAdrian Chadd { 10982e6a8c1aSJustin Hibbits struct arswitch_softc *sc = if_getsoftc(ifp); 10992e6a8c1aSJustin Hibbits struct mii_data *mii = arswitch_miiforport(sc, if_getdunit(ifp)); 1100a043e8c7SAdrian Chadd 11011b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__); 1102a043e8c7SAdrian Chadd 1103a043e8c7SAdrian Chadd if (mii == NULL) 1104a043e8c7SAdrian Chadd return; 1105a043e8c7SAdrian Chadd mii_pollstat(mii); 1106a043e8c7SAdrian Chadd ifmr->ifm_active = mii->mii_media_active; 1107a043e8c7SAdrian Chadd ifmr->ifm_status = mii->mii_media_status; 1108a043e8c7SAdrian Chadd } 1109a043e8c7SAdrian Chadd 1110b9f07b86SLuiz Otavio O Souza static int 1111b9f07b86SLuiz Otavio O Souza arswitch_getconf(device_t dev, etherswitch_conf_t *conf) 1112b9f07b86SLuiz Otavio O Souza { 1113b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc; 11142ba4bf8fSAdrian Chadd int ret; 1115b9f07b86SLuiz Otavio O Souza 1116b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev); 1117b9f07b86SLuiz Otavio O Souza 1118b9f07b86SLuiz Otavio O Souza /* Return the VLAN mode. */ 1119b9f07b86SLuiz Otavio O Souza conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 1120b9f07b86SLuiz Otavio O Souza conf->vlan_mode = sc->vlan_mode; 1121b9f07b86SLuiz Otavio O Souza 11222ba4bf8fSAdrian Chadd /* Return the switch ethernet address. */ 11232ba4bf8fSAdrian Chadd ret = sc->hal.arswitch_hw_get_switch_macaddr(sc, 11242ba4bf8fSAdrian Chadd &conf->switch_macaddr); 11252ba4bf8fSAdrian Chadd if (ret == 0) { 11262ba4bf8fSAdrian Chadd conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR; 11272ba4bf8fSAdrian Chadd } 11282ba4bf8fSAdrian Chadd 1129b9f07b86SLuiz Otavio O Souza return (0); 1130b9f07b86SLuiz Otavio O Souza } 1131b9f07b86SLuiz Otavio O Souza 1132b9f07b86SLuiz Otavio O Souza static int 1133b9f07b86SLuiz Otavio O Souza arswitch_setconf(device_t dev, etherswitch_conf_t *conf) 1134b9f07b86SLuiz Otavio O Souza { 1135b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc; 1136b9f07b86SLuiz Otavio O Souza int err; 1137b9f07b86SLuiz Otavio O Souza 1138b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev); 1139b9f07b86SLuiz Otavio O Souza 1140b9f07b86SLuiz Otavio O Souza /* Set the VLAN mode. */ 1141b9f07b86SLuiz Otavio O Souza if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { 1142b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, conf->vlan_mode); 1143b9f07b86SLuiz Otavio O Souza if (err != 0) 1144b9f07b86SLuiz Otavio O Souza return (err); 1145b9f07b86SLuiz Otavio O Souza } 1146b9f07b86SLuiz Otavio O Souza 11472ba4bf8fSAdrian Chadd /* TODO: Set the switch ethernet address. */ 11482ba4bf8fSAdrian Chadd 1149b9f07b86SLuiz Otavio O Souza return (0); 1150b9f07b86SLuiz Otavio O Souza } 1151b9f07b86SLuiz Otavio O Souza 11526dcbabd7SAdrian Chadd static int 115362042c97SAdrian Chadd arswitch_atu_flush_all(device_t dev) 115462042c97SAdrian Chadd { 115562042c97SAdrian Chadd struct arswitch_softc *sc; 115662042c97SAdrian Chadd int err; 115762042c97SAdrian Chadd 115862042c97SAdrian Chadd sc = device_get_softc(dev); 115962042c97SAdrian Chadd ARSWITCH_LOCK(sc); 116062042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush(sc); 116162042c97SAdrian Chadd /* Invalidate cached ATU */ 116262042c97SAdrian Chadd sc->atu.count = 0; 116362042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 116462042c97SAdrian Chadd return (err); 116562042c97SAdrian Chadd } 116662042c97SAdrian Chadd 116762042c97SAdrian Chadd static int 116862042c97SAdrian Chadd arswitch_atu_flush_port(device_t dev, int port) 116962042c97SAdrian Chadd { 117062042c97SAdrian Chadd struct arswitch_softc *sc; 117162042c97SAdrian Chadd int err; 117262042c97SAdrian Chadd 117362042c97SAdrian Chadd sc = device_get_softc(dev); 117462042c97SAdrian Chadd ARSWITCH_LOCK(sc); 117562042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush_port(sc, port); 117662042c97SAdrian Chadd /* Invalidate cached ATU */ 117762042c97SAdrian Chadd sc->atu.count = 0; 117862042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 117962042c97SAdrian Chadd return (err); 118062042c97SAdrian Chadd } 118162042c97SAdrian Chadd 118262042c97SAdrian Chadd static int 118362042c97SAdrian Chadd arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table) 118462042c97SAdrian Chadd { 118562042c97SAdrian Chadd struct arswitch_softc *sc; 118662042c97SAdrian Chadd int err, nitems; 118762042c97SAdrian Chadd 118862042c97SAdrian Chadd sc = device_get_softc(dev); 118962042c97SAdrian Chadd 119062042c97SAdrian Chadd ARSWITCH_LOCK(sc); 119162042c97SAdrian Chadd /* Initial setup */ 119262042c97SAdrian Chadd nitems = 0; 119362042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0); 119462042c97SAdrian Chadd 119562042c97SAdrian Chadd /* fetch - ideally yes we'd fetch into a separate table then switch */ 11967ed08319SAdrian Chadd while (err == 0 && nitems < sc->atu.size) { 119762042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, 119862042c97SAdrian Chadd &sc->atu.entries[nitems], 1); 119962042c97SAdrian Chadd if (err == 0) { 120062042c97SAdrian Chadd sc->atu.entries[nitems].id = nitems; 120162042c97SAdrian Chadd nitems++; 120262042c97SAdrian Chadd } 120362042c97SAdrian Chadd } 120462042c97SAdrian Chadd sc->atu.count = nitems; 120562042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 120662042c97SAdrian Chadd 120762042c97SAdrian Chadd table->es_nitems = nitems; 120862042c97SAdrian Chadd 120962042c97SAdrian Chadd return (0); 121062042c97SAdrian Chadd } 121162042c97SAdrian Chadd 121262042c97SAdrian Chadd static int 121362042c97SAdrian Chadd arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e) 121462042c97SAdrian Chadd { 121562042c97SAdrian Chadd struct arswitch_softc *sc; 121662042c97SAdrian Chadd int id; 121762042c97SAdrian Chadd 121862042c97SAdrian Chadd sc = device_get_softc(dev); 121962042c97SAdrian Chadd id = e->id; 122062042c97SAdrian Chadd 122162042c97SAdrian Chadd ARSWITCH_LOCK(sc); 122262042c97SAdrian Chadd if (id > sc->atu.count) { 122362042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 122462042c97SAdrian Chadd return (ENOENT); 122562042c97SAdrian Chadd } 122662042c97SAdrian Chadd 122762042c97SAdrian Chadd memcpy(e, &sc->atu.entries[id], sizeof(*e)); 122862042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 122962042c97SAdrian Chadd return (0); 123062042c97SAdrian Chadd } 123162042c97SAdrian Chadd 123262042c97SAdrian Chadd static int 12336dcbabd7SAdrian Chadd arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e) 12346dcbabd7SAdrian Chadd { 12356dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 12366dcbabd7SAdrian Chadd 12376dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_getvgroup(sc, e)); 12386dcbabd7SAdrian Chadd } 12396dcbabd7SAdrian Chadd 12406dcbabd7SAdrian Chadd static int 12416dcbabd7SAdrian Chadd arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e) 12426dcbabd7SAdrian Chadd { 12436dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 12446dcbabd7SAdrian Chadd 12456dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_setvgroup(sc, e)); 12466dcbabd7SAdrian Chadd } 12476dcbabd7SAdrian Chadd 124878549b94SAdrian Chadd static int 124978549b94SAdrian Chadd arswitch_readphy(device_t dev, int phy, int reg) 125078549b94SAdrian Chadd { 125178549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 125278549b94SAdrian Chadd 125378549b94SAdrian Chadd return (sc->hal.arswitch_phy_read(dev, phy, reg)); 125478549b94SAdrian Chadd } 125578549b94SAdrian Chadd 125678549b94SAdrian Chadd static int 125778549b94SAdrian Chadd arswitch_writephy(device_t dev, int phy, int reg, int val) 125878549b94SAdrian Chadd { 125978549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 126078549b94SAdrian Chadd 126178549b94SAdrian Chadd return (sc->hal.arswitch_phy_write(dev, phy, reg, val)); 126278549b94SAdrian Chadd } 126378549b94SAdrian Chadd 1264a043e8c7SAdrian Chadd static device_method_t arswitch_methods[] = { 1265a043e8c7SAdrian Chadd /* Device interface */ 1266a043e8c7SAdrian Chadd DEVMETHOD(device_probe, arswitch_probe), 1267a043e8c7SAdrian Chadd DEVMETHOD(device_attach, arswitch_attach), 1268a043e8c7SAdrian Chadd DEVMETHOD(device_detach, arswitch_detach), 1269a043e8c7SAdrian Chadd 1270a043e8c7SAdrian Chadd /* bus interface */ 1271a043e8c7SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 1272a043e8c7SAdrian Chadd 1273a043e8c7SAdrian Chadd /* MII interface */ 1274a043e8c7SAdrian Chadd DEVMETHOD(miibus_readreg, arswitch_readphy), 1275a043e8c7SAdrian Chadd DEVMETHOD(miibus_writereg, arswitch_writephy), 1276a043e8c7SAdrian Chadd DEVMETHOD(miibus_statchg, arswitch_statchg), 1277a043e8c7SAdrian Chadd 1278a043e8c7SAdrian Chadd /* MDIO interface */ 1279a043e8c7SAdrian Chadd DEVMETHOD(mdio_readreg, arswitch_readphy), 1280a043e8c7SAdrian Chadd DEVMETHOD(mdio_writereg, arswitch_writephy), 1281a043e8c7SAdrian Chadd 1282a043e8c7SAdrian Chadd /* etherswitch interface */ 1283454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_lock, arswitch_lock), 1284454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_unlock, arswitch_unlock), 1285a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getinfo, arswitch_getinfo), 1286a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readreg, arswitch_readreg), 1287a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writereg, arswitch_writereg), 1288a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, arswitch_readphy), 1289a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, arswitch_writephy), 1290a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getport, arswitch_getport), 1291a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setport, arswitch_setport), 1292a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, arswitch_getvgroup), 1293a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, arswitch_setvgroup), 1294b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_getconf, arswitch_getconf), 1295b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_setconf, arswitch_setconf), 129662042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all), 129762042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port), 129862042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table), 129962042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry), 1300a043e8c7SAdrian Chadd 1301a043e8c7SAdrian Chadd DEVMETHOD_END 1302a043e8c7SAdrian Chadd }; 1303a043e8c7SAdrian Chadd 1304a043e8c7SAdrian Chadd DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods, 1305a043e8c7SAdrian Chadd sizeof(struct arswitch_softc)); 1306a043e8c7SAdrian Chadd 130742726c2fSJohn Baldwin DRIVER_MODULE(arswitch, mdio, arswitch_driver, 0, 0); 13083e38757dSJohn Baldwin DRIVER_MODULE(miibus, arswitch, miibus_driver, 0, 0); 13098933f7d6SJohn Baldwin DRIVER_MODULE(mdio, arswitch, mdio_driver, 0, 0); 1310829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, 0, 0); 1311a043e8c7SAdrian Chadd MODULE_VERSION(arswitch, 1); 1312a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */ 1313a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */ 1314