This add support for IGMP Snooping on atheros switches (enabled by default), which avoids flooding the network with multicast data.
Tested on TL-WDR4300: disabling IGMP Snooping results in multicast flooding on each specific port, enabling it back again prevents each port from receiving all multicast packets. Partially based on: http://patchwork.ozlabs.org/patch/418122/ Signed-off-by: Álvaro Fernández Rojas <nolt...@gmail.com> --- .../linux/generic/files/drivers/net/phy/ar8216.h | 2 + .../linux/generic/files/drivers/net/phy/ar8327.c | 139 ++++++++++++++++++++- .../linux/generic/files/drivers/net/phy/ar8327.h | 55 ++++++++ 3 files changed, 194 insertions(+), 2 deletions(-) diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h index 14fe928..616c54f 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.h +++ b/target/linux/generic/files/drivers/net/phy/ar8216.h @@ -406,6 +406,8 @@ struct ar8xxx_chip { void (*get_arl_entry)(struct ar8xxx_priv *priv, struct arl_entry *a, u32 *status, enum arl_op op); int (*sw_hw_apply)(struct switch_dev *dev); + int (*igmp_port_get)(struct ar8xxx_priv *priv, int port); + void (*igmp_port_set)(struct ar8xxx_priv *priv, int port, int enable); const struct ar8xxx_mib_desc *mib_decs; unsigned num_mibs; diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.c b/target/linux/generic/files/drivers/net/phy/ar8327.c index 90ee411..5f6950a 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8327.c +++ b/target/linux/generic/files/drivers/net/phy/ar8327.c @@ -662,8 +662,8 @@ ar8327_init_globals(struct ar8xxx_priv *priv) /* forward multicast and broadcast frames to CPU */ t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) | - (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) | - (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S); + (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S) | + (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_IGMP_S); ar8xxx_write(priv, AR8327_REG_FWD_CTRL1, t); /* enable jumbo frames */ @@ -677,6 +677,19 @@ ar8327_init_globals(struct ar8xxx_priv *priv) /* Disable EEE on all phy's due to stability issues */ for (i = 0; i < AR8XXX_NUM_PHYS; i++) data->eee[i] = false; + + /* Enable IGMP Join/Leave */ + t = AR8327_IGMP_JOIN_EN0 | AR8327_IGMP_LEAVE_EN0 | + AR8327_IGMP_JOIN_EN1 | AR8327_IGMP_LEAVE_EN1 | + AR8327_IGMP_JOIN_EN2 | AR8327_IGMP_LEAVE_EN2 | + AR8327_IGMP_JOIN_EN3 | AR8327_IGMP_LEAVE_EN3; + ar8xxx_rmw(priv, AR8327_REG_FRAM_ACK_CTRL0, 0, t); + + t = AR8327_IGMP_JOIN_EN4 | AR8327_IGMP_LEAVE_EN4 | + AR8327_IGMP_JOIN_EN5 | AR8327_IGMP_LEAVE_EN5 | + AR8327_IGMP_JOIN_EN6 | AR8327_IGMP_LEAVE_EN6 | + AR8327_IGMP_V3_EN; + ar8xxx_rmw(priv, AR8327_REG_FRAM_ACK_CTRL1, 0, t); } static void @@ -783,6 +796,78 @@ ar8327_atu_flush_port(struct ar8xxx_priv *priv, int port) return ret; } +static int +ar8327_igmp_port_reg(int port) +{ + if (port > 3) + return AR8327_REG_FRAM_ACK_CTRL1; + else + return AR8327_REG_FRAM_ACK_CTRL0; +} + +static int +ar8327_igmp_port_fast_join_leave(int port) +{ + int ret; + + switch (port) { + case 0: + ret = AR8327_IGMP_JOIN_EN0 | AR8327_IGMP_LEAVE_EN0; + break; + case 1: + ret = AR8327_IGMP_JOIN_EN1 | AR8327_IGMP_LEAVE_EN1; + break; + case 2: + ret = AR8327_IGMP_JOIN_EN2 | AR8327_IGMP_LEAVE_EN2; + break; + case 3: + ret = AR8327_IGMP_JOIN_EN3 | AR8327_IGMP_LEAVE_EN3; + break; + case 4: + ret = AR8327_IGMP_JOIN_EN4 | AR8327_IGMP_LEAVE_EN4; + break; + case 5: + ret = AR8327_IGMP_JOIN_EN5 | AR8327_IGMP_LEAVE_EN5; + break; + case 6: + ret = AR8327_IGMP_JOIN_EN6 | AR8327_IGMP_LEAVE_EN6; + break; + } + + return ret; +} + +static int +ar8327_igmp_port_get(struct ar8xxx_priv *priv, int port) +{ + u32 val = ar8xxx_read(priv, AR8327_REG_FWD_CTRL1) >> + AR8327_FWD_CTRL1_IGMP_S; + if ((val & BIT(port)) != 0) + return 1; + else + return 0; +} + +static void +ar8327_igmp_port_set(struct ar8xxx_priv *priv, int port, int enable) +{ + if (enable) { + ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1, + BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S, + BIT(port) << AR8327_FWD_CTRL1_IGMP_S); + + ar8xxx_rmw(priv, ar8327_igmp_port_reg(port), 0, + ar8327_igmp_port_fast_join_leave(port)); + } else { + ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1, + BIT(port) << AR8327_FWD_CTRL1_IGMP_S, + BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S); + + ar8xxx_rmw(priv, ar8327_igmp_port_reg(port), + ar8327_igmp_port_fast_join_leave(port), 0); + } +} + static void ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) { @@ -1084,6 +1169,44 @@ ar8327_sw_hw_apply(struct switch_dev *dev) return 0; } +int +ar8327_sw_get_igmp_snooping(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + int port; + + port = val->port_vlan; + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->reg_mutex); + val->value.i = priv->chip->igmp_port_get(priv, port); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +int +ar8327_sw_set_igmp_snooping(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + int port; + + port = val->port_vlan; + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->reg_mutex); + priv->chip->igmp_port_set(priv, port, val->value.i); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + static const struct switch_attr ar8327_sw_attr_globals[] = { { .type = SWITCH_TYPE_INT, @@ -1174,6 +1297,14 @@ static const struct switch_attr ar8327_sw_attr_port[] = { .description = "Flush port's ARL table entries", .set = ar8xxx_sw_set_flush_port_arl_table, }, + { + .type = SWITCH_TYPE_INT, + .name = "igmp_snooping", + .description = "Enable port's IGMP Snooping", + .set = ar8327_sw_set_igmp_snooping, + .get = ar8327_sw_get_igmp_snooping, + .max = 1 + }, }; static const struct switch_dev_ops ar8327_sw_ops = { @@ -1226,6 +1357,8 @@ const struct ar8xxx_chip ar8327_chip = { .set_mirror_regs = ar8327_set_mirror_regs, .get_arl_entry = ar8327_get_arl_entry, .sw_hw_apply = ar8327_sw_hw_apply, + .igmp_port_get = ar8327_igmp_port_get, + .igmp_port_set = ar8327_igmp_port_set, .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, @@ -1260,6 +1393,8 @@ const struct ar8xxx_chip ar8337_chip = { .set_mirror_regs = ar8327_set_mirror_regs, .get_arl_entry = ar8327_get_arl_entry, .sw_hw_apply = ar8327_sw_hw_apply, + .igmp_port_get = ar8327_igmp_port_get, + .igmp_port_set = ar8327_igmp_port_set, .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.h b/target/linux/generic/files/drivers/net/phy/ar8327.h index 8d1fb3b..208e9ea 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8327.h +++ b/target/linux/generic/files/drivers/net/phy/ar8327.h @@ -98,6 +98,61 @@ #define AR8327_REG_EEE_CTRL 0x100 #define AR8327_EEE_CTRL_DISABLE_PHY(_i) BIT(4 + (_i) * 2) +#define AR8327_REG_FRAM_ACK_CTRL0 0x210 +#define AR8327_IGMP_MLD_EN0 BIT(0) +#define AR8327_IGMP_JOIN_EN0 BIT(1) +#define AR8327_IGMP_LEAVE_EN0 BIT(2) +#define AR8327_EAPOL_EN0 BIT(3) +#define AR8327_DHCP_EN0 BIT(4) +#define AR8327_ARP_ACK_EN0 BIT(5) +#define AR8327_ARP_REQ_EN0 BIT(6) +#define AR8327_IGMP_MLD_EN1 BIT(8) +#define AR8327_IGMP_JOIN_EN1 BIT(9) +#define AR8327_IGMP_LEAVE_EN1 BIT(10) +#define AR8327_EAPOL_EN1 BIT(11) +#define AR8327_DHCP_EN1 BIT(12) +#define AR8327_ARP_ACK_EN1 BIT(13) +#define AR8327_ARP_REQ_EN1 BIT(14) +#define AR8327_IGMP_MLD_EN2 BIT(16) +#define AR8327_IGMP_JOIN_EN2 BIT(17) +#define AR8327_IGMP_LEAVE_EN2 BIT(18) +#define AR8327_EAPOL_EN2 BIT(19) +#define AR8327_DHCP_EN2 BIT(20) +#define AR8327_ARP_ACK_EN2 BIT(21) +#define AR8327_ARP_REQ_EN2 BIT(22) +#define AR8327_IGMP_MLD_EN3 BIT(24) +#define AR8327_IGMP_JOIN_EN3 BIT(25) +#define AR8327_IGMP_LEAVE_EN3 BIT(26) +#define AR8327_EAPOL_EN3 BIT(27) +#define AR8327_DHCP_EN3 BIT(28) +#define AR8327_ARP_ACK_EN3 BIT(29) +#define AR8327_ARP_REQ_EN3 BIT(30) + +#define AR8327_REG_FRAM_ACK_CTRL1 0x214 +#define AR8327_IGMP_MLD_EN4 BIT(0) +#define AR8327_IGMP_JOIN_EN4 BIT(1) +#define AR8327_IGMP_LEAVE_EN4 BIT(2) +#define AR8327_EAPOL_EN4 BIT(3) +#define AR8327_DHCP_EN4 BIT(4) +#define AR8327_ARP_ACK_EN4 BIT(5) +#define AR8327_ARP_REQ_EN4 BIT(6) +#define AR8327_IGMP_MLD_EN5 BIT(8) +#define AR8327_IGMP_JOIN_EN5 BIT(9) +#define AR8327_IGMP_LEAVE_EN5 BIT(10) +#define AR8327_EAPOL_EN5 BIT(11) +#define AR8327_DHCP_EN5 BIT(12) +#define AR8327_ARP_ACK_EN5 BIT(13) +#define AR8327_ARP_REQ_EN5 BIT(14) +#define AR8327_IGMP_MLD_EN6 BIT(16) +#define AR8327_IGMP_JOIN_EN6 BIT(17) +#define AR8327_IGMP_LEAVE_EN6 BIT(18) +#define AR8327_EAPOL_EN6 BIT(19) +#define AR8327_DHCP_EN6 BIT(20) +#define AR8327_ARP_ACK_EN6 BIT(21) +#define AR8327_ARP_REQ_EN6 BIT(22) +#define AR8327_IGMP_V3_EN BIT(24) +#define AR8327_PPPOE_EN BIT(25) + #define AR8327_REG_PORT_VLAN0(_i) (0x420 + (_i) * 0x8) #define AR8327_PORT_VLAN0_DEF_SVID BITS(0, 12) #define AR8327_PORT_VLAN0_DEF_SVID_S 0 -- 1.9.1 _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel