1253572Sloos/*- 2253572Sloos * Copyright (c) 2013 Luiz Otavio O Souza. 3253572Sloos * Copyright (c) 2011-2012 Stefan Bethke. 4253572Sloos * Copyright (c) 2012 Adrian Chadd. 5253572Sloos * All rights reserved. 6253572Sloos * 7253572Sloos * Redistribution and use in source and binary forms, with or without 8253572Sloos * modification, are permitted provided that the following conditions 9253572Sloos * are met: 10253572Sloos * 1. Redistributions of source code must retain the above copyright 11253572Sloos * notice, this list of conditions and the following disclaimer. 12253572Sloos * 2. Redistributions in binary form must reproduce the above copyright 13253572Sloos * notice, this list of conditions and the following disclaimer in the 14253572Sloos * documentation and/or other materials provided with the distribution. 15253572Sloos * 16253572Sloos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17253572Sloos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18253572Sloos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19253572Sloos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20253572Sloos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21253572Sloos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22253572Sloos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23253572Sloos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24253572Sloos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25253572Sloos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26253572Sloos * SUCH DAMAGE. 27253572Sloos * 28253572Sloos * $FreeBSD$ 29253572Sloos */ 30253572Sloos 31253572Sloos#include <sys/param.h> 32253572Sloos#include <sys/bus.h> 33253572Sloos#include <sys/errno.h> 34253572Sloos#include <sys/kernel.h> 35253572Sloos#include <sys/systm.h> 36253572Sloos#include <sys/socket.h> 37253572Sloos 38253572Sloos#include <net/if.h> 39253572Sloos 40253572Sloos#include <dev/mii/mii.h> 41253572Sloos 42253572Sloos#include <dev/etherswitch/etherswitch.h> 43253572Sloos#include <dev/etherswitch/arswitch/arswitchreg.h> 44253572Sloos#include <dev/etherswitch/arswitch/arswitchvar.h> 45253572Sloos#include <dev/etherswitch/arswitch/arswitch_reg.h> 46253572Sloos#include <dev/etherswitch/arswitch/arswitch_vlans.h> 47253572Sloos 48253572Sloos#include "mdio_if.h" 49253572Sloos#include "miibus_if.h" 50253572Sloos#include "etherswitch_if.h" 51253572Sloos 52253572Sloosstatic int 53253572Sloosarswitch_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid, 54253572Sloos uint32_t data) 55253572Sloos{ 56253572Sloos int err; 57253572Sloos 58253572Sloos if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, 59253572Sloos AR8X16_VLAN_ACTIVE, 0, 5)) 60253572Sloos return (EBUSY); 61253572Sloos 62253572Sloos /* Load the vlan data if needed. */ 63253572Sloos if (op == AR8X16_VLAN_OP_LOAD) { 64253572Sloos err = arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_DATA, 65253572Sloos (data & AR8X16_VLAN_MEMBER) | AR8X16_VLAN_VALID); 66253572Sloos if (err) 67253572Sloos return (err); 68253572Sloos } 69253572Sloos 70253572Sloos if (vid != 0) 71253572Sloos op |= ((vid & ETHERSWITCH_VID_MASK) << AR8X16_VLAN_VID_SHIFT); 72253572Sloos op |= AR8X16_VLAN_ACTIVE; 73253572Sloos arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, op); 74253572Sloos 75253572Sloos /* Wait for command processing. */ 76253572Sloos if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, 77253572Sloos AR8X16_VLAN_ACTIVE, 0, 5)) 78253572Sloos return (EBUSY); 79253572Sloos 80253572Sloos return (0); 81253572Sloos} 82253572Sloos 83253572Sloosstatic int 84253572Sloosarswitch_flush_dot1q_vlan(struct arswitch_softc *sc) 85253572Sloos{ 86253572Sloos 87253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 88253572Sloos return (arswitch_vlan_op(sc, AR8X16_VLAN_OP_FLUSH, 0, 0)); 89253572Sloos} 90253572Sloos 91253572Sloosstatic int 92253572Sloosarswitch_purge_dot1q_vlan(struct arswitch_softc *sc, int vid) 93253572Sloos{ 94253572Sloos 95253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 96253572Sloos return (arswitch_vlan_op(sc, AR8X16_VLAN_OP_PURGE, vid, 0)); 97253572Sloos} 98253572Sloos 99253572Sloosstatic int 100253572Sloosarswitch_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid) 101253572Sloos{ 102253572Sloos uint32_t reg; 103253572Sloos int err; 104253572Sloos 105253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 106253572Sloos err = arswitch_vlan_op(sc, AR8X16_VLAN_OP_GET, vid, 0); 107253572Sloos if (err) 108253572Sloos return (err); 109253572Sloos 110253572Sloos reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_VLAN_DATA); 111253572Sloos if ((reg & AR8X16_VLAN_VALID) == 0) { 112253572Sloos *ports = 0; 113253572Sloos return (EINVAL); 114253572Sloos } 115253572Sloos reg &= ((1 << (sc->numphys + 1)) - 1); 116253572Sloos *ports = reg; 117253572Sloos return (0); 118253572Sloos} 119253572Sloos 120253572Sloosstatic int 121253572Sloosarswitch_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, int vid) 122253572Sloos{ 123253572Sloos int err; 124253572Sloos 125253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 126253572Sloos err = arswitch_vlan_op(sc, AR8X16_VLAN_OP_LOAD, vid, ports); 127253572Sloos if (err) 128253572Sloos return (err); 129253572Sloos return (0); 130253572Sloos} 131253572Sloos 132253572Sloosstatic int 133253572Sloosarswitch_get_port_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid) 134253572Sloos{ 135253572Sloos int port; 136253572Sloos uint32_t reg; 137253572Sloos 138253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 139253572Sloos /* For port based vlans the vlanid is the same as the port index. */ 140253572Sloos port = vid & ETHERSWITCH_VID_MASK; 141253572Sloos reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port)); 142253572Sloos *ports = (reg >> AR8X16_PORT_VLAN_DEST_PORTS_SHIFT); 143253572Sloos *ports &= AR8X16_VLAN_MEMBER; 144253572Sloos return (0); 145253572Sloos} 146253572Sloos 147253572Sloosstatic int 148253572Sloosarswitch_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid) 149253572Sloos{ 150253572Sloos int err, port; 151253572Sloos 152253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 153253572Sloos /* For port based vlans the vlanid is the same as the port index. */ 154253572Sloos port = vid & ETHERSWITCH_VID_MASK; 155253572Sloos err = arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port), 156253572Sloos AR8X16_VLAN_MEMBER << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT, 157253572Sloos (ports & AR8X16_VLAN_MEMBER) << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT); 158253572Sloos if (err) 159253572Sloos return (err); 160253572Sloos return (0); 161253572Sloos} 162253572Sloos 163253572Sloos/* 164253572Sloos * Reset vlans to default state. 165253572Sloos */ 166253572Sloosvoid 167253572Sloosarswitch_reset_vlans(struct arswitch_softc *sc) 168253572Sloos{ 169253572Sloos uint32_t ports; 170253572Sloos int i, j; 171253572Sloos 172253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 173253572Sloos 174253572Sloos ARSWITCH_LOCK(sc); 175253572Sloos 176253572Sloos /* Reset all vlan data. */ 177253572Sloos memset(sc->vid, 0, sizeof(sc->vid)); 178253572Sloos 179253572Sloos /* Disable the QinQ and egress filters for all ports. */ 180253572Sloos for (i = 0; i <= sc->numphys; i++) { 181253572Sloos if (arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(i), 182253572Sloos 0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT | 183253572Sloos AR8X16_PORT_CTRL_DOUBLE_TAG, 0)) { 184253572Sloos ARSWITCH_UNLOCK(sc); 185253572Sloos return; 186253572Sloos } 187253572Sloos } 188253572Sloos 189253572Sloos if (arswitch_flush_dot1q_vlan(sc)) { 190253572Sloos ARSWITCH_UNLOCK(sc); 191253572Sloos return; 192253572Sloos } 193253572Sloos 194253572Sloos if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 195253572Sloos /* 196253572Sloos * Reset the port based vlan settings and turn on the 197253572Sloos * ingress filter for all ports. 198253572Sloos */ 199253572Sloos ports = 0; 200253572Sloos for (i = 0; i <= sc->numphys; i++) 201253572Sloos arswitch_modifyreg(sc->sc_dev, 202253572Sloos AR8X16_REG_PORT_VLAN(i), 203253572Sloos AR8X16_PORT_VLAN_MODE_MASK | 204253572Sloos AR8X16_VLAN_MEMBER << 205253572Sloos AR8X16_PORT_VLAN_DEST_PORTS_SHIFT, 206253572Sloos AR8X16_PORT_VLAN_MODE_SECURE << 207253572Sloos AR8X16_PORT_VLAN_MODE_SHIFT); 208253572Sloos 209253572Sloos /* 210253572Sloos * Setup vlan 1 as PVID for all switch ports. Add all ports 211253572Sloos * as members of vlan 1. 212253572Sloos */ 213253572Sloos sc->vid[0] = 1; 214253572Sloos /* Set PVID for everyone. */ 215253572Sloos for (i = 0; i <= sc->numphys; i++) 216253572Sloos arswitch_set_pvid(sc, i, sc->vid[0]); 217253572Sloos ports = 0; 218253572Sloos for (i = 0; i <= sc->numphys; i++) 219253572Sloos ports |= (1 << i); 220253572Sloos arswitch_set_dot1q_vlan(sc, ports, sc->vid[0]); 221253572Sloos sc->vid[0] |= ETHERSWITCH_VID_VALID; 222253572Sloos } else if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { 223253572Sloos /* Initialize the port based vlans. */ 224253572Sloos for (i = 0; i <= sc->numphys; i++) { 225253572Sloos sc->vid[i] = i | ETHERSWITCH_VID_VALID; 226253572Sloos ports = 0; 227253572Sloos for (j = 0; j <= sc->numphys; j++) 228253572Sloos ports |= (1 << j); 229253572Sloos arswitch_modifyreg(sc->sc_dev, 230253572Sloos AR8X16_REG_PORT_VLAN(i), 231253572Sloos AR8X16_PORT_VLAN_MODE_MASK | 232253572Sloos AR8X16_VLAN_MEMBER << 233253572Sloos AR8X16_PORT_VLAN_DEST_PORTS_SHIFT, 234253572Sloos ports << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT | 235253572Sloos AR8X16_PORT_VLAN_MODE_SECURE << 236253572Sloos AR8X16_PORT_VLAN_MODE_PORT_ONLY); 237253572Sloos } 238253572Sloos } else { 239253572Sloos /* Disable the ingress filter and get everyone on all vlans. */ 240253572Sloos for (i = 0; i <= sc->numphys; i++) 241253572Sloos arswitch_modifyreg(sc->sc_dev, 242253572Sloos AR8X16_REG_PORT_VLAN(i), 243253572Sloos AR8X16_PORT_VLAN_MODE_MASK | 244253572Sloos AR8X16_VLAN_MEMBER << 245253572Sloos AR8X16_PORT_VLAN_DEST_PORTS_SHIFT, 246253572Sloos AR8X16_VLAN_MEMBER << 247253572Sloos AR8X16_PORT_VLAN_DEST_PORTS_SHIFT | 248253572Sloos AR8X16_PORT_VLAN_MODE_SECURE << 249253572Sloos AR8X16_PORT_VLAN_MODE_PORT_ONLY); 250253572Sloos } 251253572Sloos ARSWITCH_UNLOCK(sc); 252253572Sloos} 253253572Sloos 254253572Sloosint 255253572Sloosarswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 256253572Sloos{ 257253572Sloos struct arswitch_softc *sc; 258253572Sloos int err; 259253572Sloos 260253572Sloos sc = device_get_softc(dev); 261253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 262253572Sloos 263253572Sloos if (vg->es_vlangroup > sc->info.es_nvlangroups) 264253572Sloos return (EINVAL); 265253572Sloos 266253572Sloos /* Reset the members ports. */ 267253572Sloos vg->es_untagged_ports = 0; 268253572Sloos vg->es_member_ports = 0; 269253572Sloos 270253572Sloos /* Not supported. */ 271253572Sloos vg->es_fid = 0; 272253572Sloos 273253572Sloos /* Vlan ID. */ 274253572Sloos ARSWITCH_LOCK(sc); 275253572Sloos vg->es_vid = sc->vid[vg->es_vlangroup]; 276253572Sloos if ((vg->es_vid & ETHERSWITCH_VID_VALID) == 0) { 277253572Sloos ARSWITCH_UNLOCK(sc); 278253572Sloos return (0); 279253572Sloos } 280253572Sloos 281253572Sloos /* Member Ports. */ 282253572Sloos switch (sc->vlan_mode) { 283253572Sloos case ETHERSWITCH_VLAN_DOT1Q: 284253572Sloos err = arswitch_get_dot1q_vlan(sc, &vg->es_member_ports, 285253572Sloos vg->es_vid); 286253572Sloos break; 287253572Sloos case ETHERSWITCH_VLAN_PORT: 288253572Sloos err = arswitch_get_port_vlan(sc, &vg->es_member_ports, 289253572Sloos vg->es_vid); 290253572Sloos break; 291253572Sloos default: 292253572Sloos vg->es_member_ports = 0; 293253572Sloos err = -1; 294253572Sloos } 295253572Sloos ARSWITCH_UNLOCK(sc); 296253572Sloos vg->es_untagged_ports = vg->es_member_ports; 297253572Sloos return (err); 298253572Sloos} 299253572Sloos 300253572Sloosint 301253572Sloosarswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 302253572Sloos{ 303253572Sloos struct arswitch_softc *sc; 304253572Sloos int err, vid; 305253572Sloos 306253572Sloos sc = device_get_softc(dev); 307253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 308253572Sloos 309253572Sloos /* Check VLAN mode. */ 310253572Sloos if (sc->vlan_mode == 0) 311253572Sloos return (EINVAL); 312253572Sloos 313253572Sloos /* 314253572Sloos * Check if we are changing the vlanid for an already used vtu entry. 315253572Sloos * Then purge the entry first. 316253572Sloos */ 317253572Sloos ARSWITCH_LOCK(sc); 318253572Sloos vid = sc->vid[vg->es_vlangroup]; 319253572Sloos if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q && 320253572Sloos (vid & ETHERSWITCH_VID_VALID) != 0 && 321253572Sloos (vid & ETHERSWITCH_VID_MASK) != 322253572Sloos (vg->es_vid & ETHERSWITCH_VID_MASK)) { 323253572Sloos err = arswitch_purge_dot1q_vlan(sc, vid); 324253572Sloos if (err) { 325253572Sloos ARSWITCH_UNLOCK(sc); 326253572Sloos return (err); 327253572Sloos } 328253572Sloos } 329253572Sloos 330253572Sloos /* Vlan ID. */ 331253572Sloos if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 332253572Sloos sc->vid[vg->es_vlangroup] = vg->es_vid & ETHERSWITCH_VID_MASK; 333253572Sloos /* Setting the vlanid to zero disables the vlangroup. */ 334253572Sloos if (sc->vid[vg->es_vlangroup] == 0) { 335253572Sloos ARSWITCH_UNLOCK(sc); 336253572Sloos return (0); 337253572Sloos } 338253572Sloos sc->vid[vg->es_vlangroup] |= ETHERSWITCH_VID_VALID; 339253572Sloos vid = sc->vid[vg->es_vlangroup]; 340253572Sloos } 341253572Sloos 342253572Sloos /* Member Ports. */ 343253572Sloos switch (sc->vlan_mode) { 344253572Sloos case ETHERSWITCH_VLAN_DOT1Q: 345253572Sloos err = arswitch_set_dot1q_vlan(sc, vg->es_member_ports, vid); 346253572Sloos break; 347253572Sloos case ETHERSWITCH_VLAN_PORT: 348253572Sloos err = arswitch_set_port_vlan(sc, vg->es_member_ports, vid); 349253572Sloos break; 350253572Sloos default: 351253572Sloos err = -1; 352253572Sloos } 353253572Sloos ARSWITCH_UNLOCK(sc); 354253572Sloos return (err); 355253572Sloos} 356253572Sloos 357253572Sloosint 358253572Sloosarswitch_get_pvid(struct arswitch_softc *sc, int port, int *pvid) 359253572Sloos{ 360253572Sloos uint32_t reg; 361253572Sloos 362253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 363253572Sloos reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port)); 364253572Sloos *pvid = reg & 0xfff; 365253572Sloos return (0); 366253572Sloos} 367253572Sloos 368253572Sloosint 369253572Sloosarswitch_set_pvid(struct arswitch_softc *sc, int port, int pvid) 370253572Sloos{ 371253572Sloos 372253572Sloos ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 373253572Sloos return (arswitch_modifyreg(sc->sc_dev, 374253572Sloos AR8X16_REG_PORT_VLAN(port), 0xfff, pvid)); 375253572Sloos} 376