1a043e8c7SAdrian Chadd /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4a043e8c7SAdrian Chadd * Copyright (c) 2011-2012 Stefan Bethke. 5a043e8c7SAdrian Chadd * Copyright (c) 2012 Adrian Chadd. 6a043e8c7SAdrian Chadd * All rights reserved. 7a043e8c7SAdrian Chadd * 8a043e8c7SAdrian Chadd * Redistribution and use in source and binary forms, with or without 9a043e8c7SAdrian Chadd * modification, are permitted provided that the following conditions 10a043e8c7SAdrian Chadd * are met: 11a043e8c7SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 12a043e8c7SAdrian Chadd * notice, this list of conditions and the following disclaimer. 13a043e8c7SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 14a043e8c7SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 15a043e8c7SAdrian Chadd * documentation and/or other materials provided with the distribution. 16a043e8c7SAdrian Chadd * 17a043e8c7SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18a043e8c7SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19a043e8c7SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20a043e8c7SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21a043e8c7SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22a043e8c7SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23a043e8c7SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24a043e8c7SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25a043e8c7SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26a043e8c7SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27a043e8c7SAdrian Chadd * SUCH DAMAGE. 28a043e8c7SAdrian Chadd * 29a043e8c7SAdrian Chadd * $FreeBSD$ 30a043e8c7SAdrian Chadd */ 31a043e8c7SAdrian Chadd 32a043e8c7SAdrian Chadd #include <sys/param.h> 33a043e8c7SAdrian Chadd #include <sys/bus.h> 34a043e8c7SAdrian Chadd #include <sys/errno.h> 35a043e8c7SAdrian Chadd #include <sys/kernel.h> 36104dc214SGleb Smirnoff #include <sys/malloc.h> 37a043e8c7SAdrian Chadd #include <sys/module.h> 38a043e8c7SAdrian Chadd #include <sys/socket.h> 39a043e8c7SAdrian Chadd #include <sys/sockio.h> 40a043e8c7SAdrian Chadd #include <sys/sysctl.h> 41a043e8c7SAdrian Chadd #include <sys/systm.h> 42a043e8c7SAdrian Chadd 43a043e8c7SAdrian Chadd #include <net/if.h> 44104dc214SGleb Smirnoff #include <net/if_var.h> 45a043e8c7SAdrian Chadd #include <net/if_arp.h> 46a043e8c7SAdrian Chadd #include <net/ethernet.h> 47a043e8c7SAdrian Chadd #include <net/if_dl.h> 48a043e8c7SAdrian Chadd #include <net/if_media.h> 49a043e8c7SAdrian Chadd #include <net/if_types.h> 50a043e8c7SAdrian Chadd 51a043e8c7SAdrian Chadd #include <machine/bus.h> 52efce3748SRui Paulo #include <dev/iicbus/iic.h> 53a043e8c7SAdrian Chadd #include <dev/iicbus/iiconf.h> 54a043e8c7SAdrian Chadd #include <dev/iicbus/iicbus.h> 55a043e8c7SAdrian Chadd #include <dev/mii/mii.h> 56a043e8c7SAdrian Chadd #include <dev/mii/miivar.h> 5771e8eac4SAdrian Chadd #include <dev/mdio/mdio.h> 58a043e8c7SAdrian Chadd 59a043e8c7SAdrian Chadd #include <dev/etherswitch/etherswitch.h> 60a043e8c7SAdrian Chadd 61a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchreg.h> 62a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitchvar.h> 63a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_reg.h> 64a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_phy.h> 65b9f07b86SLuiz Otavio O Souza #include <dev/etherswitch/arswitch/arswitch_vlans.h> 66a043e8c7SAdrian Chadd 6727a2ecaaSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_7240.h> 68a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8216.h> 69a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8226.h> 70a043e8c7SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8316.h> 71482d268dSAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_8327.h> 72b2152161SAdrian Chadd #include <dev/etherswitch/arswitch/arswitch_9340.h> 73a043e8c7SAdrian Chadd 74a043e8c7SAdrian Chadd #include "mdio_if.h" 75a043e8c7SAdrian Chadd #include "miibus_if.h" 76a043e8c7SAdrian Chadd #include "etherswitch_if.h" 77a043e8c7SAdrian Chadd 78c94dc808SAdrian Chadd /* Map ETHERSWITCH_PORT_LED_* to Atheros pattern codes */ 79c94dc808SAdrian Chadd static int led_pattern_table[] = { 80c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_DEFAULT] = 0x3, 81c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_ON] = 0x2, 82c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_OFF] = 0x0, 83c94dc808SAdrian Chadd [ETHERSWITCH_PORT_LED_BLINK] = 0x1 84c94dc808SAdrian Chadd }; 85c94dc808SAdrian Chadd 86a043e8c7SAdrian Chadd static inline int arswitch_portforphy(int phy); 87a043e8c7SAdrian Chadd static void arswitch_tick(void *arg); 88a043e8c7SAdrian Chadd static int arswitch_ifmedia_upd(struct ifnet *); 89a043e8c7SAdrian Chadd static void arswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *); 90a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_setup(struct arswitch_softc *sc, 91a9ad4222SAdrian Chadd etherswitch_port_t *p); 92a9ad4222SAdrian Chadd static int ar8xxx_port_vlan_get(struct arswitch_softc *sc, 93a9ad4222SAdrian Chadd etherswitch_port_t *p); 94c94dc808SAdrian Chadd static int arswitch_setled(struct arswitch_softc *sc, int phy, int led, 95c94dc808SAdrian Chadd int style); 96a043e8c7SAdrian Chadd 97a043e8c7SAdrian Chadd static int 98a043e8c7SAdrian Chadd arswitch_probe(device_t dev) 99a043e8c7SAdrian Chadd { 100a043e8c7SAdrian Chadd struct arswitch_softc *sc; 101a043e8c7SAdrian Chadd uint32_t id; 102a043e8c7SAdrian Chadd char *chipname, desc[256]; 103a043e8c7SAdrian Chadd 104a043e8c7SAdrian Chadd sc = device_get_softc(dev); 105a043e8c7SAdrian Chadd bzero(sc, sizeof(*sc)); 106a043e8c7SAdrian Chadd sc->page = -1; 10727a2ecaaSAdrian Chadd 10827a2ecaaSAdrian Chadd /* AR7240 probe */ 10927a2ecaaSAdrian Chadd if (ar7240_probe(dev) == 0) { 11027a2ecaaSAdrian Chadd chipname = "AR7240"; 11127a2ecaaSAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR7240; 112b2152161SAdrian Chadd sc->is_internal_switch = 1; 113b2152161SAdrian Chadd id = 0; 114b2152161SAdrian Chadd goto done; 115b2152161SAdrian Chadd } 116b2152161SAdrian Chadd 117b2152161SAdrian Chadd /* AR9340 probe */ 118b2152161SAdrian Chadd if (ar9340_probe(dev) == 0) { 119b2152161SAdrian Chadd chipname = "AR9340"; 120b2152161SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR9340; 121b2152161SAdrian Chadd sc->is_internal_switch = 1; 122daa4deacSAleksandr Rybalko id = 0; 12327a2ecaaSAdrian Chadd goto done; 12427a2ecaaSAdrian Chadd } 12527a2ecaaSAdrian Chadd 12627a2ecaaSAdrian Chadd /* AR8xxx probe */ 127a043e8c7SAdrian Chadd id = arswitch_readreg(dev, AR8X16_REG_MASK_CTRL); 128dd843f87SAdrian Chadd sc->chip_rev = (id & AR8X16_MASK_CTRL_REV_MASK); 129dd843f87SAdrian Chadd sc->chip_ver = (id & AR8X16_MASK_CTRL_VER_MASK) > AR8X16_MASK_CTRL_VER_SHIFT; 130b2152161SAdrian Chadd switch (id & (AR8X16_MASK_CTRL_VER_MASK | AR8X16_MASK_CTRL_REV_MASK)) { 131b2152161SAdrian Chadd case 0x0101: 132a043e8c7SAdrian Chadd chipname = "AR8216"; 133a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8216; 134a043e8c7SAdrian Chadd break; 135b2152161SAdrian Chadd case 0x0201: 136a043e8c7SAdrian Chadd chipname = "AR8226"; 137a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8226; 138a043e8c7SAdrian Chadd break; 139b2152161SAdrian Chadd /* 0x0301 - AR8236 */ 140b2152161SAdrian Chadd case 0x1000: 141b2152161SAdrian Chadd case 0x1001: 142a043e8c7SAdrian Chadd chipname = "AR8316"; 143a043e8c7SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8316; 144a043e8c7SAdrian Chadd break; 1450e67bf94SAdrian Chadd case 0x1202: 1469682e347SAdrian Chadd case 0x1204: 1470e67bf94SAdrian Chadd chipname = "AR8327"; 1480e67bf94SAdrian Chadd sc->sc_switchtype = AR8X16_SWITCH_AR8327; 1490e67bf94SAdrian Chadd sc->mii_lo_first = 1; 1500e67bf94SAdrian Chadd break; 151a043e8c7SAdrian Chadd default: 152a043e8c7SAdrian Chadd chipname = NULL; 153a043e8c7SAdrian Chadd } 15427a2ecaaSAdrian Chadd 15527a2ecaaSAdrian Chadd done: 156b2152161SAdrian Chadd 1571b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, "chipname=%s, id=%08x\n", chipname, id); 158a043e8c7SAdrian Chadd if (chipname != NULL) { 159a043e8c7SAdrian Chadd snprintf(desc, sizeof(desc), 16078549b94SAdrian Chadd "Atheros %s Ethernet Switch (ver %d rev %d)", 16178549b94SAdrian Chadd chipname, 16278549b94SAdrian Chadd sc->chip_ver, 16378549b94SAdrian Chadd sc->chip_rev); 164a043e8c7SAdrian Chadd device_set_desc_copy(dev, desc); 165a043e8c7SAdrian Chadd return (BUS_PROBE_DEFAULT); 166a043e8c7SAdrian Chadd } 167a043e8c7SAdrian Chadd return (ENXIO); 168a043e8c7SAdrian Chadd } 169a043e8c7SAdrian Chadd 170a043e8c7SAdrian Chadd static int 171a043e8c7SAdrian Chadd arswitch_attach_phys(struct arswitch_softc *sc) 172a043e8c7SAdrian Chadd { 173a043e8c7SAdrian Chadd int phy, err = 0; 174a043e8c7SAdrian Chadd char name[IFNAMSIZ]; 175a043e8c7SAdrian Chadd 176a043e8c7SAdrian Chadd /* PHYs need an interface, so we generate a dummy one */ 177a043e8c7SAdrian Chadd snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev)); 178a043e8c7SAdrian Chadd for (phy = 0; phy < sc->numphys; phy++) { 179a043e8c7SAdrian Chadd sc->ifp[phy] = if_alloc(IFT_ETHER); 1800774131eSMichael Zhilin if (sc->ifp[phy] == NULL) { 1810774131eSMichael Zhilin device_printf(sc->sc_dev, "couldn't allocate ifnet structure\n"); 1820774131eSMichael Zhilin err = ENOMEM; 1830774131eSMichael Zhilin break; 1840774131eSMichael Zhilin } 1850774131eSMichael Zhilin 186a043e8c7SAdrian Chadd sc->ifp[phy]->if_softc = sc; 187a043e8c7SAdrian Chadd sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST | 188a043e8c7SAdrian Chadd IFF_DRV_RUNNING | IFF_SIMPLEX; 189a043e8c7SAdrian Chadd sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK); 190a043e8c7SAdrian Chadd bcopy(name, sc->ifname[phy], strlen(name)+1); 191a043e8c7SAdrian Chadd if_initname(sc->ifp[phy], sc->ifname[phy], 192a043e8c7SAdrian Chadd arswitch_portforphy(phy)); 193a043e8c7SAdrian Chadd err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy], 194a043e8c7SAdrian Chadd arswitch_ifmedia_upd, arswitch_ifmedia_sts, \ 195a043e8c7SAdrian Chadd BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 19678549b94SAdrian Chadd #if 0 197a043e8c7SAdrian Chadd DPRINTF(sc->sc_dev, "%s attached to pseudo interface %s\n", 198a043e8c7SAdrian Chadd device_get_nameunit(sc->miibus[phy]), 199a043e8c7SAdrian Chadd sc->ifp[phy]->if_xname); 20078549b94SAdrian Chadd #endif 201a043e8c7SAdrian Chadd if (err != 0) { 202a043e8c7SAdrian Chadd device_printf(sc->sc_dev, 203a043e8c7SAdrian Chadd "attaching PHY %d failed\n", 204a043e8c7SAdrian Chadd phy); 205a043e8c7SAdrian Chadd return (err); 206a043e8c7SAdrian Chadd } 207a043e8c7SAdrian Chadd 208c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) { 209c94dc808SAdrian Chadd int led; 210c94dc808SAdrian Chadd char ledname[IFNAMSIZ+4]; 211c94dc808SAdrian Chadd 212c94dc808SAdrian Chadd for (led = 0; led < 3; led++) { 213c94dc808SAdrian Chadd sprintf(ledname, "%s%dled%d", name, 214c94dc808SAdrian Chadd arswitch_portforphy(phy), led+1); 215c94dc808SAdrian Chadd sc->dev_led[phy][led].sc = sc; 216c94dc808SAdrian Chadd sc->dev_led[phy][led].phy = phy; 217c94dc808SAdrian Chadd sc->dev_led[phy][led].lednum = led; 218c94dc808SAdrian Chadd } 219c94dc808SAdrian Chadd } 220c94dc808SAdrian Chadd } 221c94dc808SAdrian Chadd return (0); 222c94dc808SAdrian Chadd } 223c94dc808SAdrian Chadd 224a043e8c7SAdrian Chadd static int 225b9f07b86SLuiz Otavio O Souza arswitch_reset(device_t dev) 226b9f07b86SLuiz Otavio O Souza { 227b9f07b86SLuiz Otavio O Souza 228b9f07b86SLuiz Otavio O Souza arswitch_writereg(dev, AR8X16_REG_MASK_CTRL, 229b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET); 230b9f07b86SLuiz Otavio O Souza DELAY(1000); 231b9f07b86SLuiz Otavio O Souza if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) & 232b9f07b86SLuiz Otavio O Souza AR8X16_MASK_CTRL_SOFT_RESET) { 233b9f07b86SLuiz Otavio O Souza device_printf(dev, "unable to reset switch\n"); 234b9f07b86SLuiz Otavio O Souza return (-1); 235b9f07b86SLuiz Otavio O Souza } 236b9f07b86SLuiz Otavio O Souza return (0); 237b9f07b86SLuiz Otavio O Souza } 238b9f07b86SLuiz Otavio O Souza 239b9f07b86SLuiz Otavio O Souza static int 240b9f07b86SLuiz Otavio O Souza arswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode) 241b9f07b86SLuiz Otavio O Souza { 242b9f07b86SLuiz Otavio O Souza 243b9f07b86SLuiz Otavio O Souza /* Check for invalid modes. */ 244b9f07b86SLuiz Otavio O Souza if ((mode & sc->info.es_vlan_caps) != mode) 245b9f07b86SLuiz Otavio O Souza return (EINVAL); 246b9f07b86SLuiz Otavio O Souza 247b9f07b86SLuiz Otavio O Souza switch (mode) { 248b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_DOT1Q: 249b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 250b9f07b86SLuiz Otavio O Souza break; 251b9f07b86SLuiz Otavio O Souza case ETHERSWITCH_VLAN_PORT: 252b9f07b86SLuiz Otavio O Souza sc->vlan_mode = ETHERSWITCH_VLAN_PORT; 253b9f07b86SLuiz Otavio O Souza break; 254b9f07b86SLuiz Otavio O Souza default: 255b9f07b86SLuiz Otavio O Souza sc->vlan_mode = 0; 25674b8d63dSPedro F. Giffuni } 257b9f07b86SLuiz Otavio O Souza 258b9f07b86SLuiz Otavio O Souza /* Reset VLANs. */ 2596dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw(sc); 260b9f07b86SLuiz Otavio O Souza 261b9f07b86SLuiz Otavio O Souza return (0); 262b9f07b86SLuiz Otavio O Souza } 263b9f07b86SLuiz Otavio O Souza 264b9f07b86SLuiz Otavio O Souza static void 265a9ad4222SAdrian Chadd ar8xxx_port_init(struct arswitch_softc *sc, int port) 266b9f07b86SLuiz Otavio O Souza { 267b9f07b86SLuiz Otavio O Souza 268b9f07b86SLuiz Otavio O Souza /* Port0 - CPU */ 269df892897SAdrian Chadd if (port == AR8X16_PORT_CPU) { 270b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(0), 271b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 272b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_SPEED_100 : AR8X16_PORT_STS_SPEED_1000) | 273b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_RXFLOW) | 274b9f07b86SLuiz Otavio O Souza (AR8X16_IS_SWITCH(sc, AR8216) ? 0 : AR8X16_PORT_STS_TXFLOW) | 275b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_RXMAC | 276b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_TXMAC | 277b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_DUPLEX); 278b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0), 279b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(0)) & 280b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER); 281df892897SAdrian Chadd } else { 282b9f07b86SLuiz Otavio O Souza /* Set ports to auto negotiation. */ 283b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_STS(port), 284b9f07b86SLuiz Otavio O Souza AR8X16_PORT_STS_LINK_AUTO); 285b9f07b86SLuiz Otavio O Souza arswitch_writereg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port), 286b9f07b86SLuiz Otavio O Souza arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(port)) & 287b9f07b86SLuiz Otavio O Souza ~AR8X16_PORT_CTRL_HEADER); 288b9f07b86SLuiz Otavio O Souza } 289b9f07b86SLuiz Otavio O Souza } 290b9f07b86SLuiz Otavio O Souza 291b9f07b86SLuiz Otavio O Souza static int 29262042c97SAdrian Chadd ar8xxx_atu_wait_ready(struct arswitch_softc *sc) 2934ff2f60dSAdrian Chadd { 2944ff2f60dSAdrian Chadd int ret; 2954ff2f60dSAdrian Chadd 29662042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 29762042c97SAdrian Chadd 2984ff2f60dSAdrian Chadd ret = arswitch_waitreg(sc->sc_dev, 2994ff2f60dSAdrian Chadd AR8216_REG_ATU, 3004ff2f60dSAdrian Chadd AR8216_ATU_ACTIVE, 3014ff2f60dSAdrian Chadd 0, 3024ff2f60dSAdrian Chadd 1000); 3034ff2f60dSAdrian Chadd 30462042c97SAdrian Chadd return (ret); 30562042c97SAdrian Chadd } 30662042c97SAdrian Chadd 30762042c97SAdrian Chadd /* 30862042c97SAdrian Chadd * Flush all ATU entries. 30962042c97SAdrian Chadd */ 31062042c97SAdrian Chadd static int 31162042c97SAdrian Chadd ar8xxx_atu_flush(struct arswitch_softc *sc) 31262042c97SAdrian Chadd { 31362042c97SAdrian Chadd int ret; 31462042c97SAdrian Chadd 31562042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 31662042c97SAdrian Chadd 31762042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing all ports\n", __func__); 31862042c97SAdrian Chadd 31962042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc); 3204ff2f60dSAdrian Chadd if (ret) 3214ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 3224ff2f60dSAdrian Chadd 3234ff2f60dSAdrian Chadd if (!ret) 3244ff2f60dSAdrian Chadd arswitch_writereg(sc->sc_dev, 3254ff2f60dSAdrian Chadd AR8216_REG_ATU, 3262c6ceccaSAdrian Chadd AR8216_ATU_OP_FLUSH | AR8216_ATU_ACTIVE); 3274ff2f60dSAdrian Chadd 3284ff2f60dSAdrian Chadd return (ret); 3294ff2f60dSAdrian Chadd } 3304ff2f60dSAdrian Chadd 33162042c97SAdrian Chadd /* 33262042c97SAdrian Chadd * Flush ATU entries for a single port. 33362042c97SAdrian Chadd */ 33462042c97SAdrian Chadd static int 33562042c97SAdrian Chadd ar8xxx_atu_flush_port(struct arswitch_softc *sc, int port) 33662042c97SAdrian Chadd { 33762042c97SAdrian Chadd int ret, val; 33862042c97SAdrian Chadd 33962042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: flushing port %d\n", __func__, 34062042c97SAdrian Chadd port); 34162042c97SAdrian Chadd 34262042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 34362042c97SAdrian Chadd 34462042c97SAdrian Chadd /* Flush unicast entries on port */ 34562042c97SAdrian Chadd val = AR8216_ATU_OP_FLUSH_UNICAST; 34662042c97SAdrian Chadd 34762042c97SAdrian Chadd /* TODO: bit 4 indicates whether to flush dynamic (0) or static (1) */ 34862042c97SAdrian Chadd 34962042c97SAdrian Chadd /* Which port */ 35062042c97SAdrian Chadd val |= SM(port, AR8216_ATU_PORT_NUM); 35162042c97SAdrian Chadd 35262042c97SAdrian Chadd ret = ar8xxx_atu_wait_ready(sc); 35362042c97SAdrian Chadd if (ret) 35462042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: waitreg failed\n", __func__); 35562042c97SAdrian Chadd 35662042c97SAdrian Chadd if (!ret) 35762042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 35862042c97SAdrian Chadd AR8216_REG_ATU, 35962042c97SAdrian Chadd val | AR8216_ATU_ACTIVE); 36062042c97SAdrian Chadd 36162042c97SAdrian Chadd return (ret); 36262042c97SAdrian Chadd } 36362042c97SAdrian Chadd 36462042c97SAdrian Chadd /* 36562042c97SAdrian Chadd * XXX TODO: flush a single MAC address. 36662042c97SAdrian Chadd */ 36762042c97SAdrian Chadd 36862042c97SAdrian Chadd /* 36962042c97SAdrian Chadd * Fetch a single entry from the ATU. 37062042c97SAdrian Chadd */ 37162042c97SAdrian Chadd static int 37262042c97SAdrian Chadd ar8xxx_atu_fetch_table(struct arswitch_softc *sc, etherswitch_atu_entry_t *e, 37362042c97SAdrian Chadd int atu_fetch_op) 37462042c97SAdrian Chadd { 37562042c97SAdrian Chadd uint32_t ret0, ret1, ret2, val; 37662042c97SAdrian Chadd 37762042c97SAdrian Chadd ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 37862042c97SAdrian Chadd 37962042c97SAdrian Chadd switch (atu_fetch_op) { 38062042c97SAdrian Chadd case 0: 38162042c97SAdrian Chadd /* Initialise things for the first fetch */ 38262042c97SAdrian Chadd 38362042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: initializing\n", __func__); 38462042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 38562042c97SAdrian Chadd 38662042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 38762042c97SAdrian Chadd AR8216_REG_ATU, AR8216_ATU_OP_GET_NEXT); 38862042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 38962042c97SAdrian Chadd AR8216_REG_ATU_DATA, 0); 39062042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, 39162042c97SAdrian Chadd AR8216_REG_ATU_CTRL2, 0); 39262042c97SAdrian Chadd 39362042c97SAdrian Chadd return (0); 39462042c97SAdrian Chadd case 1: 39562042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: reading next\n", __func__); 39662042c97SAdrian Chadd /* 39762042c97SAdrian Chadd * Attempt to read the next address entry; don't modify what 39862042c97SAdrian Chadd * is there in AT_ADDR{4,5} as its used for the next fetch 39962042c97SAdrian Chadd */ 40062042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 40162042c97SAdrian Chadd 40262042c97SAdrian Chadd /* Begin the next read event; not modifying anything */ 40362042c97SAdrian Chadd val = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU); 40462042c97SAdrian Chadd val |= AR8216_ATU_ACTIVE; 40562042c97SAdrian Chadd arswitch_writereg(sc->sc_dev, AR8216_REG_ATU, val); 40662042c97SAdrian Chadd 40762042c97SAdrian Chadd /* Wait for it to complete */ 40862042c97SAdrian Chadd (void) ar8xxx_atu_wait_ready(sc); 40962042c97SAdrian Chadd 41062042c97SAdrian Chadd /* Fetch the ethernet address and ATU status */ 41162042c97SAdrian Chadd ret0 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU); 41262042c97SAdrian Chadd ret1 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_DATA); 41362042c97SAdrian Chadd ret2 = arswitch_readreg(sc->sc_dev, AR8216_REG_ATU_CTRL2); 41462042c97SAdrian Chadd 41562042c97SAdrian Chadd /* If the status is zero, then we're done */ 41662042c97SAdrian Chadd if (MS(ret2, AR8216_ATU_CTRL2_AT_STATUS) == 0) 41762042c97SAdrian Chadd return (-1); 41862042c97SAdrian Chadd 41962042c97SAdrian Chadd /* MAC address */ 42062042c97SAdrian Chadd e->es_macaddr[5] = MS(ret0, AR8216_ATU_ADDR5); 42162042c97SAdrian Chadd e->es_macaddr[4] = MS(ret0, AR8216_ATU_ADDR4); 42262042c97SAdrian Chadd e->es_macaddr[3] = MS(ret1, AR8216_ATU_ADDR3); 42362042c97SAdrian Chadd e->es_macaddr[2] = MS(ret1, AR8216_ATU_ADDR2); 42462042c97SAdrian Chadd e->es_macaddr[1] = MS(ret1, AR8216_ATU_ADDR1); 42562042c97SAdrian Chadd e->es_macaddr[0] = MS(ret1, AR8216_ATU_ADDR0); 42662042c97SAdrian Chadd 42762042c97SAdrian Chadd /* Bitmask of ports this entry is for */ 42862042c97SAdrian Chadd e->es_portmask = MS(ret2, AR8216_ATU_CTRL2_DESPORT); 42962042c97SAdrian Chadd 43062042c97SAdrian Chadd /* TODO: other flags that are interesting */ 43162042c97SAdrian Chadd 43262042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: MAC %6D portmask 0x%08x\n", 43362042c97SAdrian Chadd __func__, 43462042c97SAdrian Chadd e->es_macaddr, ":", e->es_portmask); 43562042c97SAdrian Chadd return (0); 43662042c97SAdrian Chadd default: 43762042c97SAdrian Chadd return (-1); 43862042c97SAdrian Chadd } 43962042c97SAdrian Chadd return (-1); 44062042c97SAdrian Chadd } 44162042c97SAdrian Chadd 44262042c97SAdrian Chadd /* 44362042c97SAdrian Chadd * Configure aging register defaults. 44462042c97SAdrian Chadd */ 44562042c97SAdrian Chadd static int 44662042c97SAdrian Chadd ar8xxx_atu_learn_default(struct arswitch_softc *sc) 44762042c97SAdrian Chadd { 44862042c97SAdrian Chadd int ret; 44962042c97SAdrian Chadd uint32_t val; 45062042c97SAdrian Chadd 45162042c97SAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ATU, "%s: resetting learning\n", __func__); 45262042c97SAdrian Chadd 45362042c97SAdrian Chadd /* 45462042c97SAdrian Chadd * For now, configure the aging defaults: 45562042c97SAdrian Chadd * 45662042c97SAdrian Chadd * + ARP_EN - enable "acknowledgement" of ARP frames - they are 45762042c97SAdrian Chadd * forwarded to the CPU port 45862042c97SAdrian Chadd * + LEARN_CHANGE_EN - hash table violations when learning MAC addresses 45962042c97SAdrian Chadd * will force an entry to be expired/updated and a new one to be 46062042c97SAdrian Chadd * programmed in. 46162042c97SAdrian Chadd * + AGE_EN - enable address table aging 46262042c97SAdrian Chadd * + AGE_TIME - set to 5 minutes 46362042c97SAdrian Chadd */ 46462042c97SAdrian Chadd val = 0; 46562042c97SAdrian Chadd val |= AR8216_ATU_CTRL_ARP_EN; 46662042c97SAdrian Chadd val |= AR8216_ATU_CTRL_LEARN_CHANGE; 46762042c97SAdrian Chadd val |= AR8216_ATU_CTRL_AGE_EN; 46862042c97SAdrian Chadd val |= 0x2b; /* 5 minutes; bits 15:0 */ 46962042c97SAdrian Chadd 47062042c97SAdrian Chadd ret = arswitch_writereg(sc->sc_dev, 47162042c97SAdrian Chadd AR8216_REG_ATU_CTRL, 47262042c97SAdrian Chadd val); 47362042c97SAdrian Chadd 47462042c97SAdrian Chadd if (ret) 47562042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: writereg failed\n", __func__); 47662042c97SAdrian Chadd 47762042c97SAdrian Chadd return (ret); 47862042c97SAdrian Chadd } 47962042c97SAdrian Chadd 48062042c97SAdrian Chadd /* 48162042c97SAdrian Chadd * XXX TODO: add another routine to configure the leaky behaviour 48262042c97SAdrian Chadd * when unknown frames are received. These must be consistent 48362042c97SAdrian Chadd * between ethernet switches. 48462042c97SAdrian Chadd */ 48562042c97SAdrian Chadd 48662042c97SAdrian Chadd /* 48762042c97SAdrian Chadd * XXX TODO: this attach routine does NOT free all memory, resources 48862042c97SAdrian Chadd * upon failure! 48962042c97SAdrian Chadd */ 4904ff2f60dSAdrian Chadd static int 491a043e8c7SAdrian Chadd arswitch_attach(device_t dev) 492a043e8c7SAdrian Chadd { 4931b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 4941b334c8bSAdrian Chadd struct sysctl_ctx_list *ctx; 4951b334c8bSAdrian Chadd struct sysctl_oid *tree; 496a043e8c7SAdrian Chadd int err = 0; 497df892897SAdrian Chadd int port; 498a043e8c7SAdrian Chadd 499a043e8c7SAdrian Chadd /* sc->sc_switchtype is already decided in arswitch_probe() */ 500a043e8c7SAdrian Chadd sc->sc_dev = dev; 501a043e8c7SAdrian Chadd mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF); 502a043e8c7SAdrian Chadd sc->page = -1; 503a043e8c7SAdrian Chadd strlcpy(sc->info.es_name, device_get_desc(dev), 504a043e8c7SAdrian Chadd sizeof(sc->info.es_name)); 505a043e8c7SAdrian Chadd 5061b334c8bSAdrian Chadd /* Debugging */ 5071b334c8bSAdrian Chadd ctx = device_get_sysctl_ctx(sc->sc_dev); 5081b334c8bSAdrian Chadd tree = device_get_sysctl_tree(sc->sc_dev); 5091b334c8bSAdrian Chadd SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 5101b334c8bSAdrian Chadd "debug", CTLFLAG_RW, &sc->sc_debug, 0, 5111b334c8bSAdrian Chadd "control debugging printfs"); 5121b334c8bSAdrian Chadd 51362042c97SAdrian Chadd /* Allocate a 128 entry ATU table; hopefully its big enough! */ 51462042c97SAdrian Chadd /* XXX TODO: make this per chip */ 51562042c97SAdrian Chadd sc->atu.entries = malloc(sizeof(etherswitch_atu_entry_t) * 128, 51662042c97SAdrian Chadd M_DEVBUF, M_NOWAIT); 51762042c97SAdrian Chadd if (sc->atu.entries == NULL) { 51862042c97SAdrian Chadd device_printf(sc->sc_dev, "%s: failed to allocate ATU table\n", 51962042c97SAdrian Chadd __func__); 52062042c97SAdrian Chadd return (ENXIO); 52162042c97SAdrian Chadd } 52262042c97SAdrian Chadd sc->atu.count = 0; 52362042c97SAdrian Chadd sc->atu.size = 128; 52462042c97SAdrian Chadd 525ddbc4420SAdrian Chadd /* Default HAL methods */ 526a9ad4222SAdrian Chadd sc->hal.arswitch_port_init = ar8xxx_port_init; 527a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup; 528a9ad4222SAdrian Chadd sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get; 5296dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans; 530749cac13SAdrian Chadd 5316dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup; 5326dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup; 533749cac13SAdrian Chadd 5346dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid; 5356dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid; 536749cac13SAdrian Chadd 537749cac13SAdrian Chadd sc->hal.arswitch_get_dot1q_vlan = ar8xxx_get_dot1q_vlan; 538749cac13SAdrian Chadd sc->hal.arswitch_set_dot1q_vlan = ar8xxx_set_dot1q_vlan; 539f35f94f4SAdrian Chadd sc->hal.arswitch_flush_dot1q_vlan = ar8xxx_flush_dot1q_vlan; 540f35f94f4SAdrian Chadd sc->hal.arswitch_purge_dot1q_vlan = ar8xxx_purge_dot1q_vlan; 541749cac13SAdrian Chadd sc->hal.arswitch_get_port_vlan = ar8xxx_get_port_vlan; 542749cac13SAdrian Chadd sc->hal.arswitch_set_port_vlan = ar8xxx_set_port_vlan; 543749cac13SAdrian Chadd 5444ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush = ar8xxx_atu_flush; 54562042c97SAdrian Chadd sc->hal.arswitch_atu_flush_port = ar8xxx_atu_flush_port; 54662042c97SAdrian Chadd sc->hal.arswitch_atu_learn_default = ar8xxx_atu_learn_default; 54762042c97SAdrian Chadd sc->hal.arswitch_atu_fetch_table = ar8xxx_atu_fetch_table; 548749cac13SAdrian Chadd 54978549b94SAdrian Chadd sc->hal.arswitch_phy_read = arswitch_readphy_internal; 55078549b94SAdrian Chadd sc->hal.arswitch_phy_write = arswitch_writephy_internal; 551ddbc4420SAdrian Chadd 552a043e8c7SAdrian Chadd /* 553a043e8c7SAdrian Chadd * Attach switch related functions 554a043e8c7SAdrian Chadd */ 55527a2ecaaSAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR7240)) 55627a2ecaaSAdrian Chadd ar7240_attach(sc); 557b2152161SAdrian Chadd else if (AR8X16_IS_SWITCH(sc, AR9340)) 558b2152161SAdrian Chadd ar9340_attach(sc); 55927a2ecaaSAdrian Chadd else 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 652a043e8c7SAdrian Chadd bus_generic_probe(dev); 653a043e8c7SAdrian Chadd bus_enumerate_hinted_children(dev); 654a043e8c7SAdrian Chadd err = bus_generic_attach(dev); 6551b334c8bSAdrian Chadd if (err != 0) { 6561b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_ANY, 6571b334c8bSAdrian Chadd "%s: bus_generic_attach: err=%d\n", __func__, err); 658a043e8c7SAdrian Chadd return (err); 6591b334c8bSAdrian Chadd } 660a043e8c7SAdrian Chadd 661a043e8c7SAdrian Chadd callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0); 662454d507aSAleksandr Rybalko 663454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc); 664a043e8c7SAdrian Chadd arswitch_tick(sc); 665454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc); 666a043e8c7SAdrian Chadd 667a043e8c7SAdrian Chadd return (err); 668a043e8c7SAdrian Chadd } 669a043e8c7SAdrian Chadd 670a043e8c7SAdrian Chadd static int 671a043e8c7SAdrian Chadd arswitch_detach(device_t dev) 672a043e8c7SAdrian Chadd { 673a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 674a043e8c7SAdrian Chadd int i; 675a043e8c7SAdrian Chadd 676a043e8c7SAdrian Chadd callout_drain(&sc->callout_tick); 677a043e8c7SAdrian Chadd 678a043e8c7SAdrian Chadd for (i=0; i < sc->numphys; i++) { 679a043e8c7SAdrian Chadd if (sc->miibus[i] != NULL) 680a043e8c7SAdrian Chadd device_delete_child(dev, sc->miibus[i]); 681a043e8c7SAdrian Chadd if (sc->ifp[i] != NULL) 682a043e8c7SAdrian Chadd if_free(sc->ifp[i]); 683a043e8c7SAdrian Chadd free(sc->ifname[i], M_DEVBUF); 684a043e8c7SAdrian Chadd } 685a043e8c7SAdrian Chadd 68662042c97SAdrian Chadd free(sc->atu.entries, M_DEVBUF); 68762042c97SAdrian Chadd 688a043e8c7SAdrian Chadd bus_generic_detach(dev); 689a043e8c7SAdrian Chadd mtx_destroy(&sc->sc_mtx); 690a043e8c7SAdrian Chadd 691a043e8c7SAdrian Chadd return (0); 692a043e8c7SAdrian Chadd } 693a043e8c7SAdrian Chadd 694a043e8c7SAdrian Chadd /* 695a043e8c7SAdrian Chadd * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to 696a043e8c7SAdrian Chadd * port 2, etc. 697a043e8c7SAdrian Chadd */ 698a043e8c7SAdrian Chadd static inline int 699a043e8c7SAdrian Chadd arswitch_portforphy(int phy) 700a043e8c7SAdrian Chadd { 701a043e8c7SAdrian Chadd return (phy+1); 702a043e8c7SAdrian Chadd } 703a043e8c7SAdrian Chadd 704a043e8c7SAdrian Chadd static inline struct mii_data * 705a043e8c7SAdrian Chadd arswitch_miiforport(struct arswitch_softc *sc, int port) 706a043e8c7SAdrian Chadd { 707a043e8c7SAdrian Chadd int phy = port-1; 708a043e8c7SAdrian Chadd 709a043e8c7SAdrian Chadd if (phy < 0 || phy >= sc->numphys) 710a043e8c7SAdrian Chadd return (NULL); 711a043e8c7SAdrian Chadd return (device_get_softc(sc->miibus[phy])); 712a043e8c7SAdrian Chadd } 713a043e8c7SAdrian Chadd 714a043e8c7SAdrian Chadd static inline struct ifnet * 715a043e8c7SAdrian Chadd arswitch_ifpforport(struct arswitch_softc *sc, int port) 716a043e8c7SAdrian Chadd { 717a043e8c7SAdrian Chadd int phy = port-1; 718a043e8c7SAdrian Chadd 719a043e8c7SAdrian Chadd if (phy < 0 || phy >= sc->numphys) 720a043e8c7SAdrian Chadd return (NULL); 721a043e8c7SAdrian Chadd return (sc->ifp[phy]); 722a043e8c7SAdrian Chadd } 723a043e8c7SAdrian Chadd 724a043e8c7SAdrian Chadd /* 725a043e8c7SAdrian Chadd * Convert port status to ifmedia. 726a043e8c7SAdrian Chadd */ 727a043e8c7SAdrian Chadd static void 728a043e8c7SAdrian Chadd arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active) 729a043e8c7SAdrian Chadd { 730a043e8c7SAdrian Chadd *media_active = IFM_ETHER; 731a043e8c7SAdrian Chadd *media_status = IFM_AVALID; 732a043e8c7SAdrian Chadd 733a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0) 734a043e8c7SAdrian Chadd *media_status |= IFM_ACTIVE; 735a043e8c7SAdrian Chadd else { 736a043e8c7SAdrian Chadd *media_active |= IFM_NONE; 737a043e8c7SAdrian Chadd return; 738a043e8c7SAdrian Chadd } 739a043e8c7SAdrian Chadd switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) { 740a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_10: 741a043e8c7SAdrian Chadd *media_active |= IFM_10_T; 742a043e8c7SAdrian Chadd break; 743a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_100: 744a043e8c7SAdrian Chadd *media_active |= IFM_100_TX; 745a043e8c7SAdrian Chadd break; 746a043e8c7SAdrian Chadd case AR8X16_PORT_STS_SPEED_1000: 747a043e8c7SAdrian Chadd *media_active |= IFM_1000_T; 748a043e8c7SAdrian Chadd break; 749a043e8c7SAdrian Chadd } 750a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0) 751a043e8c7SAdrian Chadd *media_active |= IFM_FDX; 752a043e8c7SAdrian Chadd else 753a043e8c7SAdrian Chadd *media_active |= IFM_HDX; 754a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0) 755a043e8c7SAdrian Chadd *media_active |= IFM_ETH_TXPAUSE; 756a043e8c7SAdrian Chadd if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0) 757a043e8c7SAdrian Chadd *media_active |= IFM_ETH_RXPAUSE; 758a043e8c7SAdrian Chadd } 759a043e8c7SAdrian Chadd 760a043e8c7SAdrian Chadd /* 761a043e8c7SAdrian Chadd * Poll the status for all PHYs. We're using the switch port status because 762a043e8c7SAdrian Chadd * thats a lot quicker to read than talking to all the PHYs. Care must be 763a043e8c7SAdrian Chadd * taken that the resulting ifmedia_active is identical to what the PHY will 764a043e8c7SAdrian Chadd * compute, or gratuitous link status changes will occur whenever the PHYs 765a043e8c7SAdrian Chadd * update function is called. 766a043e8c7SAdrian Chadd */ 767a043e8c7SAdrian Chadd static void 768a043e8c7SAdrian Chadd arswitch_miipollstat(struct arswitch_softc *sc) 769a043e8c7SAdrian Chadd { 770a043e8c7SAdrian Chadd int i; 771a043e8c7SAdrian Chadd struct mii_data *mii; 772a043e8c7SAdrian Chadd struct mii_softc *miisc; 773a043e8c7SAdrian Chadd int portstatus; 7744ff2f60dSAdrian Chadd int port_flap = 0; 775a043e8c7SAdrian Chadd 776454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 777454d507aSAleksandr Rybalko 778a043e8c7SAdrian Chadd for (i = 0; i < sc->numphys; i++) { 779a043e8c7SAdrian Chadd if (sc->miibus[i] == NULL) 780a043e8c7SAdrian Chadd continue; 781a043e8c7SAdrian Chadd mii = device_get_softc(sc->miibus[i]); 782ddbc4420SAdrian Chadd /* XXX This would be nice to have abstracted out to be per-chip */ 783ddbc4420SAdrian Chadd /* AR8327/AR8337 has a different register base */ 784ddbc4420SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) 785ddbc4420SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev, 786ddbc4420SAdrian Chadd AR8327_REG_PORT_STATUS(arswitch_portforphy(i))); 787ddbc4420SAdrian Chadd else 788a043e8c7SAdrian Chadd portstatus = arswitch_readreg(sc->sc_dev, 789a043e8c7SAdrian Chadd AR8X16_REG_PORT_STS(arswitch_portforphy(i))); 7901b334c8bSAdrian Chadd #if 1 7911b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "p[%d]=0x%08x (%b)\n", 792b2152161SAdrian Chadd i, 793a043e8c7SAdrian Chadd portstatus, 7941b334c8bSAdrian Chadd portstatus, 795a043e8c7SAdrian Chadd "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7" 796a043e8c7SAdrian Chadd "DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE"); 797a043e8c7SAdrian Chadd #endif 7984ff2f60dSAdrian Chadd /* 7994ff2f60dSAdrian Chadd * If the current status is down, but we have a link 8004ff2f60dSAdrian Chadd * status showing up, we need to do an ATU flush. 8014ff2f60dSAdrian Chadd */ 8024ff2f60dSAdrian Chadd if ((mii->mii_media_status & IFM_ACTIVE) == 0 && 8034ff2f60dSAdrian Chadd (portstatus & AR8X16_PORT_STS_LINK_UP) != 0) { 8044ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: port %d: port -> UP\n", 8054ff2f60dSAdrian Chadd __func__, 8064ff2f60dSAdrian Chadd i); 8074ff2f60dSAdrian Chadd port_flap = 1; 8084ff2f60dSAdrian Chadd } 8094ff2f60dSAdrian Chadd /* 8104ff2f60dSAdrian Chadd * and maybe if a port goes up->down? 8114ff2f60dSAdrian Chadd */ 8124ff2f60dSAdrian Chadd if ((mii->mii_media_status & IFM_ACTIVE) != 0 && 8134ff2f60dSAdrian Chadd (portstatus & AR8X16_PORT_STS_LINK_UP) == 0) { 8144ff2f60dSAdrian Chadd device_printf(sc->sc_dev, "%s: port %d: port -> DOWN\n", 8154ff2f60dSAdrian Chadd __func__, 8164ff2f60dSAdrian Chadd i); 8174ff2f60dSAdrian Chadd port_flap = 1; 8184ff2f60dSAdrian Chadd } 819a043e8c7SAdrian Chadd arswitch_update_ifmedia(portstatus, &mii->mii_media_status, 820a043e8c7SAdrian Chadd &mii->mii_media_active); 821a043e8c7SAdrian Chadd LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 822a043e8c7SAdrian Chadd if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != 823a043e8c7SAdrian Chadd miisc->mii_inst) 824a043e8c7SAdrian Chadd continue; 825a043e8c7SAdrian Chadd mii_phy_update(miisc, MII_POLLSTAT); 826a043e8c7SAdrian Chadd } 827a043e8c7SAdrian Chadd } 8284ff2f60dSAdrian Chadd 8294ff2f60dSAdrian Chadd /* If a port went from down->up, flush the ATU */ 8304ff2f60dSAdrian Chadd if (port_flap) 8314ff2f60dSAdrian Chadd sc->hal.arswitch_atu_flush(sc); 832a043e8c7SAdrian Chadd } 833a043e8c7SAdrian Chadd 834a043e8c7SAdrian Chadd static void 835a043e8c7SAdrian Chadd arswitch_tick(void *arg) 836a043e8c7SAdrian Chadd { 837a043e8c7SAdrian Chadd struct arswitch_softc *sc = arg; 838a043e8c7SAdrian Chadd 839a043e8c7SAdrian Chadd arswitch_miipollstat(sc); 840a043e8c7SAdrian Chadd callout_reset(&sc->callout_tick, hz, arswitch_tick, sc); 841a043e8c7SAdrian Chadd } 842a043e8c7SAdrian Chadd 843454d507aSAleksandr Rybalko static void 844454d507aSAleksandr Rybalko arswitch_lock(device_t dev) 845454d507aSAleksandr Rybalko { 846454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev); 847454d507aSAleksandr Rybalko 848454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 849454d507aSAleksandr Rybalko ARSWITCH_LOCK(sc); 850454d507aSAleksandr Rybalko } 851454d507aSAleksandr Rybalko 852454d507aSAleksandr Rybalko static void 853454d507aSAleksandr Rybalko arswitch_unlock(device_t dev) 854454d507aSAleksandr Rybalko { 855454d507aSAleksandr Rybalko struct arswitch_softc *sc = device_get_softc(dev); 856454d507aSAleksandr Rybalko 857454d507aSAleksandr Rybalko ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 858454d507aSAleksandr Rybalko ARSWITCH_UNLOCK(sc); 859454d507aSAleksandr Rybalko } 860454d507aSAleksandr Rybalko 861a043e8c7SAdrian Chadd static etherswitch_info_t * 862a043e8c7SAdrian Chadd arswitch_getinfo(device_t dev) 863a043e8c7SAdrian Chadd { 864a043e8c7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 865a043e8c7SAdrian Chadd 866a043e8c7SAdrian Chadd return (&sc->info); 867a043e8c7SAdrian Chadd } 868a043e8c7SAdrian Chadd 869a043e8c7SAdrian Chadd static int 870a9ad4222SAdrian Chadd ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p) 871a043e8c7SAdrian Chadd { 872b9f07b86SLuiz Otavio O Souza uint32_t reg; 873b9f07b86SLuiz Otavio O Souza 874b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc); 875b9f07b86SLuiz Otavio O Souza 876b9f07b86SLuiz Otavio O Souza /* Retrieve the PVID. */ 8776dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid); 878b9f07b86SLuiz Otavio O Souza 879b9f07b86SLuiz Otavio O Souza /* Port flags. */ 880b9f07b86SLuiz Otavio O Souza reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port)); 881b9f07b86SLuiz Otavio O Souza if (reg & AR8X16_PORT_CTRL_DOUBLE_TAG) 882b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG; 883b9f07b86SLuiz Otavio O Souza reg >>= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 884b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD) 885b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 886b9f07b86SLuiz Otavio O Souza if ((reg & 0x3) == AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP) 887b9f07b86SLuiz Otavio O Souza p->es_flags |= ETHERSWITCH_PORT_STRIPTAG; 888b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 889a043e8c7SAdrian Chadd 890a9ad4222SAdrian Chadd return (0); 891a9ad4222SAdrian Chadd } 892a9ad4222SAdrian Chadd 893a9ad4222SAdrian Chadd static int 894749cac13SAdrian Chadd arswitch_is_cpuport(struct arswitch_softc *sc, int port) 895749cac13SAdrian Chadd { 896749cac13SAdrian Chadd 897749cac13SAdrian Chadd return ((port == AR8X16_PORT_CPU) || 898749cac13SAdrian Chadd ((AR8X16_IS_SWITCH(sc, AR8327) && 899749cac13SAdrian Chadd port == AR8327_PORT_GMAC6))); 900749cac13SAdrian Chadd } 901749cac13SAdrian Chadd 902749cac13SAdrian Chadd static int 903a9ad4222SAdrian Chadd arswitch_getport(device_t dev, etherswitch_port_t *p) 904a9ad4222SAdrian Chadd { 905a9ad4222SAdrian Chadd struct arswitch_softc *sc; 906a9ad4222SAdrian Chadd struct mii_data *mii; 907a9ad4222SAdrian Chadd struct ifmediareq *ifmr; 908a9ad4222SAdrian Chadd int err; 909a9ad4222SAdrian Chadd 910a9ad4222SAdrian Chadd sc = device_get_softc(dev); 911749cac13SAdrian Chadd /* XXX +1 is for AR8327; should make this configurable! */ 912749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports) 913a9ad4222SAdrian Chadd return (ENXIO); 914a9ad4222SAdrian Chadd 915a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_get(sc, p); 916a9ad4222SAdrian Chadd if (err != 0) 917a9ad4222SAdrian Chadd return (err); 918a9ad4222SAdrian Chadd 919a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port); 920749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) { 921a043e8c7SAdrian Chadd /* fill in fixed values for CPU port */ 922a9ad4222SAdrian Chadd /* XXX is this valid in all cases? */ 923f47857dcSAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 924b9f07b86SLuiz Otavio O Souza ifmr = &p->es_ifmr; 925a043e8c7SAdrian Chadd ifmr->ifm_count = 0; 926a043e8c7SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 927a043e8c7SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 928a043e8c7SAdrian Chadd ifmr->ifm_mask = 0; 929a043e8c7SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 930a043e8c7SAdrian Chadd } else if (mii != NULL) { 931a043e8c7SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 932a043e8c7SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 933a043e8c7SAdrian Chadd if (err) 934a043e8c7SAdrian Chadd return (err); 935a043e8c7SAdrian Chadd } else { 936a043e8c7SAdrian Chadd return (ENXIO); 937a043e8c7SAdrian Chadd } 938c94dc808SAdrian Chadd 939c94dc808SAdrian Chadd if (!arswitch_is_cpuport(sc, p->es_port) && 940c94dc808SAdrian Chadd AR8X16_IS_SWITCH(sc, AR8327)) { 941c94dc808SAdrian Chadd int led; 942c94dc808SAdrian Chadd p->es_nleds = 3; 943c94dc808SAdrian Chadd 944c94dc808SAdrian Chadd for (led = 0; led < p->es_nleds; led++) 945c94dc808SAdrian Chadd { 946c94dc808SAdrian Chadd int style; 947c94dc808SAdrian Chadd uint32_t val; 948c94dc808SAdrian Chadd 949c94dc808SAdrian Chadd /* Find the right style enum for our pattern */ 950c94dc808SAdrian Chadd val = arswitch_readreg(dev, 951c94dc808SAdrian Chadd ar8327_led_mapping[p->es_port-1][led].reg); 952c94dc808SAdrian Chadd val = (val>>ar8327_led_mapping[p->es_port-1][led].shift)&0x03; 953c94dc808SAdrian Chadd 954c94dc808SAdrian Chadd for (style = 0; style < ETHERSWITCH_PORT_LED_MAX; style++) 955c94dc808SAdrian Chadd { 956c94dc808SAdrian Chadd if (led_pattern_table[style] == val) break; 957c94dc808SAdrian Chadd } 958c94dc808SAdrian Chadd 959c94dc808SAdrian Chadd /* can't happen */ 960c94dc808SAdrian Chadd if (style == ETHERSWITCH_PORT_LED_MAX) 961c94dc808SAdrian Chadd style = ETHERSWITCH_PORT_LED_DEFAULT; 962c94dc808SAdrian Chadd 963c94dc808SAdrian Chadd p->es_led[led] = style; 964c94dc808SAdrian Chadd } 965c94dc808SAdrian Chadd } else 966c94dc808SAdrian Chadd { 967c94dc808SAdrian Chadd p->es_nleds = 0; 968c94dc808SAdrian Chadd } 969c94dc808SAdrian Chadd 970a043e8c7SAdrian Chadd return (0); 971a043e8c7SAdrian Chadd } 972a043e8c7SAdrian Chadd 973a043e8c7SAdrian Chadd static int 974a9ad4222SAdrian Chadd ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p) 975a043e8c7SAdrian Chadd { 976b9f07b86SLuiz Otavio O Souza uint32_t reg; 977a9ad4222SAdrian Chadd int err; 978a043e8c7SAdrian Chadd 979b9f07b86SLuiz Otavio O Souza ARSWITCH_LOCK(sc); 980a9ad4222SAdrian Chadd 981b9f07b86SLuiz Otavio O Souza /* Set the PVID. */ 982b9f07b86SLuiz Otavio O Souza if (p->es_pvid != 0) 9836dcbabd7SAdrian Chadd sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid); 984b9f07b86SLuiz Otavio O Souza 985b9f07b86SLuiz Otavio O Souza /* Mutually exclusive. */ 986b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG && 987b9f07b86SLuiz Otavio O Souza p->es_flags & ETHERSWITCH_PORT_STRIPTAG) { 988b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 989b9f07b86SLuiz Otavio O Souza return (EINVAL); 990b9f07b86SLuiz Otavio O Souza } 991b9f07b86SLuiz Otavio O Souza 992b9f07b86SLuiz Otavio O Souza reg = 0; 993b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_DOUBLE_TAG) 994b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_DOUBLE_TAG; 995b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 996b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD << 997b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 998b9f07b86SLuiz Otavio O Souza if (p->es_flags & ETHERSWITCH_PORT_STRIPTAG) 999b9f07b86SLuiz Otavio O Souza reg |= AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP << 1000b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT; 1001b9f07b86SLuiz Otavio O Souza 1002b9f07b86SLuiz Otavio O Souza err = arswitch_modifyreg(sc->sc_dev, 1003b9f07b86SLuiz Otavio O Souza AR8X16_REG_PORT_CTRL(p->es_port), 1004b9f07b86SLuiz Otavio O Souza 0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT | 1005b9f07b86SLuiz Otavio O Souza AR8X16_PORT_CTRL_DOUBLE_TAG, reg); 1006b9f07b86SLuiz Otavio O Souza 1007b9f07b86SLuiz Otavio O Souza ARSWITCH_UNLOCK(sc); 1008a9ad4222SAdrian Chadd return (err); 1009a9ad4222SAdrian Chadd } 1010a9ad4222SAdrian Chadd 1011a9ad4222SAdrian Chadd static int 1012a9ad4222SAdrian Chadd arswitch_setport(device_t dev, etherswitch_port_t *p) 1013a9ad4222SAdrian Chadd { 1014c94dc808SAdrian Chadd int err, i; 1015a9ad4222SAdrian Chadd struct arswitch_softc *sc; 1016a9ad4222SAdrian Chadd struct ifmedia *ifm; 1017a9ad4222SAdrian Chadd struct mii_data *mii; 1018a9ad4222SAdrian Chadd struct ifnet *ifp; 1019a9ad4222SAdrian Chadd 1020a9ad4222SAdrian Chadd sc = device_get_softc(dev); 1021749cac13SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->info.es_nports) 1022a9ad4222SAdrian Chadd return (ENXIO); 1023a9ad4222SAdrian Chadd 1024a9ad4222SAdrian Chadd /* Port flags. */ 1025a9ad4222SAdrian Chadd if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 1026a9ad4222SAdrian Chadd err = sc->hal.arswitch_port_vlan_setup(sc, p); 1027b9f07b86SLuiz Otavio O Souza if (err) 1028b9f07b86SLuiz Otavio O Souza return (err); 1029b9f07b86SLuiz Otavio O Souza } 1030b9f07b86SLuiz Otavio O Souza 1031c94dc808SAdrian Chadd /* Do not allow media or led changes on CPU port. */ 1032749cac13SAdrian Chadd if (arswitch_is_cpuport(sc, p->es_port)) 1033b9f07b86SLuiz Otavio O Souza return (0); 1034a043e8c7SAdrian Chadd 1035c94dc808SAdrian Chadd if (AR8X16_IS_SWITCH(sc, AR8327)) 1036c94dc808SAdrian Chadd { 1037c94dc808SAdrian Chadd for (i = 0; i < 3; i++) 1038c94dc808SAdrian Chadd { 1039c94dc808SAdrian Chadd int err; 1040c94dc808SAdrian Chadd err = arswitch_setled(sc, p->es_port-1, i, p->es_led[i]); 1041c94dc808SAdrian Chadd if (err) 1042c94dc808SAdrian Chadd return (err); 1043c94dc808SAdrian Chadd } 1044c94dc808SAdrian Chadd } 1045c94dc808SAdrian Chadd 1046a043e8c7SAdrian Chadd mii = arswitch_miiforport(sc, p->es_port); 1047a043e8c7SAdrian Chadd if (mii == NULL) 1048a043e8c7SAdrian Chadd return (ENXIO); 1049a043e8c7SAdrian Chadd 1050a043e8c7SAdrian Chadd ifp = arswitch_ifpforport(sc, p->es_port); 1051a043e8c7SAdrian Chadd 1052a043e8c7SAdrian Chadd ifm = &mii->mii_media; 1053b9f07b86SLuiz Otavio O Souza return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); 1054a043e8c7SAdrian Chadd } 1055a043e8c7SAdrian Chadd 1056c94dc808SAdrian Chadd static int 1057c94dc808SAdrian Chadd arswitch_setled(struct arswitch_softc *sc, int phy, int led, int style) 1058c94dc808SAdrian Chadd { 1059c94dc808SAdrian Chadd int shift; 10606d011946SKristof Provost int err; 1061c94dc808SAdrian Chadd 1062c94dc808SAdrian Chadd if (phy < 0 || phy > sc->numphys) 1063c94dc808SAdrian Chadd return EINVAL; 1064c94dc808SAdrian Chadd 1065c94dc808SAdrian Chadd if (style < 0 || style > ETHERSWITCH_PORT_LED_MAX) 1066c94dc808SAdrian Chadd return (EINVAL); 1067c94dc808SAdrian Chadd 10686d011946SKristof Provost ARSWITCH_LOCK(sc); 10696d011946SKristof Provost 1070c94dc808SAdrian Chadd shift = ar8327_led_mapping[phy][led].shift; 10716d011946SKristof Provost err = (arswitch_modifyreg(sc->sc_dev, 1072c94dc808SAdrian Chadd ar8327_led_mapping[phy][led].reg, 1073c94dc808SAdrian Chadd 0x03 << shift, led_pattern_table[style] << shift)); 10746d011946SKristof Provost ARSWITCH_UNLOCK(sc); 10756d011946SKristof Provost 10766d011946SKristof Provost return (err); 1077c94dc808SAdrian Chadd } 1078c94dc808SAdrian Chadd 1079a043e8c7SAdrian Chadd static void 1080a043e8c7SAdrian Chadd arswitch_statchg(device_t dev) 1081a043e8c7SAdrian Chadd { 10821b334c8bSAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 1083a043e8c7SAdrian Chadd 10841b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__); 1085a043e8c7SAdrian Chadd } 1086a043e8c7SAdrian Chadd 1087a043e8c7SAdrian Chadd static int 1088a043e8c7SAdrian Chadd arswitch_ifmedia_upd(struct ifnet *ifp) 1089a043e8c7SAdrian Chadd { 1090a043e8c7SAdrian Chadd struct arswitch_softc *sc = ifp->if_softc; 1091a043e8c7SAdrian Chadd struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit); 1092a043e8c7SAdrian Chadd 1093a043e8c7SAdrian Chadd if (mii == NULL) 1094a043e8c7SAdrian Chadd return (ENXIO); 1095a043e8c7SAdrian Chadd mii_mediachg(mii); 1096a043e8c7SAdrian Chadd return (0); 1097a043e8c7SAdrian Chadd } 1098a043e8c7SAdrian Chadd 1099a043e8c7SAdrian Chadd static void 1100a043e8c7SAdrian Chadd arswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 1101a043e8c7SAdrian Chadd { 1102a043e8c7SAdrian Chadd struct arswitch_softc *sc = ifp->if_softc; 1103a043e8c7SAdrian Chadd struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit); 1104a043e8c7SAdrian Chadd 11051b334c8bSAdrian Chadd DPRINTF(sc, ARSWITCH_DBG_POLL, "%s\n", __func__); 1106a043e8c7SAdrian Chadd 1107a043e8c7SAdrian Chadd if (mii == NULL) 1108a043e8c7SAdrian Chadd return; 1109a043e8c7SAdrian Chadd mii_pollstat(mii); 1110a043e8c7SAdrian Chadd ifmr->ifm_active = mii->mii_media_active; 1111a043e8c7SAdrian Chadd ifmr->ifm_status = mii->mii_media_status; 1112a043e8c7SAdrian Chadd } 1113a043e8c7SAdrian Chadd 1114b9f07b86SLuiz Otavio O Souza static int 1115b9f07b86SLuiz Otavio O Souza arswitch_getconf(device_t dev, etherswitch_conf_t *conf) 1116b9f07b86SLuiz Otavio O Souza { 1117b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc; 1118b9f07b86SLuiz Otavio O Souza 1119b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev); 1120b9f07b86SLuiz Otavio O Souza 1121b9f07b86SLuiz Otavio O Souza /* Return the VLAN mode. */ 1122b9f07b86SLuiz Otavio O Souza conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 1123b9f07b86SLuiz Otavio O Souza conf->vlan_mode = sc->vlan_mode; 1124b9f07b86SLuiz Otavio O Souza 1125b9f07b86SLuiz Otavio O Souza return (0); 1126b9f07b86SLuiz Otavio O Souza } 1127b9f07b86SLuiz Otavio O Souza 1128b9f07b86SLuiz Otavio O Souza static int 1129b9f07b86SLuiz Otavio O Souza arswitch_setconf(device_t dev, etherswitch_conf_t *conf) 1130b9f07b86SLuiz Otavio O Souza { 1131b9f07b86SLuiz Otavio O Souza struct arswitch_softc *sc; 1132b9f07b86SLuiz Otavio O Souza int err; 1133b9f07b86SLuiz Otavio O Souza 1134b9f07b86SLuiz Otavio O Souza sc = device_get_softc(dev); 1135b9f07b86SLuiz Otavio O Souza 1136b9f07b86SLuiz Otavio O Souza /* Set the VLAN mode. */ 1137b9f07b86SLuiz Otavio O Souza if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { 1138b9f07b86SLuiz Otavio O Souza err = arswitch_set_vlan_mode(sc, conf->vlan_mode); 1139b9f07b86SLuiz Otavio O Souza if (err != 0) 1140b9f07b86SLuiz Otavio O Souza return (err); 1141b9f07b86SLuiz Otavio O Souza } 1142b9f07b86SLuiz Otavio O Souza 1143b9f07b86SLuiz Otavio O Souza return (0); 1144b9f07b86SLuiz Otavio O Souza } 1145b9f07b86SLuiz Otavio O Souza 11466dcbabd7SAdrian Chadd static int 114762042c97SAdrian Chadd arswitch_atu_flush_all(device_t dev) 114862042c97SAdrian Chadd { 114962042c97SAdrian Chadd struct arswitch_softc *sc; 115062042c97SAdrian Chadd int err; 115162042c97SAdrian Chadd 115262042c97SAdrian Chadd sc = device_get_softc(dev); 115362042c97SAdrian Chadd ARSWITCH_LOCK(sc); 115462042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush(sc); 115562042c97SAdrian Chadd /* Invalidate cached ATU */ 115662042c97SAdrian Chadd sc->atu.count = 0; 115762042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 115862042c97SAdrian Chadd return (err); 115962042c97SAdrian Chadd } 116062042c97SAdrian Chadd 116162042c97SAdrian Chadd static int 116262042c97SAdrian Chadd arswitch_atu_flush_port(device_t dev, int port) 116362042c97SAdrian Chadd { 116462042c97SAdrian Chadd struct arswitch_softc *sc; 116562042c97SAdrian Chadd int err; 116662042c97SAdrian Chadd 116762042c97SAdrian Chadd sc = device_get_softc(dev); 116862042c97SAdrian Chadd ARSWITCH_LOCK(sc); 116962042c97SAdrian Chadd err = sc->hal.arswitch_atu_flush_port(sc, port); 117062042c97SAdrian Chadd /* Invalidate cached ATU */ 117162042c97SAdrian Chadd sc->atu.count = 0; 117262042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 117362042c97SAdrian Chadd return (err); 117462042c97SAdrian Chadd } 117562042c97SAdrian Chadd 117662042c97SAdrian Chadd static int 117762042c97SAdrian Chadd arswitch_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table) 117862042c97SAdrian Chadd { 117962042c97SAdrian Chadd struct arswitch_softc *sc; 118062042c97SAdrian Chadd int err, nitems; 118162042c97SAdrian Chadd 118262042c97SAdrian Chadd sc = device_get_softc(dev); 118362042c97SAdrian Chadd 118462042c97SAdrian Chadd ARSWITCH_LOCK(sc); 118562042c97SAdrian Chadd /* Initial setup */ 118662042c97SAdrian Chadd nitems = 0; 118762042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, NULL, 0); 118862042c97SAdrian Chadd 118962042c97SAdrian Chadd /* fetch - ideally yes we'd fetch into a separate table then switch */ 1190*7ed08319SAdrian Chadd while (err == 0 && nitems < sc->atu.size) { 119162042c97SAdrian Chadd err = sc->hal.arswitch_atu_fetch_table(sc, 119262042c97SAdrian Chadd &sc->atu.entries[nitems], 1); 119362042c97SAdrian Chadd if (err == 0) { 119462042c97SAdrian Chadd sc->atu.entries[nitems].id = nitems; 119562042c97SAdrian Chadd nitems++; 119662042c97SAdrian Chadd } 119762042c97SAdrian Chadd } 119862042c97SAdrian Chadd sc->atu.count = nitems; 119962042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 120062042c97SAdrian Chadd 120162042c97SAdrian Chadd table->es_nitems = nitems; 120262042c97SAdrian Chadd 120362042c97SAdrian Chadd return (0); 120462042c97SAdrian Chadd } 120562042c97SAdrian Chadd 120662042c97SAdrian Chadd static int 120762042c97SAdrian Chadd arswitch_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e) 120862042c97SAdrian Chadd { 120962042c97SAdrian Chadd struct arswitch_softc *sc; 121062042c97SAdrian Chadd int id; 121162042c97SAdrian Chadd 121262042c97SAdrian Chadd sc = device_get_softc(dev); 121362042c97SAdrian Chadd id = e->id; 121462042c97SAdrian Chadd 121562042c97SAdrian Chadd ARSWITCH_LOCK(sc); 121662042c97SAdrian Chadd if (id > sc->atu.count) { 121762042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 121862042c97SAdrian Chadd return (ENOENT); 121962042c97SAdrian Chadd } 122062042c97SAdrian Chadd 122162042c97SAdrian Chadd memcpy(e, &sc->atu.entries[id], sizeof(*e)); 122262042c97SAdrian Chadd ARSWITCH_UNLOCK(sc); 122362042c97SAdrian Chadd return (0); 122462042c97SAdrian Chadd } 122562042c97SAdrian Chadd 122662042c97SAdrian Chadd static int 12276dcbabd7SAdrian Chadd arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e) 12286dcbabd7SAdrian Chadd { 12296dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 12306dcbabd7SAdrian Chadd 12316dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_getvgroup(sc, e)); 12326dcbabd7SAdrian Chadd } 12336dcbabd7SAdrian Chadd 12346dcbabd7SAdrian Chadd static int 12356dcbabd7SAdrian Chadd arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e) 12366dcbabd7SAdrian Chadd { 12376dcbabd7SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 12386dcbabd7SAdrian Chadd 12396dcbabd7SAdrian Chadd return (sc->hal.arswitch_vlan_setvgroup(sc, e)); 12406dcbabd7SAdrian Chadd } 12416dcbabd7SAdrian Chadd 124278549b94SAdrian Chadd static int 124378549b94SAdrian Chadd arswitch_readphy(device_t dev, int phy, int reg) 124478549b94SAdrian Chadd { 124578549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 124678549b94SAdrian Chadd 124778549b94SAdrian Chadd return (sc->hal.arswitch_phy_read(dev, phy, reg)); 124878549b94SAdrian Chadd } 124978549b94SAdrian Chadd 125078549b94SAdrian Chadd static int 125178549b94SAdrian Chadd arswitch_writephy(device_t dev, int phy, int reg, int val) 125278549b94SAdrian Chadd { 125378549b94SAdrian Chadd struct arswitch_softc *sc = device_get_softc(dev); 125478549b94SAdrian Chadd 125578549b94SAdrian Chadd return (sc->hal.arswitch_phy_write(dev, phy, reg, val)); 125678549b94SAdrian Chadd } 125778549b94SAdrian Chadd 1258a043e8c7SAdrian Chadd static device_method_t arswitch_methods[] = { 1259a043e8c7SAdrian Chadd /* Device interface */ 1260a043e8c7SAdrian Chadd DEVMETHOD(device_probe, arswitch_probe), 1261a043e8c7SAdrian Chadd DEVMETHOD(device_attach, arswitch_attach), 1262a043e8c7SAdrian Chadd DEVMETHOD(device_detach, arswitch_detach), 1263a043e8c7SAdrian Chadd 1264a043e8c7SAdrian Chadd /* bus interface */ 1265a043e8c7SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 1266a043e8c7SAdrian Chadd 1267a043e8c7SAdrian Chadd /* MII interface */ 1268a043e8c7SAdrian Chadd DEVMETHOD(miibus_readreg, arswitch_readphy), 1269a043e8c7SAdrian Chadd DEVMETHOD(miibus_writereg, arswitch_writephy), 1270a043e8c7SAdrian Chadd DEVMETHOD(miibus_statchg, arswitch_statchg), 1271a043e8c7SAdrian Chadd 1272a043e8c7SAdrian Chadd /* MDIO interface */ 1273a043e8c7SAdrian Chadd DEVMETHOD(mdio_readreg, arswitch_readphy), 1274a043e8c7SAdrian Chadd DEVMETHOD(mdio_writereg, arswitch_writephy), 1275a043e8c7SAdrian Chadd 1276a043e8c7SAdrian Chadd /* etherswitch interface */ 1277454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_lock, arswitch_lock), 1278454d507aSAleksandr Rybalko DEVMETHOD(etherswitch_unlock, arswitch_unlock), 1279a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getinfo, arswitch_getinfo), 1280a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readreg, arswitch_readreg), 1281a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writereg, arswitch_writereg), 1282a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, arswitch_readphy), 1283a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, arswitch_writephy), 1284a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getport, arswitch_getport), 1285a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setport, arswitch_setport), 1286a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, arswitch_getvgroup), 1287a043e8c7SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, arswitch_setvgroup), 1288b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_getconf, arswitch_getconf), 1289b9f07b86SLuiz Otavio O Souza DEVMETHOD(etherswitch_setconf, arswitch_setconf), 129062042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_all, arswitch_atu_flush_all), 129162042c97SAdrian Chadd DEVMETHOD(etherswitch_flush_port, arswitch_atu_flush_port), 129262042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table, arswitch_atu_fetch_table), 129362042c97SAdrian Chadd DEVMETHOD(etherswitch_fetch_table_entry, arswitch_atu_fetch_table_entry), 1294a043e8c7SAdrian Chadd 1295a043e8c7SAdrian Chadd DEVMETHOD_END 1296a043e8c7SAdrian Chadd }; 1297a043e8c7SAdrian Chadd 1298a043e8c7SAdrian Chadd DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods, 1299a043e8c7SAdrian Chadd sizeof(struct arswitch_softc)); 1300a043e8c7SAdrian Chadd static devclass_t arswitch_devclass; 1301a043e8c7SAdrian Chadd 1302a043e8c7SAdrian Chadd DRIVER_MODULE(arswitch, mdio, arswitch_driver, arswitch_devclass, 0, 0); 1303a043e8c7SAdrian Chadd DRIVER_MODULE(miibus, arswitch, miibus_driver, miibus_devclass, 0, 0); 1304a043e8c7SAdrian Chadd DRIVER_MODULE(mdio, arswitch, mdio_driver, mdio_devclass, 0, 0); 1305a043e8c7SAdrian Chadd DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, etherswitch_devclass, 0, 0); 1306a043e8c7SAdrian Chadd MODULE_VERSION(arswitch, 1); 1307a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */ 1308a043e8c7SAdrian Chadd MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */ 1309