diff options
author | Jo-Philipp Wich <jow@openwrt.org> | 2014-05-19 15:48:18 +0000 |
---|---|---|
committer | Jo-Philipp Wich <jow@openwrt.org> | 2014-05-19 15:48:18 +0000 |
commit | 608c7e8730c4238c567c52232dae42ef0a5ea879 (patch) | |
tree | 720cd5f189412786dec33f5fbe8911af97dac15a /target/linux | |
parent | 9baf77b88d1bd460c13451923a091b7caf420948 (diff) | |
download | upstream-608c7e8730c4238c567c52232dae42ef0a5ea879.tar.gz upstream-608c7e8730c4238c567c52232dae42ef0a5ea879.tar.bz2 upstream-608c7e8730c4238c567c52232dae42ef0a5ea879.zip |
ar71xx: Fix tagged+untagged operation on AR8327N (#12181)
Replace the global "vlan_tagged" variable with an array storing the
tagging state per vlan.
The code was taken from #12181, tested and cleaned up by Saverio Proto
with additional bug fixes supplied by Álvaro Fernández.
Tested-by: Jo-Philipp Wich <jow@openwrt.org>
Signed-off-by: Saverio Proto <zioproto@gmail.com>
Signed-off-by: Álvaro Fernández <noltari@gmail.com>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@40777 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux')
-rw-r--r-- | target/linux/generic/files/drivers/net/phy/ar8216.c | 226 |
1 files changed, 154 insertions, 72 deletions
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 3f60878723..8d69be683e 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -73,12 +73,11 @@ struct ar8xxx_chip { void (*init_globals)(struct ar8xxx_priv *priv); void (*init_port)(struct ar8xxx_priv *priv, int port); - void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 egress, - u32 ingress, u32 members, u32 pvid); + void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 members); u32 (*read_port_status)(struct ar8xxx_priv *priv, int port); int (*atu_flush)(struct ar8xxx_priv *priv); void (*vtu_flush)(struct ar8xxx_priv *priv); - void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask); + void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vlan); const struct ar8xxx_mib_desc *mib_decs; unsigned num_mibs; @@ -112,7 +111,12 @@ struct ar8327_led { enum ar8327_led_pattern pattern; }; +struct ar8216_data { + u8 vlan_tagged; +}; + struct ar8327_data { + u8 vlan_tagged[AR8X16_MAX_VLANS]; u32 port0_status; u32 port6_status; @@ -138,6 +142,7 @@ struct ar8xxx_priv { u8 chip_rev; const struct ar8xxx_chip *chip; union { + struct ar8216_data ar8216; struct ar8327_data ar8327; } chip_data; bool initialized; @@ -159,7 +164,6 @@ struct ar8xxx_priv { bool vlan; u16 vlan_id[AR8X16_MAX_VLANS]; u8 vlan_table[AR8X16_MAX_VLANS]; - u8 vlan_tagged; u16 pvid[AR8X16_MAX_PORTS]; /* mirroring */ @@ -641,7 +645,7 @@ ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb) port = buf[0] & 0xf; /* no need to fix up packets coming from a tagged source */ - if (priv->vlan_tagged & (1 << port)) + if (priv->chip_data.ar8216.vlan_tagged & BIT(port)) return; /* lookup port vid from local table, the switch passes an invalid vlan id */ @@ -695,11 +699,15 @@ ar8216_vtu_flush(struct ar8xxx_priv *priv) } static void -ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) +ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vlan) { u32 op; + u32 vid = priv->vlan_id[vlan]; + u32 port_mask = priv->vlan_table[vlan]; + op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S); + ar8216_vtu_op(priv, op, port_mask); } @@ -722,10 +730,26 @@ ar8216_read_port_status(struct ar8xxx_priv *priv, int port) } static void -ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, - u32 members, u32 pvid) +ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { u32 header; + u32 egress, ingress; + u32 pvid; + + if (priv->vlan) { + pvid = priv->vlan_id[priv->pvid[port]]; + if (priv->chip_data.ar8216.vlan_tagged & BIT(port)) + egress = AR8216_OUT_ADD_VLAN; + else + egress = AR8216_OUT_STRIP_VLAN; + ingress = AR8216_IN_SECURE; + } else { + pvid = port; + egress = AR8216_OUT_KEEP; + ingress = AR8216_IN_PORT_ONLY; + } + + if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU) header = AR8216_PORT_CTRL_HEADER; @@ -807,9 +831,24 @@ static const struct ar8xxx_chip ar8216_chip = { }; static void -ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, - u32 members, u32 pvid) +ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { + u32 egress, ingress; + u32 pvid; + + if (priv->vlan) { + pvid = priv->vlan_id[priv->pvid[port]]; + if (priv->chip_data.ar8216.vlan_tagged & BIT(port)) + egress = AR8216_OUT_ADD_VLAN; + else + egress = AR8216_OUT_STRIP_VLAN; + ingress = AR8216_IN_SECURE; + } else { + pvid = port; + egress = AR8216_OUT_KEEP; + ingress = AR8216_IN_PORT_ONLY; + } + ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | @@ -1696,12 +1735,16 @@ ar8327_vtu_flush(struct ar8xxx_priv *priv) } static void -ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) +ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vlan) { - u32 op; - u32 val; + u32 op, val; int i; + u32 vid = priv->vlan_id[vlan]; + u32 port_mask = priv->vlan_table[vlan]; + u32 tagged = priv->chip_data.ar8327.vlan_tagged[vlan]; + + op = AR8327_VTU_FUNC1_OP_LOAD | (vid << AR8327_VTU_FUNC1_VID_S); val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL; for (i = 0; i < AR8327_NUM_PORTS; i++) { @@ -1711,7 +1754,7 @@ ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) mode = AR8327_VTU_FUNC0_EG_MODE_NOT; else if (priv->vlan == 0) mode = AR8327_VTU_FUNC0_EG_MODE_KEEP; - else if (priv->vlan_tagged & BIT(i)) + else if (tagged & BIT(i)) mode = AR8327_VTU_FUNC0_EG_MODE_TAG; else mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG; @@ -1722,29 +1765,27 @@ ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) } static void -ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, - u32 members, u32 pvid) +ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { + u32 ingress, mode; + u32 pvid; + u32 t; - u32 mode; + + if (priv->vlan) { + pvid = priv->vlan_id[priv->pvid[port]]; + mode = AR8327_PORT_VLAN1_OUT_MODE_UNMOD; + ingress = AR8216_IN_SECURE; + } else { + pvid = port; + mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; + ingress = AR8216_IN_PORT_ONLY; + } t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S; t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S; priv->write(priv, AR8327_REG_PORT_VLAN0(port), t); - mode = AR8327_PORT_VLAN1_OUT_MODE_UNMOD; - switch (egress) { - case AR8216_OUT_KEEP: - mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; - break; - case AR8216_OUT_STRIP_VLAN: - mode = AR8327_PORT_VLAN1_OUT_MODE_UNTAG; - break; - case AR8216_OUT_ADD_VLAN: - mode = AR8327_PORT_VLAN1_OUT_MODE_TAG; - break; - } - t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; t |= mode << AR8327_PORT_VLAN1_OUT_MODE_S; priv->write(priv, AR8327_REG_PORT_VLAN1(port), t); @@ -1842,23 +1883,20 @@ ar8xxx_sw_get_port_link(struct switch_dev *dev, int port, } static int -ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val) +ar8xxx_sw_get_ports(struct switch_val *val, int ports, u8 port_mask, u8 tagged) { - struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - u8 ports = priv->vlan_table[val->port_vlan]; int i; - val->len = 0; - for (i = 0; i < dev->ports; i++) { + for (i = 0; i < ports; i++) { struct switch_port *p; - if (!(ports & (1 << i))) + if (!(port_mask & BIT(i))) continue; p = &val->value.ports[val->len++]; p->id = i; - if (priv->vlan_tagged & (1 << i)) - p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); + if (tagged & BIT(i)) + p->flags = BIT(SWITCH_PORT_FLAG_TAGGED); else p->flags = 0; } @@ -1866,20 +1904,56 @@ ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val) } static int -ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val) +ar8216_sw_get_ports(struct switch_dev *dev, struct switch_val *val) +{ + int ports = dev->ports; + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + u8 port_mask = priv->vlan_table[val->port_vlan]; + u8 tagged = priv->chip_data.ar8216.vlan_tagged; + + return ar8xxx_sw_get_ports(val, ports, port_mask, tagged); +} + +static int +ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val) +{ + int ports = dev->ports; + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + u8 port_mask = priv->vlan_table[val->port_vlan]; + u8 tagged = priv->chip_data.ar8327.vlan_tagged[val->port_vlan]; + + return ar8xxx_sw_get_ports(val, ports, port_mask, tagged); +} + +static int +ar8216_sw_set_ports(struct switch_dev *dev, struct switch_val *val) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); u8 *vt = &priv->vlan_table[val->port_vlan]; + u8 *tagged = &priv->chip_data.ar8216.vlan_tagged; + int i, j; *vt = 0; for (i = 0; i < val->len; i++) { struct switch_port *p = &val->value.ports[i]; - if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) { - priv->vlan_tagged |= (1 << p->id); + if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED)) { + + /* if port was untagged before then + * remove him from other vlans */ + if(*tagged & BIT(p->id)){ + for (j = 0; j < AR8X16_MAX_VLANS; j++) { + if (j == val->port_vlan) + continue; + + priv->vlan_table[j] &= ~(BIT(p->id)); + } + } + + *tagged |= BIT(p->id); } else { - priv->vlan_tagged &= ~(1 << p->id); + *tagged &= ~(BIT(p->id)); priv->pvid[p->id] = val->port_vlan; /* make sure that an untagged port does not @@ -1887,11 +1961,38 @@ ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val) for (j = 0; j < AR8X16_MAX_VLANS; j++) { if (j == val->port_vlan) continue; - priv->vlan_table[j] &= ~(1 << p->id); + + priv->vlan_table[j] &= ~(BIT(p->id)); } } - *vt |= 1 << p->id; + *vt |= BIT(p->id); + } + return 0; +} + +static int +ar8327_sw_set_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + u8 *vt = &priv->vlan_table[val->port_vlan]; + u8 *vlan_tagged = priv->chip_data.ar8327.vlan_tagged; + u8 *tagged = &vlan_tagged[val->port_vlan]; + + int i, j; + + *vt = 0; + *tagged = 0; + for (i = 0; i < val->len; i++) { + struct switch_port *p = &val->value.ports[i]; + + if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED)) { + *tagged |= BIT(p->id); + } else { + priv->pvid[p->id] = val->port_vlan; + } + + *vt |= BIT(p->id); } return 0; } @@ -2010,13 +2111,11 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev) continue; for (i = 0; i < dev->ports; i++) { - u8 mask = (1 << i); + u8 mask = BIT(i); if (vp & mask) portmask[i] |= vp & ~mask; } - - priv->chip->vtu_load_vlan(priv, priv->vlan_id[j], - priv->vlan_table[j]); + priv->chip->vtu_load_vlan(priv, j); } } else { /* vlan disabled: @@ -2025,31 +2124,14 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev) if (i == AR8216_PORT_CPU) continue; - portmask[i] = 1 << AR8216_PORT_CPU; - portmask[AR8216_PORT_CPU] |= (1 << i); + portmask[i] = BIT(AR8216_PORT_CPU); + portmask[AR8216_PORT_CPU] |= BIT(i); } } /* update the port destination mask registers and tag settings */ for (i = 0; i < dev->ports; i++) { - int egress, ingress; - int pvid; - - if (priv->vlan) { - pvid = priv->vlan_id[priv->pvid[i]]; - if (priv->vlan_tagged & (1 << i)) - egress = AR8216_OUT_ADD_VLAN; - else - egress = AR8216_OUT_STRIP_VLAN; - ingress = AR8216_IN_SECURE; - } else { - pvid = i; - egress = AR8216_OUT_KEEP; - ingress = AR8216_IN_PORT_ONLY; - } - - priv->chip->setup_port(priv, i, egress, ingress, portmask[i], - pvid); + priv->chip->setup_port(priv, i, portmask[i]); } ar8xxx_set_mirror_regs(priv); @@ -2433,8 +2515,8 @@ static const struct switch_dev_ops ar8xxx_sw_ops = { }, .get_port_pvid = ar8xxx_sw_get_pvid, .set_port_pvid = ar8xxx_sw_set_pvid, - .get_vlan_ports = ar8xxx_sw_get_ports, - .set_vlan_ports = ar8xxx_sw_set_ports, + .get_vlan_ports = ar8216_sw_get_ports, + .set_vlan_ports = ar8216_sw_set_ports, .apply_config = ar8xxx_sw_hw_apply, .reset_switch = ar8xxx_sw_reset_switch, .get_port_link = ar8xxx_sw_get_port_link, @@ -2455,8 +2537,8 @@ static const struct switch_dev_ops ar8327_sw_ops = { }, .get_port_pvid = ar8xxx_sw_get_pvid, .set_port_pvid = ar8xxx_sw_set_pvid, - .get_vlan_ports = ar8xxx_sw_get_ports, - .set_vlan_ports = ar8xxx_sw_set_ports, + .get_vlan_ports = ar8327_sw_get_ports, + .set_vlan_ports = ar8327_sw_set_ports, .apply_config = ar8xxx_sw_hw_apply, .reset_switch = ar8xxx_sw_reset_switch, .get_port_link = ar8xxx_sw_get_port_link, |