1/*- 2 * Copyright (c) 2013 Luiz Otavio O Souza. 3 * Copyright (c) 2011-2012 Stefan Bethke. 4 * Copyright (c) 2012 Adrian Chadd. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 *
|
28 * $FreeBSD: head/sys/dev/etherswitch/arswitch/arswitch_vlans.c 257284 2013-10-28 22:26:03Z glebius $
|
28 * $FreeBSD: head/sys/dev/etherswitch/arswitch/arswitch_vlans.c 262429 2014-02-24 04:44:28Z adrian $ |
29 */ 30 31#include <sys/param.h> 32#include <sys/bus.h> 33#include <sys/errno.h> 34#include <sys/lock.h> 35#include <sys/kernel.h> 36#include <sys/mutex.h> 37#include <sys/systm.h> 38#include <sys/socket.h> 39 40#include <net/if.h> 41#include <dev/mii/mii.h> 42 43#include <dev/etherswitch/etherswitch.h> 44#include <dev/etherswitch/arswitch/arswitchreg.h> 45#include <dev/etherswitch/arswitch/arswitchvar.h> 46#include <dev/etherswitch/arswitch/arswitch_reg.h> 47#include <dev/etherswitch/arswitch/arswitch_vlans.h> 48 49#include "mdio_if.h" 50#include "miibus_if.h" 51#include "etherswitch_if.h" 52 53/* 54 * XXX TODO: teach about the AR933x SoC switch 55 * XXX TODO: teach about the AR934x SoC switch 56 * XXX TODO: teach about the AR8327 external switch 57 */ 58 59static int 60arswitch_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid, 61 uint32_t data) 62{ 63 int err; 64 65 if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, 66 AR8X16_VLAN_ACTIVE, 0, 5)) 67 return (EBUSY); 68 69 /* Load the vlan data if needed. */ 70 if (op == AR8X16_VLAN_OP_LOAD) { 71 err = arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_DATA, 72 (data & AR8X16_VLAN_MEMBER) | AR8X16_VLAN_VALID); 73 if (err) 74 return (err); 75 } 76 77 if (vid != 0) 78 op |= ((vid & ETHERSWITCH_VID_MASK) << AR8X16_VLAN_VID_SHIFT); 79 op |= AR8X16_VLAN_ACTIVE; 80 arswitch_writereg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, op); 81 82 /* Wait for command processing. */ 83 if (arswitch_waitreg(sc->sc_dev, AR8X16_REG_VLAN_CTRL, 84 AR8X16_VLAN_ACTIVE, 0, 5)) 85 return (EBUSY); 86 87 return (0); 88} 89 90static int 91arswitch_flush_dot1q_vlan(struct arswitch_softc *sc) 92{ 93 94 ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 95 return (arswitch_vlan_op(sc, AR8X16_VLAN_OP_FLUSH, 0, 0)); 96} 97 98static int 99arswitch_purge_dot1q_vlan(struct arswitch_softc *sc, int vid) 100{ 101 102 ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 103 return (arswitch_vlan_op(sc, AR8X16_VLAN_OP_PURGE, vid, 0)); 104} 105 106static int 107arswitch_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid) 108{ 109 uint32_t reg; 110 int err; 111 112 ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 113 err = arswitch_vlan_op(sc, AR8X16_VLAN_OP_GET, vid, 0); 114 if (err) 115 return (err); 116 117 reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_VLAN_DATA); 118 if ((reg & AR8X16_VLAN_VALID) == 0) { 119 *ports = 0; 120 return (EINVAL); 121 } 122 reg &= ((1 << (sc->numphys + 1)) - 1); 123 *ports = reg; 124 return (0); 125} 126 127static int 128arswitch_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, int vid) 129{ 130 int err; 131 132 ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 133 err = arswitch_vlan_op(sc, AR8X16_VLAN_OP_LOAD, vid, ports); 134 if (err) 135 return (err); 136 return (0); 137} 138 139static int 140arswitch_get_port_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid) 141{ 142 int port; 143 uint32_t reg; 144 145 ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 146 /* For port based vlans the vlanid is the same as the port index. */ 147 port = vid & ETHERSWITCH_VID_MASK; 148 reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port)); 149 *ports = (reg >> AR8X16_PORT_VLAN_DEST_PORTS_SHIFT); 150 *ports &= AR8X16_VLAN_MEMBER; 151 return (0); 152} 153 154static int 155arswitch_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid) 156{ 157 int err, port; 158 159 ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 160 /* For port based vlans the vlanid is the same as the port index. */ 161 port = vid & ETHERSWITCH_VID_MASK; 162 err = arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port), 163 AR8X16_VLAN_MEMBER << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT, 164 (ports & AR8X16_VLAN_MEMBER) << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT); 165 if (err) 166 return (err); 167 return (0); 168} 169 170/* 171 * Reset vlans to default state. 172 */ 173void
|
174arswitch_reset_vlans(struct arswitch_softc *sc)
|
174ar8xxx_reset_vlans(struct arswitch_softc *sc) |
175{ 176 uint32_t ports; 177 int i, j; 178 179 ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 180 181 ARSWITCH_LOCK(sc); 182 183 /* Reset all vlan data. */ 184 memset(sc->vid, 0, sizeof(sc->vid)); 185 186 /* Disable the QinQ and egress filters for all ports. */ 187 for (i = 0; i <= sc->numphys; i++) { 188 if (arswitch_modifyreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(i), 189 0x3 << AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT | 190 AR8X16_PORT_CTRL_DOUBLE_TAG, 0)) { 191 ARSWITCH_UNLOCK(sc); 192 return; 193 } 194 } 195 196 if (arswitch_flush_dot1q_vlan(sc)) { 197 ARSWITCH_UNLOCK(sc); 198 return; 199 } 200 201 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 202 /* 203 * Reset the port based vlan settings and turn on the 204 * ingress filter for all ports. 205 */ 206 ports = 0; 207 for (i = 0; i <= sc->numphys; i++) 208 arswitch_modifyreg(sc->sc_dev, 209 AR8X16_REG_PORT_VLAN(i), 210 AR8X16_PORT_VLAN_MODE_MASK | 211 AR8X16_VLAN_MEMBER << 212 AR8X16_PORT_VLAN_DEST_PORTS_SHIFT, 213 AR8X16_PORT_VLAN_MODE_SECURE << 214 AR8X16_PORT_VLAN_MODE_SHIFT); 215 216 /* 217 * Setup vlan 1 as PVID for all switch ports. Add all ports 218 * as members of vlan 1. 219 */ 220 sc->vid[0] = 1; 221 /* Set PVID for everyone. */ 222 for (i = 0; i <= sc->numphys; i++)
|
223 arswitch_set_pvid(sc, i, sc->vid[0]);
|
223 sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]); |
224 ports = 0; 225 for (i = 0; i <= sc->numphys; i++) 226 ports |= (1 << i); 227 arswitch_set_dot1q_vlan(sc, ports, sc->vid[0]); 228 sc->vid[0] |= ETHERSWITCH_VID_VALID; 229 } else if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { 230 /* Initialize the port based vlans. */ 231 for (i = 0; i <= sc->numphys; i++) { 232 sc->vid[i] = i | ETHERSWITCH_VID_VALID; 233 ports = 0; 234 for (j = 0; j <= sc->numphys; j++) 235 ports |= (1 << j); 236 arswitch_modifyreg(sc->sc_dev, 237 AR8X16_REG_PORT_VLAN(i), 238 AR8X16_PORT_VLAN_MODE_MASK | 239 AR8X16_VLAN_MEMBER << 240 AR8X16_PORT_VLAN_DEST_PORTS_SHIFT, 241 ports << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT | 242 AR8X16_PORT_VLAN_MODE_SECURE << 243 AR8X16_PORT_VLAN_MODE_PORT_ONLY); 244 } 245 } else { 246 /* Disable the ingress filter and get everyone on all vlans. */ 247 for (i = 0; i <= sc->numphys; i++) 248 arswitch_modifyreg(sc->sc_dev, 249 AR8X16_REG_PORT_VLAN(i), 250 AR8X16_PORT_VLAN_MODE_MASK | 251 AR8X16_VLAN_MEMBER << 252 AR8X16_PORT_VLAN_DEST_PORTS_SHIFT, 253 AR8X16_VLAN_MEMBER << 254 AR8X16_PORT_VLAN_DEST_PORTS_SHIFT | 255 AR8X16_PORT_VLAN_MODE_SECURE << 256 AR8X16_PORT_VLAN_MODE_PORT_ONLY); 257 } 258 ARSWITCH_UNLOCK(sc); 259} 260 261int
|
262arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
|
262ar8xxx_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) |
263{
|
264 struct arswitch_softc *sc;
|
264 int err; 265
|
267 sc = device_get_softc(dev);
|
266 ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 267 268 if (vg->es_vlangroup > sc->info.es_nvlangroups) 269 return (EINVAL); 270 271 /* Reset the members ports. */ 272 vg->es_untagged_ports = 0; 273 vg->es_member_ports = 0; 274 275 /* Not supported. */ 276 vg->es_fid = 0; 277 278 /* Vlan ID. */ 279 ARSWITCH_LOCK(sc); 280 vg->es_vid = sc->vid[vg->es_vlangroup]; 281 if ((vg->es_vid & ETHERSWITCH_VID_VALID) == 0) { 282 ARSWITCH_UNLOCK(sc); 283 return (0); 284 } 285 286 /* Member Ports. */ 287 switch (sc->vlan_mode) { 288 case ETHERSWITCH_VLAN_DOT1Q: 289 err = arswitch_get_dot1q_vlan(sc, &vg->es_member_ports, 290 vg->es_vid); 291 break; 292 case ETHERSWITCH_VLAN_PORT: 293 err = arswitch_get_port_vlan(sc, &vg->es_member_ports, 294 vg->es_vid); 295 break; 296 default: 297 vg->es_member_ports = 0; 298 err = -1; 299 } 300 ARSWITCH_UNLOCK(sc); 301 vg->es_untagged_ports = vg->es_member_ports; 302 return (err); 303} 304 305int
|
308arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
|
306ar8xxx_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) |
307{
|
310 struct arswitch_softc *sc;
|
308 int err, vid; 309
|
313 sc = device_get_softc(dev);
|
310 ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); 311 312 /* Check VLAN mode. */ 313 if (sc->vlan_mode == 0) 314 return (EINVAL); 315 316 /* 317 * Check if we are changing the vlanid for an already used vtu entry. 318 * Then purge the entry first. 319 */ 320 ARSWITCH_LOCK(sc); 321 vid = sc->vid[vg->es_vlangroup]; 322 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q && 323 (vid & ETHERSWITCH_VID_VALID) != 0 && 324 (vid & ETHERSWITCH_VID_MASK) != 325 (vg->es_vid & ETHERSWITCH_VID_MASK)) { 326 err = arswitch_purge_dot1q_vlan(sc, vid); 327 if (err) { 328 ARSWITCH_UNLOCK(sc); 329 return (err); 330 } 331 } 332 333 /* Vlan ID. */ 334 if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { 335 sc->vid[vg->es_vlangroup] = vg->es_vid & ETHERSWITCH_VID_MASK; 336 /* Setting the vlanid to zero disables the vlangroup. */ 337 if (sc->vid[vg->es_vlangroup] == 0) { 338 ARSWITCH_UNLOCK(sc); 339 return (0); 340 } 341 sc->vid[vg->es_vlangroup] |= ETHERSWITCH_VID_VALID; 342 vid = sc->vid[vg->es_vlangroup]; 343 } 344 345 /* Member Ports. */ 346 switch (sc->vlan_mode) { 347 case ETHERSWITCH_VLAN_DOT1Q: 348 err = arswitch_set_dot1q_vlan(sc, vg->es_member_ports, vid); 349 break; 350 case ETHERSWITCH_VLAN_PORT: 351 err = arswitch_set_port_vlan(sc, vg->es_member_ports, vid); 352 break; 353 default: 354 err = -1; 355 } 356 ARSWITCH_UNLOCK(sc); 357 return (err); 358} 359 360int
|
365arswitch_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
|
361ar8xxx_get_pvid(struct arswitch_softc *sc, int port, int *pvid) |
362{ 363 uint32_t reg; 364 365 ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 366 reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_VLAN(port)); 367 *pvid = reg & 0xfff; 368 return (0); 369} 370 371int
|
376arswitch_set_pvid(struct arswitch_softc *sc, int port, int pvid)
|
372ar8xxx_set_pvid(struct arswitch_softc *sc, int port, int pvid) |
373{ 374 375 ARSWITCH_LOCK_ASSERT(sc, MA_OWNED); 376 return (arswitch_modifyreg(sc->sc_dev, 377 AR8X16_REG_PORT_VLAN(port), 0xfff, pvid)); 378}
|