1e388de98SAdrian Chadd /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3e388de98SAdrian Chadd * 4e388de98SAdrian Chadd * Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>. 5e388de98SAdrian Chadd * 6e388de98SAdrian Chadd * Redistribution and use in source and binary forms, with or without 7e388de98SAdrian Chadd * modification, are permitted provided that the following conditions 8e388de98SAdrian Chadd * are met: 9e388de98SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 10e388de98SAdrian Chadd * notice, this list of conditions and the following disclaimer. 11e388de98SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 12e388de98SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 13e388de98SAdrian Chadd * documentation and/or other materials provided with the distribution. 14e388de98SAdrian Chadd * 15e388de98SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16e388de98SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17e388de98SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18e388de98SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19e388de98SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20e388de98SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21e388de98SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22e388de98SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23e388de98SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24e388de98SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25e388de98SAdrian Chadd * SUCH DAMAGE. 26e388de98SAdrian Chadd */ 27e388de98SAdrian Chadd 28e388de98SAdrian Chadd #include <sys/param.h> 29e388de98SAdrian Chadd #include <sys/bus.h> 30e388de98SAdrian Chadd #include <sys/errno.h> 31e388de98SAdrian Chadd #include <sys/kernel.h> 32e388de98SAdrian Chadd #include <sys/malloc.h> 33e388de98SAdrian Chadd #include <sys/module.h> 34e388de98SAdrian Chadd #include <sys/socket.h> 35e388de98SAdrian Chadd #include <sys/sockio.h> 36e388de98SAdrian Chadd #include <sys/sysctl.h> 37e388de98SAdrian Chadd #include <sys/systm.h> 38e388de98SAdrian Chadd 39e388de98SAdrian Chadd #include <net/if.h> 40e388de98SAdrian Chadd #include <net/if_var.h> 41e388de98SAdrian Chadd #include <net/if_arp.h> 42e388de98SAdrian Chadd #include <net/ethernet.h> 43e388de98SAdrian Chadd #include <net/if_dl.h> 44e388de98SAdrian Chadd #include <net/if_media.h> 45e388de98SAdrian Chadd #include <net/if_types.h> 46e388de98SAdrian Chadd 47e388de98SAdrian Chadd #include <machine/bus.h> 48e388de98SAdrian Chadd #include <dev/iicbus/iic.h> 49e388de98SAdrian Chadd #include <dev/iicbus/iiconf.h> 50e388de98SAdrian Chadd #include <dev/iicbus/iicbus.h> 51e388de98SAdrian Chadd #include <dev/mii/mii.h> 52e388de98SAdrian Chadd #include <dev/mii/miivar.h> 53e388de98SAdrian Chadd #include <dev/mdio/mdio.h> 54be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 551f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h> 56e388de98SAdrian Chadd 57e388de98SAdrian Chadd #include <dev/fdt/fdt_common.h> 58e388de98SAdrian Chadd #include <dev/ofw/ofw_bus.h> 59e388de98SAdrian Chadd #include <dev/ofw/ofw_bus_subr.h> 60e388de98SAdrian Chadd 61e388de98SAdrian Chadd #include <dev/etherswitch/etherswitch.h> 62e388de98SAdrian Chadd 63e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_var.h> 64e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_reg.h> 65e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_phy.h> 66e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_debug.h> 67e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_hw.h> 68e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h> 69e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_hw_port.h> 70e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_hw_mib.h> 71e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_hw_vtu.h> 72e388de98SAdrian Chadd #include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h> 73e388de98SAdrian Chadd 74e388de98SAdrian Chadd #include "mdio_if.h" 75e388de98SAdrian Chadd #include "miibus_if.h" 76e388de98SAdrian Chadd #include "etherswitch_if.h" 77e388de98SAdrian Chadd 78e388de98SAdrian Chadd static struct ofw_compat_data compat_data[] = { 79e388de98SAdrian Chadd { "qcom,ess-switch", 1 }, 80e388de98SAdrian Chadd { NULL, 0 }, 81e388de98SAdrian Chadd }; 82e388de98SAdrian Chadd 83e388de98SAdrian Chadd static int 84e388de98SAdrian Chadd ar40xx_probe(device_t dev) 85e388de98SAdrian Chadd { 86e388de98SAdrian Chadd 87e388de98SAdrian Chadd if (! ofw_bus_status_okay(dev)) 88e388de98SAdrian Chadd return (ENXIO); 89e388de98SAdrian Chadd 90e388de98SAdrian Chadd if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 91e388de98SAdrian Chadd return (ENXIO); 92e388de98SAdrian Chadd 93e388de98SAdrian Chadd device_set_desc(dev, "IPQ4018 ESS Switch fabric / PSGMII PHY"); 94e388de98SAdrian Chadd return (BUS_PROBE_DEFAULT); 95e388de98SAdrian Chadd } 96e388de98SAdrian Chadd 97e388de98SAdrian Chadd static void 98e388de98SAdrian Chadd ar40xx_tick(void *arg) 99e388de98SAdrian Chadd { 100e388de98SAdrian Chadd struct ar40xx_softc *sc = arg; 101e388de98SAdrian Chadd 102e388de98SAdrian Chadd (void) ar40xx_phy_tick(sc); 103e388de98SAdrian Chadd callout_reset(&sc->sc_phy_callout, hz, ar40xx_tick, sc); 104e388de98SAdrian Chadd } 105e388de98SAdrian Chadd 106e388de98SAdrian Chadd static void 107e388de98SAdrian Chadd ar40xx_statchg(device_t dev) 108e388de98SAdrian Chadd { 109e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 110e388de98SAdrian Chadd 111e388de98SAdrian Chadd AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s\n", __func__); 112e388de98SAdrian Chadd } 113e388de98SAdrian Chadd 114e388de98SAdrian Chadd static int 115e388de98SAdrian Chadd ar40xx_readphy(device_t dev, int phy, int reg) 116e388de98SAdrian Chadd { 117e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 118e388de98SAdrian Chadd 119e388de98SAdrian Chadd return MDIO_READREG(sc->sc_mdio_dev, phy, reg); 120e388de98SAdrian Chadd } 121e388de98SAdrian Chadd 122e388de98SAdrian Chadd static int 123e388de98SAdrian Chadd ar40xx_writephy(device_t dev, int phy, int reg, int val) 124e388de98SAdrian Chadd { 125e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 126e388de98SAdrian Chadd 127e388de98SAdrian Chadd return MDIO_WRITEREG(sc->sc_mdio_dev, phy, reg, val); 128e388de98SAdrian Chadd } 129e388de98SAdrian Chadd 130e388de98SAdrian Chadd /* 131e388de98SAdrian Chadd * Do the initial switch configuration. 132e388de98SAdrian Chadd */ 133e388de98SAdrian Chadd static int 134e388de98SAdrian Chadd ar40xx_reset_switch(struct ar40xx_softc *sc) 135e388de98SAdrian Chadd { 136e388de98SAdrian Chadd int ret, i; 137e388de98SAdrian Chadd 138e388de98SAdrian Chadd AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__); 139e388de98SAdrian Chadd 140e388de98SAdrian Chadd /* blank the VLAN config */ 141e388de98SAdrian Chadd memset(&sc->sc_vlan, 0, sizeof(sc->sc_vlan)); 142e388de98SAdrian Chadd 143e388de98SAdrian Chadd /* initial vlan port mapping */ 144e388de98SAdrian Chadd for (i = 0; i < AR40XX_NUM_VTU_ENTRIES; i++) 145e388de98SAdrian Chadd sc->sc_vlan.vlan_id[i] = 0; 146e388de98SAdrian Chadd 147e388de98SAdrian Chadd /* init vlan config */ 148e388de98SAdrian Chadd ret = ar40xx_hw_vlan_init(sc); 149e388de98SAdrian Chadd 150e388de98SAdrian Chadd /* init monitor config */ 151e388de98SAdrian Chadd sc->sc_monitor.mirror_tx = false; 152e388de98SAdrian Chadd sc->sc_monitor.mirror_rx = false; 153e388de98SAdrian Chadd sc->sc_monitor.source_port = 0; 154e388de98SAdrian Chadd sc->sc_monitor.monitor_port = 0; 155e388de98SAdrian Chadd 156e388de98SAdrian Chadd /* apply switch config */ 157e388de98SAdrian Chadd ret = ar40xx_hw_sw_hw_apply(sc); 158e388de98SAdrian Chadd 159e388de98SAdrian Chadd return (ret); 160e388de98SAdrian Chadd } 161e388de98SAdrian Chadd 162e388de98SAdrian Chadd static int 163e388de98SAdrian Chadd ar40xx_sysctl_dump_port_state(SYSCTL_HANDLER_ARGS) 164e388de98SAdrian Chadd { 165e388de98SAdrian Chadd struct ar40xx_softc *sc = arg1; 166e388de98SAdrian Chadd int val = 0; 167e388de98SAdrian Chadd int error; 168e388de98SAdrian Chadd int i; 169e388de98SAdrian Chadd 170e388de98SAdrian Chadd (void) i; (void) sc; 171e388de98SAdrian Chadd 172e388de98SAdrian Chadd error = sysctl_handle_int(oidp, &val, 0, req); 173e388de98SAdrian Chadd if (error || !req->newptr) 174e388de98SAdrian Chadd return (error); 175e388de98SAdrian Chadd 176e388de98SAdrian Chadd if (val < 0 || val > 5) { 177e388de98SAdrian Chadd return (EINVAL); 178e388de98SAdrian Chadd } 179e388de98SAdrian Chadd 180e388de98SAdrian Chadd AR40XX_LOCK(sc); 181e388de98SAdrian Chadd 182e388de98SAdrian Chadd device_printf(sc->sc_dev, "port %d: PORT_STATUS=0x%08x\n", val, 183e388de98SAdrian Chadd AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(val))); 184e388de98SAdrian Chadd device_printf(sc->sc_dev, "port %d: PORT_HEADER=0x%08x\n", val, 185e388de98SAdrian Chadd AR40XX_REG_READ(sc, AR40XX_REG_PORT_HEADER(val))); 186e388de98SAdrian Chadd device_printf(sc->sc_dev, "port %d: PORT_VLAN0=0x%08x\n", val, 187e388de98SAdrian Chadd AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN0(val))); 188e388de98SAdrian Chadd device_printf(sc->sc_dev, "port %d: PORT_VLAN1=0x%08x\n", val, 189e388de98SAdrian Chadd AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN1(val))); 190e388de98SAdrian Chadd device_printf(sc->sc_dev, "port %d: PORT_LOOKUP=0x%08x\n", val, 191e388de98SAdrian Chadd AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(val))); 192e388de98SAdrian Chadd device_printf(sc->sc_dev, "port %d: PORT_HOL_CTRL1=0x%08x\n", val, 193e388de98SAdrian Chadd AR40XX_REG_READ(sc, AR40XX_REG_PORT_HOL_CTRL1(val))); 194e388de98SAdrian Chadd device_printf(sc->sc_dev, "port %d: PORT_FLOWCTRL_THRESH=0x%08x\n", 195e388de98SAdrian Chadd val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_FLOWCTRL_THRESH(val))); 196e388de98SAdrian Chadd 197e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 198e388de98SAdrian Chadd 199e388de98SAdrian Chadd return (0); 200e388de98SAdrian Chadd } 201e388de98SAdrian Chadd 202e388de98SAdrian Chadd static int 203e388de98SAdrian Chadd ar40xx_sysctl_dump_port_mibstats(SYSCTL_HANDLER_ARGS) 204e388de98SAdrian Chadd { 205e388de98SAdrian Chadd struct ar40xx_softc *sc = arg1; 206e388de98SAdrian Chadd int val = 0; 207e388de98SAdrian Chadd int error; 208e388de98SAdrian Chadd int i; 209e388de98SAdrian Chadd 210e388de98SAdrian Chadd (void) i; (void) sc; 211e388de98SAdrian Chadd 212e388de98SAdrian Chadd error = sysctl_handle_int(oidp, &val, 0, req); 213e388de98SAdrian Chadd if (error || !req->newptr) 214e388de98SAdrian Chadd return (error); 215e388de98SAdrian Chadd 216e388de98SAdrian Chadd if (val < 0 || val > 5) { 217e388de98SAdrian Chadd return (EINVAL); 218e388de98SAdrian Chadd } 219e388de98SAdrian Chadd 220e388de98SAdrian Chadd AR40XX_LOCK(sc); 221e388de98SAdrian Chadd 222e388de98SAdrian Chadd /* Yes, this snapshots all ports */ 223e388de98SAdrian Chadd (void) ar40xx_hw_mib_capture(sc); 224e388de98SAdrian Chadd (void) ar40xx_hw_mib_fetch(sc, val); 225e388de98SAdrian Chadd 226e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 227e388de98SAdrian Chadd 228e388de98SAdrian Chadd return (0); 229e388de98SAdrian Chadd } 230e388de98SAdrian Chadd 231e388de98SAdrian Chadd 232e388de98SAdrian Chadd static int 233e388de98SAdrian Chadd ar40xx_sysctl_attach(struct ar40xx_softc *sc) 234e388de98SAdrian Chadd { 235e388de98SAdrian Chadd struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 236e388de98SAdrian Chadd struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 237e388de98SAdrian Chadd 238e388de98SAdrian Chadd SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 239e388de98SAdrian Chadd "debug", CTLFLAG_RW, &sc->sc_debug, 0, 240e388de98SAdrian Chadd "debugging flags"); 241e388de98SAdrian Chadd 242e388de98SAdrian Chadd SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 243e388de98SAdrian Chadd "port_state", CTLTYPE_INT | CTLFLAG_RW, sc, 244e388de98SAdrian Chadd 0, ar40xx_sysctl_dump_port_state, "I", ""); 245e388de98SAdrian Chadd 246e388de98SAdrian Chadd SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 247e388de98SAdrian Chadd "port_mibstats", CTLTYPE_INT | CTLFLAG_RW, sc, 248e388de98SAdrian Chadd 0, ar40xx_sysctl_dump_port_mibstats, "I", ""); 249e388de98SAdrian Chadd 250e388de98SAdrian Chadd return (0); 251e388de98SAdrian Chadd } 252e388de98SAdrian Chadd 253e388de98SAdrian Chadd static int 254e388de98SAdrian Chadd ar40xx_detach(device_t dev) 255e388de98SAdrian Chadd { 256e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 257e388de98SAdrian Chadd int i; 258e388de98SAdrian Chadd 259e388de98SAdrian Chadd device_printf(sc->sc_dev, "%s: called\n", __func__); 260e388de98SAdrian Chadd 261e388de98SAdrian Chadd callout_drain(&sc->sc_phy_callout); 262e388de98SAdrian Chadd 263e388de98SAdrian Chadd /* Free PHYs */ 264e388de98SAdrian Chadd for (i = 0; i < AR40XX_NUM_PHYS; i++) { 265e388de98SAdrian Chadd if (sc->sc_phys.miibus[i] != NULL) 266e388de98SAdrian Chadd device_delete_child(dev, sc->sc_phys.miibus[i]); 267e388de98SAdrian Chadd if (sc->sc_phys.ifp[i] != NULL) 268e388de98SAdrian Chadd if_free(sc->sc_phys.ifp[i]); 269e388de98SAdrian Chadd free(sc->sc_phys.ifname[i], M_DEVBUF); 270e388de98SAdrian Chadd } 271e388de98SAdrian Chadd 272e388de98SAdrian Chadd bus_generic_detach(dev); 273e388de98SAdrian Chadd mtx_destroy(&sc->sc_mtx); 274e388de98SAdrian Chadd 275e388de98SAdrian Chadd return (0); 276e388de98SAdrian Chadd } 277e388de98SAdrian Chadd 278e388de98SAdrian Chadd static int 279e388de98SAdrian Chadd ar40xx_attach(device_t dev) 280e388de98SAdrian Chadd { 281e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 282e388de98SAdrian Chadd phandle_t psgmii_p, root_p, mdio_p; 283e388de98SAdrian Chadd int ret, i; 284e388de98SAdrian Chadd 285e388de98SAdrian Chadd sc->sc_dev = dev; 286e388de98SAdrian Chadd mtx_init(&sc->sc_mtx, "ar40xx_switch", NULL, MTX_DEF); 287e388de98SAdrian Chadd 288e388de98SAdrian Chadd psgmii_p = OF_finddevice("/soc/ess-psgmii"); 289e388de98SAdrian Chadd if (psgmii_p == -1) { 290e388de98SAdrian Chadd device_printf(dev, 291e388de98SAdrian Chadd "%s: couldn't find /soc/ess-psgmii DT node\n", 292e388de98SAdrian Chadd __func__); 293e388de98SAdrian Chadd goto error; 294e388de98SAdrian Chadd } 295e388de98SAdrian Chadd 296e388de98SAdrian Chadd /* 297e388de98SAdrian Chadd * Get the ipq4019-mdio node here, to talk to our local PHYs 298e388de98SAdrian Chadd * if needed 299e388de98SAdrian Chadd */ 300e388de98SAdrian Chadd root_p = OF_finddevice("/soc"); 301e388de98SAdrian Chadd mdio_p = ofw_bus_find_compatible(root_p, "qcom,ipq4019-mdio"); 302e388de98SAdrian Chadd if (mdio_p == -1) { 303e388de98SAdrian Chadd device_printf(dev, "%s: couldn't find ipq4019-mdio DT node\n", 304e388de98SAdrian Chadd __func__); 305e388de98SAdrian Chadd goto error; 306e388de98SAdrian Chadd } 307e388de98SAdrian Chadd sc->sc_mdio_phandle = mdio_p; 308e388de98SAdrian Chadd sc->sc_mdio_dev = OF_device_from_xref(OF_xref_from_node(mdio_p)); 309e388de98SAdrian Chadd if (sc->sc_mdio_dev == NULL) { 310e388de98SAdrian Chadd device_printf(dev, 311e388de98SAdrian Chadd "%s: couldn't get mdio device (mdio_p=%u)\n", 312e388de98SAdrian Chadd __func__, mdio_p); 313e388de98SAdrian Chadd goto error; 314e388de98SAdrian Chadd } 315e388de98SAdrian Chadd 316e388de98SAdrian Chadd /* get psgmii base address from psgmii node */ 317e388de98SAdrian Chadd ret = OF_decode_addr(psgmii_p, 0, &sc->sc_psgmii_mem_tag, 318e388de98SAdrian Chadd &sc->sc_psgmii_mem_handle, 319e388de98SAdrian Chadd &sc->sc_psgmii_mem_size); 320e388de98SAdrian Chadd if (ret != 0) { 321e388de98SAdrian Chadd device_printf(dev, "%s: couldn't map psgmii mem (%d)\n", 322e388de98SAdrian Chadd __func__, ret); 323e388de98SAdrian Chadd goto error; 324e388de98SAdrian Chadd } 325e388de98SAdrian Chadd 326e388de98SAdrian Chadd /* get switch base address */ 327e388de98SAdrian Chadd sc->sc_ess_mem_rid = 0; 328e388de98SAdrian Chadd sc->sc_ess_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 329e388de98SAdrian Chadd &sc->sc_ess_mem_rid, RF_ACTIVE); 330e388de98SAdrian Chadd if (sc->sc_ess_mem_res == NULL) { 331e388de98SAdrian Chadd device_printf(dev, "%s: failed to find memory resource\n", 332e388de98SAdrian Chadd __func__); 333e388de98SAdrian Chadd goto error; 334e388de98SAdrian Chadd } 335e388de98SAdrian Chadd sc->sc_ess_mem_size = (size_t) bus_get_resource_count(dev, 336e388de98SAdrian Chadd SYS_RES_MEMORY, sc->sc_ess_mem_rid); 337e388de98SAdrian Chadd if (sc->sc_ess_mem_size == 0) { 338e388de98SAdrian Chadd device_printf(dev, "%s: failed to get device memory size\n", 339e388de98SAdrian Chadd __func__); 340e388de98SAdrian Chadd goto error; 341e388de98SAdrian Chadd } 342e388de98SAdrian Chadd 343e388de98SAdrian Chadd ret = OF_getencprop(ofw_bus_get_node(dev), "switch_mac_mode", 344e388de98SAdrian Chadd &sc->sc_config.switch_mac_mode, 345e388de98SAdrian Chadd sizeof(sc->sc_config.switch_mac_mode)); 346e388de98SAdrian Chadd if (ret < 0) { 347e388de98SAdrian Chadd device_printf(dev, "%s: missing switch_mac_mode property\n", 348e388de98SAdrian Chadd __func__); 349e388de98SAdrian Chadd goto error; 350e388de98SAdrian Chadd } 351e388de98SAdrian Chadd 352e388de98SAdrian Chadd ret = OF_getencprop(ofw_bus_get_node(dev), "switch_cpu_bmp", 353e388de98SAdrian Chadd &sc->sc_config.switch_cpu_bmp, 354e388de98SAdrian Chadd sizeof(sc->sc_config.switch_cpu_bmp)); 355e388de98SAdrian Chadd if (ret < 0) { 356e388de98SAdrian Chadd device_printf(dev, "%s: missing switch_cpu_bmp property\n", 357e388de98SAdrian Chadd __func__); 358e388de98SAdrian Chadd goto error; 359e388de98SAdrian Chadd } 360e388de98SAdrian Chadd 361e388de98SAdrian Chadd ret = OF_getencprop(ofw_bus_get_node(dev), "switch_lan_bmp", 362e388de98SAdrian Chadd &sc->sc_config.switch_lan_bmp, 363e388de98SAdrian Chadd sizeof(sc->sc_config.switch_lan_bmp)); 364e388de98SAdrian Chadd if (ret < 0) { 365e388de98SAdrian Chadd device_printf(dev, "%s: missing switch_lan_bmp property\n", 366e388de98SAdrian Chadd __func__); 367e388de98SAdrian Chadd goto error; 368e388de98SAdrian Chadd } 369e388de98SAdrian Chadd 370e388de98SAdrian Chadd ret = OF_getencprop(ofw_bus_get_node(dev), "switch_wan_bmp", 371e388de98SAdrian Chadd &sc->sc_config.switch_wan_bmp, 372e388de98SAdrian Chadd sizeof(sc->sc_config.switch_wan_bmp)); 373e388de98SAdrian Chadd if (ret < 0) { 374e388de98SAdrian Chadd device_printf(dev, "%s: missing switch_wan_bmp property\n", 375e388de98SAdrian Chadd __func__); 376e388de98SAdrian Chadd goto error; 377e388de98SAdrian Chadd } 378e388de98SAdrian Chadd 379e388de98SAdrian Chadd ret = clk_get_by_ofw_name(dev, 0, "ess_clk", &sc->sc_ess_clk); 380e388de98SAdrian Chadd if (ret != 0) { 381e388de98SAdrian Chadd device_printf(dev, "%s: failed to find ess_clk (%d)\n", 382e388de98SAdrian Chadd __func__, ret); 383e388de98SAdrian Chadd goto error; 384e388de98SAdrian Chadd } 385e388de98SAdrian Chadd ret = clk_enable(sc->sc_ess_clk); 386e388de98SAdrian Chadd if (ret != 0) { 387e388de98SAdrian Chadd device_printf(dev, "%s: failed to enable clock (%d)\n", 388e388de98SAdrian Chadd __func__, ret); 389e388de98SAdrian Chadd goto error; 390e388de98SAdrian Chadd } 391e388de98SAdrian Chadd 392e388de98SAdrian Chadd ret = hwreset_get_by_ofw_name(dev, 0, "ess_rst", &sc->sc_ess_rst); 393e388de98SAdrian Chadd if (ret != 0) { 394e388de98SAdrian Chadd device_printf(dev, "%s: failed to find ess_rst (%d)\n", 395e388de98SAdrian Chadd __func__, ret); 396e388de98SAdrian Chadd goto error; 397e388de98SAdrian Chadd } 398e388de98SAdrian Chadd 399e388de98SAdrian Chadd /* 400e388de98SAdrian Chadd * Ok, at this point we have enough resources to do an initial 401e388de98SAdrian Chadd * reset and configuration. 402e388de98SAdrian Chadd */ 403e388de98SAdrian Chadd 404e388de98SAdrian Chadd AR40XX_LOCK(sc); 405e388de98SAdrian Chadd 406e388de98SAdrian Chadd /* Initial PSGMII/RGMII port configuration */ 407e388de98SAdrian Chadd ret = ar40xx_hw_psgmii_init_config(sc); 408e388de98SAdrian Chadd if (ret != 0) { 409e388de98SAdrian Chadd device_printf(sc->sc_dev, 410e388de98SAdrian Chadd "ERROR: failed to init PSGMII (%d)\n", ret); 411e388de98SAdrian Chadd goto error_locked; 412e388de98SAdrian Chadd } 413e388de98SAdrian Chadd 414e388de98SAdrian Chadd /* 415e388de98SAdrian Chadd * ESS reset - this resets both the ethernet switch 416e388de98SAdrian Chadd * AND the ethernet block. 417e388de98SAdrian Chadd */ 418e388de98SAdrian Chadd ret = ar40xx_hw_ess_reset(sc); 419e388de98SAdrian Chadd if (ret != 0) { 420e388de98SAdrian Chadd device_printf(sc->sc_dev, 421e388de98SAdrian Chadd "ERROR: failed to reset ESS block (%d)\n", ret); 422e388de98SAdrian Chadd goto error_locked; 423e388de98SAdrian Chadd } 424e388de98SAdrian Chadd 425e388de98SAdrian Chadd /* 426e388de98SAdrian Chadd * Check the PHY IDs for each of the PHYs from 0..4; 427e388de98SAdrian Chadd * this is useful to make sure that we can SEE the external 428e388de98SAdrian Chadd * PHY(s). 429e388de98SAdrian Chadd */ 430e388de98SAdrian Chadd if (bootverbose) { 431e388de98SAdrian Chadd ret = ar40xx_hw_phy_get_ids(sc); 432e388de98SAdrian Chadd if (ret != 0) { 433e388de98SAdrian Chadd device_printf(sc->sc_dev, 434e388de98SAdrian Chadd "ERROR: failed to check PHY IDs (%d)\n", ret); 435e388de98SAdrian Chadd goto error_locked; 436e388de98SAdrian Chadd } 437e388de98SAdrian Chadd } 438e388de98SAdrian Chadd 439e388de98SAdrian Chadd /* 440e388de98SAdrian Chadd * Do PSGMII PHY self-test; work-around issues. 441e388de98SAdrian Chadd */ 442e388de98SAdrian Chadd ret = ar40xx_hw_psgmii_self_test(sc); 443e388de98SAdrian Chadd if (ret != 0) { 444e388de98SAdrian Chadd device_printf(sc->sc_dev, 445e388de98SAdrian Chadd "ERROR: failed to do PSGMII self-test (%d)\n", ret); 446e388de98SAdrian Chadd goto error_locked; 447e388de98SAdrian Chadd } 448e388de98SAdrian Chadd 449e388de98SAdrian Chadd /* Return port config to runtime state */ 450e388de98SAdrian Chadd ret = ar40xx_hw_psgmii_self_test_clean(sc); 451e388de98SAdrian Chadd if (ret != 0) { 452e388de98SAdrian Chadd device_printf(sc->sc_dev, 453e388de98SAdrian Chadd "ERROR: failed to do PSGMII runtime config (%d)\n", ret); 454e388de98SAdrian Chadd goto error_locked; 455e388de98SAdrian Chadd } 456e388de98SAdrian Chadd 457e388de98SAdrian Chadd /* mac_mode_init */ 458e388de98SAdrian Chadd ret = ar40xx_hw_psgmii_set_mac_mode(sc, 459e388de98SAdrian Chadd sc->sc_config.switch_mac_mode); 460e388de98SAdrian Chadd 461e388de98SAdrian Chadd /* Initialise each hardware port */ 462e388de98SAdrian Chadd for (i = 0; i < AR40XX_NUM_PORTS; i++) { 463e388de98SAdrian Chadd ret = ar40xx_hw_port_init(sc, i); 464e388de98SAdrian Chadd } 465e388de98SAdrian Chadd 466e388de98SAdrian Chadd /* initialise the global switch configuration */ 467e388de98SAdrian Chadd ret = ar40xx_hw_init_globals(sc); 468e388de98SAdrian Chadd 469e388de98SAdrian Chadd /* reset the switch vlan/port learning config */ 470e388de98SAdrian Chadd ret = ar40xx_reset_switch(sc); 471e388de98SAdrian Chadd 472e388de98SAdrian Chadd /* cpuport setup */ 473e388de98SAdrian Chadd ret = ar40xx_hw_port_cpuport_setup(sc); 474e388de98SAdrian Chadd 475e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 476e388de98SAdrian Chadd 477e388de98SAdrian Chadd #if 0 478e388de98SAdrian Chadd /* We may end up needing the QM workaround code here.. */ 479e388de98SAdrian Chadd device_printf(dev, "%s: TODO: QM error check\n", __func__); 480e388de98SAdrian Chadd #endif 481e388de98SAdrian Chadd 482e388de98SAdrian Chadd /* Attach PHYs */ 483e388de98SAdrian Chadd ret = ar40xx_attach_phys(sc); 484e388de98SAdrian Chadd 485*723da5d9SJohn Baldwin bus_identify_children(dev); 486e388de98SAdrian Chadd bus_enumerate_hinted_children(dev); 487e388de98SAdrian Chadd ret = bus_generic_attach(dev); 488e388de98SAdrian Chadd 489e388de98SAdrian Chadd /* Start timer */ 490e388de98SAdrian Chadd callout_init_mtx(&sc->sc_phy_callout, &sc->sc_mtx, 0); 491e388de98SAdrian Chadd 492e388de98SAdrian Chadd /* 493e388de98SAdrian Chadd * Setup the etherswitch info block. 494e388de98SAdrian Chadd */ 495e388de98SAdrian Chadd strlcpy(sc->sc_info.es_name, device_get_desc(dev), 496e388de98SAdrian Chadd sizeof(sc->sc_info.es_name)); 497e388de98SAdrian Chadd sc->sc_info.es_nports = AR40XX_NUM_PORTS; 498e388de98SAdrian Chadd sc->sc_info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q; 499e388de98SAdrian Chadd /* XXX TODO: double-tag / 802.1ad */ 500e388de98SAdrian Chadd sc->sc_info.es_nvlangroups = AR40XX_NUM_VTU_ENTRIES; 501e388de98SAdrian Chadd 502e388de98SAdrian Chadd /* 503e388de98SAdrian Chadd * Fetch the initial port configuration. 504e388de98SAdrian Chadd */ 505e388de98SAdrian Chadd AR40XX_LOCK(sc); 506e388de98SAdrian Chadd ar40xx_tick(sc); 507e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 508e388de98SAdrian Chadd 509e388de98SAdrian Chadd ar40xx_sysctl_attach(sc); 510e388de98SAdrian Chadd 511e388de98SAdrian Chadd return (0); 512e388de98SAdrian Chadd error_locked: 513e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 514e388de98SAdrian Chadd error: 515e388de98SAdrian Chadd ar40xx_detach(dev); 516e388de98SAdrian Chadd return (ENXIO); 517e388de98SAdrian Chadd } 518e388de98SAdrian Chadd 519e388de98SAdrian Chadd static void 520e388de98SAdrian Chadd ar40xx_lock(device_t dev) 521e388de98SAdrian Chadd { 522e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 523e388de98SAdrian Chadd 524e388de98SAdrian Chadd AR40XX_LOCK(sc); 525e388de98SAdrian Chadd } 526e388de98SAdrian Chadd 527e388de98SAdrian Chadd static void 528e388de98SAdrian Chadd ar40xx_unlock(device_t dev) 529e388de98SAdrian Chadd { 530e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 531e388de98SAdrian Chadd 532e388de98SAdrian Chadd AR40XX_LOCK_ASSERT(sc); 533e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 534e388de98SAdrian Chadd } 535e388de98SAdrian Chadd 536e388de98SAdrian Chadd static etherswitch_info_t * 537e388de98SAdrian Chadd ar40xx_getinfo(device_t dev) 538e388de98SAdrian Chadd { 539e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 540e388de98SAdrian Chadd 541e388de98SAdrian Chadd return (&sc->sc_info); 542e388de98SAdrian Chadd } 543e388de98SAdrian Chadd 544e388de98SAdrian Chadd static int 545e388de98SAdrian Chadd ar40xx_readreg(device_t dev, int addr) 546e388de98SAdrian Chadd { 547e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 548e388de98SAdrian Chadd 549e388de98SAdrian Chadd if (addr >= sc->sc_ess_mem_size - 1) 550e388de98SAdrian Chadd return (-1); 551e388de98SAdrian Chadd 552e388de98SAdrian Chadd AR40XX_REG_BARRIER_READ(sc); 553e388de98SAdrian Chadd 554e388de98SAdrian Chadd return AR40XX_REG_READ(sc, addr); 555e388de98SAdrian Chadd } 556e388de98SAdrian Chadd 557e388de98SAdrian Chadd static int 558e388de98SAdrian Chadd ar40xx_writereg(device_t dev, int addr, int value) 559e388de98SAdrian Chadd { 560e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 561e388de98SAdrian Chadd 562e388de98SAdrian Chadd if (addr >= sc->sc_ess_mem_size - 1) 563e388de98SAdrian Chadd return (-1); 564e388de98SAdrian Chadd 565e388de98SAdrian Chadd AR40XX_REG_WRITE(sc, addr, value); 566e388de98SAdrian Chadd AR40XX_REG_BARRIER_WRITE(sc); 567e388de98SAdrian Chadd return (0); 568e388de98SAdrian Chadd } 569e388de98SAdrian Chadd 570e388de98SAdrian Chadd /* 571e388de98SAdrian Chadd * Get the port configuration and status. 572e388de98SAdrian Chadd */ 573e388de98SAdrian Chadd static int 574e388de98SAdrian Chadd ar40xx_getport(device_t dev, etherswitch_port_t *p) 575e388de98SAdrian Chadd { 576e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 577e388de98SAdrian Chadd struct mii_data *mii = NULL; 578e388de98SAdrian Chadd struct ifmediareq *ifmr; 579e388de98SAdrian Chadd int err; 580e388de98SAdrian Chadd 581e388de98SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports) 582e388de98SAdrian Chadd return (ENXIO); 583e388de98SAdrian Chadd 584e388de98SAdrian Chadd AR40XX_LOCK(sc); 585e388de98SAdrian Chadd /* Fetch the current VLAN configuration for this port */ 586e388de98SAdrian Chadd /* PVID */ 587e388de98SAdrian Chadd ar40xx_hw_get_port_pvid(sc, p->es_port, &p->es_pvid); 588e388de98SAdrian Chadd 589e388de98SAdrian Chadd /* 590e388de98SAdrian Chadd * The VLAN egress aren't appropriate to the ports; 591e388de98SAdrian Chadd * instead it's part of the VLAN group config. 592e388de98SAdrian Chadd */ 593e388de98SAdrian Chadd 594e388de98SAdrian Chadd /* Get MII config */ 595e388de98SAdrian Chadd mii = ar40xx_phy_miiforport(sc, p->es_port); 596e388de98SAdrian Chadd 597e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 598e388de98SAdrian Chadd 599e388de98SAdrian Chadd if (p->es_port == 0) { 600e388de98SAdrian Chadd /* CPU port */ 601e388de98SAdrian Chadd p->es_flags |= ETHERSWITCH_PORT_CPU; 602e388de98SAdrian Chadd ifmr = &p->es_ifmr; 603e388de98SAdrian Chadd ifmr->ifm_count = 0; 604e388de98SAdrian Chadd ifmr->ifm_current = ifmr->ifm_active = 605e388de98SAdrian Chadd IFM_ETHER | IFM_1000_T | IFM_FDX; 606e388de98SAdrian Chadd ifmr->ifm_mask = 0; 607e388de98SAdrian Chadd ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 608e388de98SAdrian Chadd } else if (mii != NULL) { 609e388de98SAdrian Chadd /* non-CPU port */ 610e388de98SAdrian Chadd err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 611e388de98SAdrian Chadd &mii->mii_media, SIOCGIFMEDIA); 612e388de98SAdrian Chadd if (err) 613e388de98SAdrian Chadd return (err); 614e388de98SAdrian Chadd } else { 615e388de98SAdrian Chadd return (ENXIO); 616e388de98SAdrian Chadd } 617e388de98SAdrian Chadd 618e388de98SAdrian Chadd return (0); 619e388de98SAdrian Chadd } 620e388de98SAdrian Chadd 621e388de98SAdrian Chadd /* 622e388de98SAdrian Chadd * Set the port configuration and status. 623e388de98SAdrian Chadd */ 624e388de98SAdrian Chadd static int 625e388de98SAdrian Chadd ar40xx_setport(device_t dev, etherswitch_port_t *p) 626e388de98SAdrian Chadd { 627e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 628e388de98SAdrian Chadd struct ifmedia *ifm; 629e388de98SAdrian Chadd struct mii_data *mii; 6302e6a8c1aSJustin Hibbits if_t ifp; 631e388de98SAdrian Chadd int ret; 632e388de98SAdrian Chadd 633e388de98SAdrian Chadd if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports) 634e388de98SAdrian Chadd return (EINVAL); 635e388de98SAdrian Chadd 636e388de98SAdrian Chadd /* Port flags */ 637e388de98SAdrian Chadd AR40XX_LOCK(sc); 638e388de98SAdrian Chadd ret = ar40xx_hw_set_port_pvid(sc, p->es_port, p->es_pvid); 639e388de98SAdrian Chadd if (ret != 0) { 640e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 641e388de98SAdrian Chadd return (ret); 642e388de98SAdrian Chadd } 643e388de98SAdrian Chadd /* XXX TODO: tag strip/unstrip, double-tag, etc */ 644e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 645e388de98SAdrian Chadd 646e388de98SAdrian Chadd /* Don't change media config on CPU port */ 647e388de98SAdrian Chadd if (p->es_port == 0) 648e388de98SAdrian Chadd return (0); 649e388de98SAdrian Chadd 650e388de98SAdrian Chadd mii = ar40xx_phy_miiforport(sc, p->es_port); 651e388de98SAdrian Chadd if (mii == NULL) 652e388de98SAdrian Chadd return (ENXIO); 653e388de98SAdrian Chadd 654e388de98SAdrian Chadd ifp = ar40xx_phy_ifpforport(sc, p->es_port); 655e388de98SAdrian Chadd 656e388de98SAdrian Chadd ifm = &mii->mii_media; 657e388de98SAdrian Chadd return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA)); 658e388de98SAdrian Chadd 659e388de98SAdrian Chadd return (0); 660e388de98SAdrian Chadd } 661e388de98SAdrian Chadd 662e388de98SAdrian Chadd /* 663e388de98SAdrian Chadd * Get the current VLAN group (per-port, ISL, dot1q) configuration. 664e388de98SAdrian Chadd * 665e388de98SAdrian Chadd * For now the only supported operating mode is dot1q. 666e388de98SAdrian Chadd */ 667e388de98SAdrian Chadd static int 668e388de98SAdrian Chadd ar40xx_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 669e388de98SAdrian Chadd { 670e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 671e388de98SAdrian Chadd int vid, ret; 672e388de98SAdrian Chadd 673e388de98SAdrian Chadd if (vg->es_vlangroup > sc->sc_info.es_nvlangroups) 674e388de98SAdrian Chadd return (EINVAL); 675e388de98SAdrian Chadd 676e388de98SAdrian Chadd vg->es_untagged_ports = 0; 677e388de98SAdrian Chadd vg->es_member_ports = 0; 678e388de98SAdrian Chadd vg->es_fid = 0; 679e388de98SAdrian Chadd 680e388de98SAdrian Chadd AR40XX_LOCK(sc); 681e388de98SAdrian Chadd 682e388de98SAdrian Chadd /* Note: only supporting 802.1q VLAN config for now */ 683e388de98SAdrian Chadd if (sc->sc_vlan.vlan != 1) { 684e388de98SAdrian Chadd vg->es_member_ports = 0; 685e388de98SAdrian Chadd vg->es_untagged_ports = 0; 686e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 687e388de98SAdrian Chadd return (-1); 688e388de98SAdrian Chadd } 689e388de98SAdrian Chadd 690e388de98SAdrian Chadd /* Get vlangroup mapping to VLAN id */ 691e388de98SAdrian Chadd vid = sc->sc_vlan.vlan_id[vg->es_vlangroup]; 692e388de98SAdrian Chadd if ((vid & ETHERSWITCH_VID_VALID) == 0) { 693e388de98SAdrian Chadd /* Not an active vgroup; bail */ 694e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 695e388de98SAdrian Chadd return (0); 696e388de98SAdrian Chadd } 697e388de98SAdrian Chadd vg->es_vid = vid; 698e388de98SAdrian Chadd 699e388de98SAdrian Chadd ret = ar40xx_hw_vtu_get_vlan(sc, vid, &vg->es_member_ports, 700e388de98SAdrian Chadd &vg->es_untagged_ports); 701e388de98SAdrian Chadd 702e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 703e388de98SAdrian Chadd 704e388de98SAdrian Chadd if (ret == 0) { 705e388de98SAdrian Chadd vg->es_vid |= ETHERSWITCH_VID_VALID; 706e388de98SAdrian Chadd } 707e388de98SAdrian Chadd 708e388de98SAdrian Chadd return (ret); 709e388de98SAdrian Chadd } 710e388de98SAdrian Chadd 711e388de98SAdrian Chadd /* 712e388de98SAdrian Chadd * Set the current VLAN group (per-port, ISL, dot1q) configuration. 713e388de98SAdrian Chadd * 714e388de98SAdrian Chadd * For now the only supported operating mode is dot1q. 715e388de98SAdrian Chadd */ 716e388de98SAdrian Chadd static int 717e388de98SAdrian Chadd ar40xx_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 718e388de98SAdrian Chadd { 719e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 720e388de98SAdrian Chadd int err, vid; 721e388de98SAdrian Chadd 722e388de98SAdrian Chadd /* For now we only support 802.1q mode */ 723e388de98SAdrian Chadd if (sc->sc_vlan.vlan == 0) 724e388de98SAdrian Chadd return (EINVAL); 725e388de98SAdrian Chadd 726e388de98SAdrian Chadd AR40XX_LOCK(sc); 727e388de98SAdrian Chadd vid = sc->sc_vlan.vlan_id[vg->es_vlangroup]; 728e388de98SAdrian Chadd /* 729e388de98SAdrian Chadd * If we have an 802.1q VID and it's different to the current one, 730e388de98SAdrian Chadd * purge the current VTU entry. 731e388de98SAdrian Chadd */ 732e388de98SAdrian Chadd if ((vid != 0) && 733e388de98SAdrian Chadd ((vid & ETHERSWITCH_VID_VALID) != 0) && 734e388de98SAdrian Chadd ((vid & ETHERSWITCH_VID_MASK) != 735e388de98SAdrian Chadd (vg->es_vid & ETHERSWITCH_VID_MASK))) { 736e388de98SAdrian Chadd AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP, 737e388de98SAdrian Chadd "%s: purging VID %d first\n", __func__, vid); 738e388de98SAdrian Chadd err = ar40xx_hw_vtu_flush(sc); 739e388de98SAdrian Chadd if (err != 0) { 740e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 741e388de98SAdrian Chadd return (err); 742e388de98SAdrian Chadd } 743e388de98SAdrian Chadd } 744e388de98SAdrian Chadd 745e388de98SAdrian Chadd /* Update VLAN ID */ 746e388de98SAdrian Chadd vid = vg->es_vid & ETHERSWITCH_VID_MASK; 747e388de98SAdrian Chadd sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid; 748e388de98SAdrian Chadd if (vid == 0) { 749e388de98SAdrian Chadd /* Setting it to 0 disables the group */ 750e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 751e388de98SAdrian Chadd return (0); 752e388de98SAdrian Chadd } 753e388de98SAdrian Chadd /* Add valid bit for this entry */ 754e388de98SAdrian Chadd sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid | ETHERSWITCH_VID_VALID; 755e388de98SAdrian Chadd 756e388de98SAdrian Chadd /* Update hardware */ 757e388de98SAdrian Chadd err = ar40xx_hw_vtu_load_vlan(sc, vid, vg->es_member_ports, 758e388de98SAdrian Chadd vg->es_untagged_ports); 759e388de98SAdrian Chadd if (err != 0) { 760e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 761e388de98SAdrian Chadd return (err); 762e388de98SAdrian Chadd } 763e388de98SAdrian Chadd 764e388de98SAdrian Chadd /* Update the config for the given entry */ 765e388de98SAdrian Chadd sc->sc_vlan.vlan_ports[vg->es_vlangroup] = vg->es_member_ports; 766e388de98SAdrian Chadd sc->sc_vlan.vlan_untagged[vg->es_vlangroup] = vg->es_untagged_ports; 767e388de98SAdrian Chadd 768e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 769e388de98SAdrian Chadd 770e388de98SAdrian Chadd return (0); 771e388de98SAdrian Chadd } 772e388de98SAdrian Chadd 773e388de98SAdrian Chadd /* 774e388de98SAdrian Chadd * Get the current configuration mode. 775e388de98SAdrian Chadd */ 776e388de98SAdrian Chadd static int 777e388de98SAdrian Chadd ar40xx_getconf(device_t dev, etherswitch_conf_t *conf) 778e388de98SAdrian Chadd { 779e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 780e388de98SAdrian Chadd int ret; 781e388de98SAdrian Chadd 782e388de98SAdrian Chadd AR40XX_LOCK(sc); 783e388de98SAdrian Chadd 784e388de98SAdrian Chadd /* Only support dot1q VLAN for now */ 785e388de98SAdrian Chadd conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 786e388de98SAdrian Chadd conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 787e388de98SAdrian Chadd 788e388de98SAdrian Chadd /* Switch MAC address */ 789e388de98SAdrian Chadd ret = ar40xx_hw_read_switch_mac_address(sc, &conf->switch_macaddr); 790e388de98SAdrian Chadd if (ret == 0) 791e388de98SAdrian Chadd conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR; 792e388de98SAdrian Chadd 793e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 794e388de98SAdrian Chadd 795e388de98SAdrian Chadd return (0); 796e388de98SAdrian Chadd } 797e388de98SAdrian Chadd 798e388de98SAdrian Chadd /* 799e388de98SAdrian Chadd * Set the current configuration and do a switch reset. 800e388de98SAdrian Chadd * 801e388de98SAdrian Chadd * For now the only supported operating mode is dot1q, don't 802e388de98SAdrian Chadd * allow it to be set to non-dot1q. 803e388de98SAdrian Chadd */ 804e388de98SAdrian Chadd static int 805e388de98SAdrian Chadd ar40xx_setconf(device_t dev, etherswitch_conf_t *conf) 806e388de98SAdrian Chadd { 807e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 808e388de98SAdrian Chadd int ret = 0; 809e388de98SAdrian Chadd 810e388de98SAdrian Chadd if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { 811e388de98SAdrian Chadd /* Only support dot1q VLAN for now */ 812e388de98SAdrian Chadd if (conf->vlan_mode != ETHERSWITCH_VLAN_DOT1Q) 813e388de98SAdrian Chadd return (EINVAL); 814e388de98SAdrian Chadd } 815e388de98SAdrian Chadd 816e388de98SAdrian Chadd if (conf->cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) { 817e388de98SAdrian Chadd AR40XX_LOCK(sc); 818e388de98SAdrian Chadd ret = ar40xx_hw_read_switch_mac_address(sc, 819e388de98SAdrian Chadd &conf->switch_macaddr); 820e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 821e388de98SAdrian Chadd } 822e388de98SAdrian Chadd 823e388de98SAdrian Chadd return (ret); 824e388de98SAdrian Chadd } 825e388de98SAdrian Chadd 826e388de98SAdrian Chadd /* 827e388de98SAdrian Chadd * Flush all ATU entries. 828e388de98SAdrian Chadd */ 829e388de98SAdrian Chadd static int 830e388de98SAdrian Chadd ar40xx_atu_flush_all(device_t dev) 831e388de98SAdrian Chadd { 832e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 833e388de98SAdrian Chadd int ret; 834e388de98SAdrian Chadd 835e388de98SAdrian Chadd AR40XX_LOCK(sc); 836e388de98SAdrian Chadd ret = ar40xx_hw_atu_flush_all(sc); 837e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 838e388de98SAdrian Chadd return (ret); 839e388de98SAdrian Chadd } 840e388de98SAdrian Chadd 841e388de98SAdrian Chadd /* 842e388de98SAdrian Chadd * Flush all ATU entries for the given port. 843e388de98SAdrian Chadd */ 844e388de98SAdrian Chadd static int 845e388de98SAdrian Chadd ar40xx_atu_flush_port(device_t dev, int port) 846e388de98SAdrian Chadd { 847e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 848e388de98SAdrian Chadd int ret; 849e388de98SAdrian Chadd 850e388de98SAdrian Chadd AR40XX_LOCK(sc); 851e388de98SAdrian Chadd ret = ar40xx_hw_atu_flush_port(sc, port); 852e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 853e388de98SAdrian Chadd return (ret); 854e388de98SAdrian Chadd } 855e388de98SAdrian Chadd 856e388de98SAdrian Chadd /* 857e388de98SAdrian Chadd * Load the ATU table into local storage so it can be iterated 858e388de98SAdrian Chadd * over. 859e388de98SAdrian Chadd */ 860e388de98SAdrian Chadd static int 861e388de98SAdrian Chadd ar40xx_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table) 862e388de98SAdrian Chadd { 863e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 864e388de98SAdrian Chadd int err, nitems; 865e388de98SAdrian Chadd 866e388de98SAdrian Chadd memset(&sc->atu.entries, 0, sizeof(sc->atu.entries)); 867e388de98SAdrian Chadd 868e388de98SAdrian Chadd table->es_nitems = 0; 869e388de98SAdrian Chadd nitems = 0; 870e388de98SAdrian Chadd 871e388de98SAdrian Chadd AR40XX_LOCK(sc); 872e388de98SAdrian Chadd sc->atu.count = 0; 873e388de98SAdrian Chadd err = ar40xx_hw_atu_fetch_entry(sc, NULL, 0); 874e388de98SAdrian Chadd if (err != 0) 875e388de98SAdrian Chadd goto done; 876e388de98SAdrian Chadd 877e388de98SAdrian Chadd while (nitems < AR40XX_NUM_ATU_ENTRIES) { 878e388de98SAdrian Chadd err = ar40xx_hw_atu_fetch_entry(sc, 879e388de98SAdrian Chadd &sc->atu.entries[nitems], 1); 880e388de98SAdrian Chadd if (err != 0) 881e388de98SAdrian Chadd goto done; 882e388de98SAdrian Chadd sc->atu.entries[nitems].id = nitems; 883e388de98SAdrian Chadd nitems++; 884e388de98SAdrian Chadd } 885e388de98SAdrian Chadd done: 886e388de98SAdrian Chadd sc->atu.count = nitems; 887e388de98SAdrian Chadd table->es_nitems = nitems; 888e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 889e388de98SAdrian Chadd 890e388de98SAdrian Chadd return (0); 891e388de98SAdrian Chadd } 892e388de98SAdrian Chadd 893e388de98SAdrian Chadd /* 894e388de98SAdrian Chadd * Iterate over the ATU table entries that have been previously 895e388de98SAdrian Chadd * fetched. 896e388de98SAdrian Chadd */ 897e388de98SAdrian Chadd static int 898e388de98SAdrian Chadd ar40xx_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e) 899e388de98SAdrian Chadd { 900e388de98SAdrian Chadd struct ar40xx_softc *sc = device_get_softc(dev); 901e388de98SAdrian Chadd int id, err = 0; 902e388de98SAdrian Chadd 903e388de98SAdrian Chadd id = e->id; 904e388de98SAdrian Chadd AR40XX_LOCK(sc); 905e388de98SAdrian Chadd if (id > sc->atu.count) { 906e388de98SAdrian Chadd err = ENOENT; 907e388de98SAdrian Chadd goto done; 908e388de98SAdrian Chadd } 909e388de98SAdrian Chadd memcpy(e, &sc->atu.entries[id], sizeof(*e)); 910e388de98SAdrian Chadd done: 911e388de98SAdrian Chadd AR40XX_UNLOCK(sc); 912e388de98SAdrian Chadd return (err); 913e388de98SAdrian Chadd } 914e388de98SAdrian Chadd 915e388de98SAdrian Chadd static device_method_t ar40xx_methods[] = { 916e388de98SAdrian Chadd /* Device interface */ 917e388de98SAdrian Chadd DEVMETHOD(device_probe, ar40xx_probe), 918e388de98SAdrian Chadd DEVMETHOD(device_attach, ar40xx_attach), 919e388de98SAdrian Chadd DEVMETHOD(device_detach, ar40xx_detach), 920e388de98SAdrian Chadd 921e388de98SAdrian Chadd /* bus interface */ 922e388de98SAdrian Chadd DEVMETHOD(bus_add_child, device_add_child_ordered), 923e388de98SAdrian Chadd 924e388de98SAdrian Chadd /* MII interface */ 925e388de98SAdrian Chadd DEVMETHOD(miibus_readreg, ar40xx_readphy), 926e388de98SAdrian Chadd DEVMETHOD(miibus_writereg, ar40xx_writephy), 927e388de98SAdrian Chadd DEVMETHOD(miibus_statchg, ar40xx_statchg), 928e388de98SAdrian Chadd 929e388de98SAdrian Chadd /* MDIO interface */ 930e388de98SAdrian Chadd DEVMETHOD(mdio_readreg, ar40xx_readphy), 931e388de98SAdrian Chadd DEVMETHOD(mdio_writereg, ar40xx_writephy), 932e388de98SAdrian Chadd 933e388de98SAdrian Chadd /* etherswitch interface */ 934e388de98SAdrian Chadd DEVMETHOD(etherswitch_lock, ar40xx_lock), 935e388de98SAdrian Chadd DEVMETHOD(etherswitch_unlock, ar40xx_unlock), 936e388de98SAdrian Chadd DEVMETHOD(etherswitch_getinfo, ar40xx_getinfo), 937e388de98SAdrian Chadd DEVMETHOD(etherswitch_readreg, ar40xx_readreg), 938e388de98SAdrian Chadd DEVMETHOD(etherswitch_writereg, ar40xx_writereg), 939e388de98SAdrian Chadd DEVMETHOD(etherswitch_readphyreg, ar40xx_readphy), 940e388de98SAdrian Chadd DEVMETHOD(etherswitch_writephyreg, ar40xx_writephy), 941e388de98SAdrian Chadd DEVMETHOD(etherswitch_getport, ar40xx_getport), 942e388de98SAdrian Chadd DEVMETHOD(etherswitch_setport, ar40xx_setport), 943e388de98SAdrian Chadd DEVMETHOD(etherswitch_getvgroup, ar40xx_getvgroup), 944e388de98SAdrian Chadd DEVMETHOD(etherswitch_setvgroup, ar40xx_setvgroup), 945e388de98SAdrian Chadd DEVMETHOD(etherswitch_getconf, ar40xx_getconf), 946e388de98SAdrian Chadd DEVMETHOD(etherswitch_setconf, ar40xx_setconf), 947e388de98SAdrian Chadd DEVMETHOD(etherswitch_flush_all, ar40xx_atu_flush_all), 948e388de98SAdrian Chadd DEVMETHOD(etherswitch_flush_port, ar40xx_atu_flush_port), 949e388de98SAdrian Chadd DEVMETHOD(etherswitch_fetch_table, ar40xx_atu_fetch_table), 950e388de98SAdrian Chadd DEVMETHOD(etherswitch_fetch_table_entry, 951e388de98SAdrian Chadd ar40xx_atu_fetch_table_entry), 952e388de98SAdrian Chadd 953e388de98SAdrian Chadd DEVMETHOD_END 954e388de98SAdrian Chadd }; 955e388de98SAdrian Chadd 956e388de98SAdrian Chadd DEFINE_CLASS_0(ar40xx, ar40xx_driver, ar40xx_methods, 957e388de98SAdrian Chadd sizeof(struct ar40xx_softc)); 958e388de98SAdrian Chadd 95942726c2fSJohn Baldwin DRIVER_MODULE(ar40xx, simplebus, ar40xx_driver, 0, 0); 96042726c2fSJohn Baldwin DRIVER_MODULE(ar40xx, ofwbus, ar40xx_driver, 0, 0); 9613e38757dSJohn Baldwin DRIVER_MODULE(miibus, ar40xx, miibus_driver, 0, 0); 9628933f7d6SJohn Baldwin DRIVER_MODULE(mdio, ar40xx, mdio_driver, 0, 0); 963829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, ar40xx, etherswitch_driver, 0, 0); 964e388de98SAdrian Chadd MODULE_DEPEND(ar40xx, mdio, 1, 1, 1); 965e388de98SAdrian Chadd MODULE_DEPEND(ar40xx, miibus, 1, 1, 1); 966e388de98SAdrian Chadd MODULE_DEPEND(ar40xx, etherswitch, 1, 1, 1); 967e388de98SAdrian Chadd MODULE_VERSION(ar40xx, 1); 968