Date: Fri, 13 Mar 2015 02:16:40 +0000 (UTC) From: Adrian Chadd <adrian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r279943 - head/sys/dev/etherswitch/arswitch Message-ID: <201503130216.t2D2Gel2069219@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: adrian Date: Fri Mar 13 02:16:39 2015 New Revision: 279943 URL: https://svnweb.freebsd.org/changeset/base/279943 Log: Commit 802.1q configuration support for the AR8327. This is slightly different to the other switches - the VLAN table (VTU) programs in the vlan port mapping /and/ the port config (tagged, untagged, passthrough, any.) So: * Add VTU operations to program the VTU (vlan table) * abstract out the mirror-disable function so it's .. well, a function. * setup the port to have a dot1q configuration for dot1q - the port security is VLAN (not per-port VLAN) and requires an entry in the VLAN table; * add set_dot1q / get_dot1q to program the VLAN table; * since the tagged/untagged ports are now programmed into the VTU, rather than global - plumb the ports /and/ untagged ports bitmaps through the arswitch API. Tested: * AP135 - QCA9558 SoC + AR8327N switch Modified: head/sys/dev/etherswitch/arswitch/arswitch_8327.c head/sys/dev/etherswitch/arswitch/arswitch_vlans.c head/sys/dev/etherswitch/arswitch/arswitch_vlans.h head/sys/dev/etherswitch/arswitch/arswitchvar.h Modified: head/sys/dev/etherswitch/arswitch/arswitch_8327.c ============================================================================== --- head/sys/dev/etherswitch/arswitch/arswitch_8327.c Fri Mar 13 01:18:46 2015 (r279942) +++ head/sys/dev/etherswitch/arswitch/arswitch_8327.c Fri Mar 13 02:16:39 2015 (r279943) @@ -66,6 +66,51 @@ #include "miibus_if.h" #include "etherswitch_if.h" + +static int +ar8327_vlan_op(struct arswitch_softc *sc, uint32_t op, uint32_t vid, + uint32_t data) +{ + int err; + + /* + * Wait for the "done" bit to finish. + */ + if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1, + AR8327_VTU_FUNC1_BUSY, 0, 5)) + return (EBUSY); + + /* + * If it's a "load" operation, then ensure 'data' is loaded + * in first. + */ + if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD) { + err = arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC0, data); + if (err) + return (err); + } + + /* + * Set the VID. + */ + op |= ((vid & 0xfff) << AR8327_VTU_FUNC1_VID_S); + + /* + * Set busy bit to start loading in the command. + */ + op |= AR8327_VTU_FUNC1_BUSY; + arswitch_writereg(sc->sc_dev, AR8327_REG_VTU_FUNC1, op); + + /* + * Finally - wait for it to load. + */ + if (arswitch_waitreg(sc->sc_dev, AR8327_REG_VTU_FUNC1, + AR8327_VTU_FUNC1_BUSY, 0, 5)) + return (EBUSY); + + return (0); +} + static void ar8327_phy_fixup(struct arswitch_softc *sc, int phy) { @@ -742,7 +787,7 @@ ar8327_port_vlan_get(struct arswitch_sof /* Retrieve the PVID */ sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid); - /* Retrieve the current port configuration */ + /* Retrieve the current port configuration from the VTU */ /* * DOUBLE_TAG * VLAN_MODE_ADD @@ -754,10 +799,24 @@ ar8327_port_vlan_get(struct arswitch_sof } static void +ar8327_port_disable_mirror(struct arswitch_softc *sc, int port) +{ + + arswitch_modifyreg(sc->sc_dev, + AR8327_REG_PORT_LOOKUP(port), + AR8327_PORT_LOOKUP_ING_MIRROR_EN, + 0); + arswitch_modifyreg(sc->sc_dev, + AR8327_REG_PORT_HOL_CTRL1(port), + AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, + 0); +} + +static void ar8327_reset_vlans(struct arswitch_softc *sc) { int i; - uint32_t mode, t; + uint32_t t; int ports; ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED); @@ -787,43 +846,66 @@ ar8327_reset_vlans(struct arswitch_softc */ ports = 0x7f; + /* + * XXX TODO: set things up correctly for vlans! + */ for (i = 0; i < AR8327_NUM_PORTS; i++) { + int egress, ingress; - if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) + if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { sc->vid[i] = i | ETHERSWITCH_VID_VALID; + /* set egress == out_keep */ + ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY; + /* in_port_only, forward */ + egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; + } else if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { + ingress = AR8X16_PORT_VLAN_MODE_SECURE; + egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD; + } else { + /* set egress == out_keep */ + ingress = AR8X16_PORT_VLAN_MODE_PORT_ONLY; + /* in_port_only, forward */ + egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; + } /* set pvid = 1; there's only one vlangroup to start with */ t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(i), t); - /* set egress == out_keep */ - mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; - t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; - t |= mode << AR8327_PORT_VLAN1_OUT_MODE_S; + t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S; arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(i), t); /* Ports can see other ports */ + /* XXX not entirely true for dot1q? */ t = (ports & ~(1 << i)); /* all ports besides us */ t |= AR8327_PORT_LOOKUP_LEARN; - /* in_port_only, forward */ - t |= AR8X16_PORT_VLAN_MODE_PORT_ONLY << AR8327_PORT_LOOKUP_IN_MODE_S; + t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S; t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(i), t); + } - /* - * Disable port mirroring entirely. - */ - arswitch_modifyreg(sc->sc_dev, - AR8327_REG_PORT_LOOKUP(i), - AR8327_PORT_LOOKUP_ING_MIRROR_EN, - 0); - arswitch_modifyreg(sc->sc_dev, - AR8327_REG_PORT_HOL_CTRL1(i), - AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, - 0); + /* + * Disable port mirroring entirely. + */ + for (i = 0; i < AR8327_NUM_PORTS; i++) { + ar8327_port_disable_mirror(sc, i); + } + + /* + * If dot1q - set pvid; dot1q, etc. + */ + sc->vid[0] = 1; + if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) { + for (i = 0; i < AR8327_NUM_PORTS; i++) { + /* Each port - pvid 1 */ + sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]); + } + /* Initialise vlan1 - all ports, untagged */ + sc->hal.arswitch_set_dot1q_vlan(sc, ports, ports, sc->vid[0]); + sc->vid[0] |= ETHERSWITCH_VID_VALID; } ARSWITCH_UNLOCK(sc); @@ -867,9 +949,6 @@ static int ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) { - /* XXX for now, no dot1q vlans */ - if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) - return (EINVAL); return (ar8xxx_getvgroup(sc, vg)); } @@ -877,9 +956,6 @@ static int ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg) { - /* XXX for now, no dot1q vlans */ - if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) - return (EINVAL); return (ar8xxx_setvgroup(sc, vg)); } @@ -939,6 +1015,98 @@ ar8327_atu_flush(struct arswitch_softc * return (ret); } +static int +ar8327_flush_dot1q_vlan(struct arswitch_softc *sc) +{ + + return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_FLUSH, 0, 0)); +} + +static int +ar8327_purge_dot1q_vlan(struct arswitch_softc *sc, int vid) +{ + + return (ar8327_vlan_op(sc, AR8327_VTU_FUNC1_OP_PURGE, vid, 0)); +} + +static int +ar8327_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, + uint32_t *untagged_ports, int vid) +{ + int i, r; + uint32_t op, reg, val; + + op = AR8327_VTU_FUNC1_OP_GET_ONE; + + /* Filter out the vid flags; only grab the VLAN ID */ + vid &= 0xfff; + + /* XXX TODO: the VTU here stores egress mode - keep, tag, untagged, none */ + r = ar8327_vlan_op(sc, op, vid, 0); + if (r != 0) { + device_printf(sc->sc_dev, "%s: %d: op failed\n", __func__, vid); + } + + reg = arswitch_readreg(sc->sc_dev, AR8327_REG_VTU_FUNC0); + DPRINTF(sc->sc_dev, "%s: %d: reg=0x%08x\n", __func__, vid, reg); + + /* + * If any of the bits are set, update the port mask. + * Worry about the port config itself when getport() is called. + */ + *ports = 0; + for (i = 0; i < AR8327_NUM_PORTS; i++) { + val = reg >> AR8327_VTU_FUNC0_EG_MODE_S(i); + val = val & 0x3; + /* XXX KEEP (unmodified?) */ + if (val == AR8327_VTU_FUNC0_EG_MODE_TAG) { + *ports |= (1 << i); + } else if (val == AR8327_VTU_FUNC0_EG_MODE_UNTAG) { + *ports |= (1 << i); + *untagged_ports |= (1 << i); + } + } + + return (0); +} + +static int +ar8327_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, + uint32_t untagged_ports, int vid) +{ + int i; + uint32_t op, val, mode; + + op = AR8327_VTU_FUNC1_OP_LOAD; + vid &= 0xfff; + + DPRINTF(sc->sc_dev, + "%s: vid: %d, ports=0x%08x, untagged_ports=0x%08x\n", + __func__, + vid, + ports, + untagged_ports); + + /* + * Mark it as valid; and that it should use per-VLAN MAC table, + * not VID=0 when doing MAC lookups + */ + val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL; + + for (i = 0; i < AR8327_NUM_PORTS; i++) { + if ((ports & BIT(i)) == 0) + mode = AR8327_VTU_FUNC0_EG_MODE_NOT; + else if (untagged_ports & BIT(i)) + mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG; + else + mode = AR8327_VTU_FUNC0_EG_MODE_TAG; + + val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i); + } + + return (ar8327_vlan_op(sc, op, vid, val)); +} + void ar8327_attach(struct arswitch_softc *sc) { @@ -952,6 +1120,10 @@ ar8327_attach(struct arswitch_softc *sc) sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup; sc->hal.arswitch_port_vlan_setup = ar8327_port_vlan_setup; sc->hal.arswitch_port_vlan_get = ar8327_port_vlan_get; + sc->hal.arswitch_flush_dot1q_vlan = ar8327_flush_dot1q_vlan; + sc->hal.arswitch_purge_dot1q_vlan = ar8327_purge_dot1q_vlan; + sc->hal.arswitch_set_dot1q_vlan = ar8327_set_dot1q_vlan; + sc->hal.arswitch_get_dot1q_vlan = ar8327_get_dot1q_vlan; sc->hal.arswitch_vlan_init_hw = ar8327_reset_vlans; sc->hal.arswitch_vlan_get_pvid = ar8327_get_pvid; Modified: head/sys/dev/etherswitch/arswitch/arswitch_vlans.c ============================================================================== --- head/sys/dev/etherswitch/arswitch/arswitch_vlans.c Fri Mar 13 01:18:46 2015 (r279942) +++ head/sys/dev/etherswitch/arswitch/arswitch_vlans.c Fri Mar 13 02:16:39 2015 (r279943) @@ -103,7 +103,8 @@ ar8xxx_purge_dot1q_vlan(struct arswitch_ } int -ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid) +ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, + uint32_t *untagged_ports, int vid) { uint32_t reg; int err; @@ -120,11 +121,13 @@ ar8xxx_get_dot1q_vlan(struct arswitch_so } reg &= ((1 << (sc->numphys + 1)) - 1); *ports = reg; + *untagged_ports = reg; return (0); } int -ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, int vid) +ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, + uint32_t untagged_ports, int vid) { int err; @@ -223,7 +226,7 @@ ar8xxx_reset_vlans(struct arswitch_softc ports = 0; for (i = 0; i <= sc->numphys; i++) ports |= (1 << i); - sc->hal.arswitch_set_dot1q_vlan(sc, ports, sc->vid[0]); + sc->hal.arswitch_set_dot1q_vlan(sc, ports, sc->vid[0], sc->vid[0]); sc->vid[0] |= ETHERSWITCH_VID_VALID; } else if (sc->vlan_mode == ETHERSWITCH_VLAN_PORT) { /* Initialize the port based vlans. */ @@ -240,6 +243,7 @@ ar8xxx_reset_vlans(struct arswitch_softc ports << AR8X16_PORT_VLAN_DEST_PORTS_SHIFT | AR8X16_PORT_VLAN_MODE_SECURE << AR8X16_PORT_VLAN_MODE_PORT_ONLY); + /* XXX TODO: SECURE / PORT_ONLY is wrong? */ } } else { /* Disable the ingress filter and get everyone on all vlans. */ @@ -286,18 +290,21 @@ ar8xxx_getvgroup(struct arswitch_softc * switch (sc->vlan_mode) { case ETHERSWITCH_VLAN_DOT1Q: err = sc->hal.arswitch_get_dot1q_vlan(sc, &vg->es_member_ports, + &vg->es_untagged_ports, vg->es_vid); break; case ETHERSWITCH_VLAN_PORT: err = sc->hal.arswitch_get_port_vlan(sc, &vg->es_member_ports, vg->es_vid); + vg->es_untagged_ports = vg->es_member_ports; break; default: vg->es_member_ports = 0; + vg->es_untagged_ports = 0; err = -1; } ARSWITCH_UNLOCK(sc); - vg->es_untagged_ports = vg->es_member_ports; + return (err); } @@ -344,7 +351,8 @@ ar8xxx_setvgroup(struct arswitch_softc * /* Member Ports. */ switch (sc->vlan_mode) { case ETHERSWITCH_VLAN_DOT1Q: - err = sc->hal.arswitch_set_dot1q_vlan(sc, vg->es_member_ports, vid); + err = sc->hal.arswitch_set_dot1q_vlan(sc, vg->es_member_ports, + vg->es_untagged_ports, vid); break; case ETHERSWITCH_VLAN_PORT: err = sc->hal.arswitch_set_port_vlan(sc, vg->es_member_ports, vid); Modified: head/sys/dev/etherswitch/arswitch/arswitch_vlans.h ============================================================================== --- head/sys/dev/etherswitch/arswitch/arswitch_vlans.h Fri Mar 13 01:18:46 2015 (r279942) +++ head/sys/dev/etherswitch/arswitch/arswitch_vlans.h Fri Mar 13 02:16:39 2015 (r279943) @@ -37,8 +37,10 @@ int ar8xxx_set_pvid(struct arswitch_soft int ar8xxx_flush_dot1q_vlan(struct arswitch_softc *sc); int ar8xxx_purge_dot1q_vlan(struct arswitch_softc *sc, int vid); -int ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid); -int ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, int vid); +int ar8xxx_get_dot1q_vlan(struct arswitch_softc *sc, uint32_t *ports, + uint32_t *untagged_ports, int vid); +int ar8xxx_set_dot1q_vlan(struct arswitch_softc *sc, uint32_t ports, + uint32_t untagged_ports, int vid); int ar8xxx_get_port_vlan(struct arswitch_softc *sc, uint32_t *ports, int vid); int ar8xxx_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid); Modified: head/sys/dev/etherswitch/arswitch/arswitchvar.h ============================================================================== --- head/sys/dev/etherswitch/arswitch/arswitchvar.h Fri Mar 13 01:18:46 2015 (r279942) +++ head/sys/dev/etherswitch/arswitch/arswitchvar.h Fri Mar 13 02:16:39 2015 (r279943) @@ -103,9 +103,9 @@ struct arswitch_softc { int (* arswitch_purge_dot1q_vlan) (struct arswitch_softc *sc, int vid); int (* arswitch_get_dot1q_vlan) (struct arswitch_softc *, - uint32_t *ports, int vid); + uint32_t *ports, uint32_t *untagged_ports, int vid); int (* arswitch_set_dot1q_vlan) (struct arswitch_softc *sc, - uint32_t ports, int vid); + uint32_t ports, uint32_t untagged_ports, int vid); int (* arswitch_get_port_vlan) (struct arswitch_softc *sc, uint32_t *ports, int vid); int (* arswitch_set_port_vlan) (struct arswitch_softc *sc,
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201503130216.t2D2Gel2069219>