From f8d2ec6e9db8c1a6659eefb4d4d91c01b9a3e0c5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 17 Jan 2016 10:42:46 +0000 Subject: [PATCH] ar8327: add IGMP Snooping support MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This add support for IGMP Snooping on atheros switches (disabled 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 SVN-Revision: 48268 --- .../generic/files/drivers/net/phy/ar8327.c | 167 ++++++++++++++++++ .../generic/files/drivers/net/phy/ar8327.h | 65 +++++++ 2 files changed, 232 insertions(+) diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.c b/target/linux/generic/files/drivers/net/phy/ar8327.c index 90ee4115c0..02fd2e77bf 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8327.c +++ b/target/linux/generic/files/drivers/net/phy/ar8327.c @@ -783,6 +783,45 @@ ar8327_atu_flush_port(struct ar8xxx_priv *priv, int port) return ret; } +static int +ar8327_get_port_igmp(struct ar8xxx_priv *priv, int port) +{ + u32 fwd_ctrl, frame_ack; + + fwd_ctrl = (BIT(port) << AR8327_FWD_CTRL1_IGMP_S); + frame_ack = ((AR8327_FRAME_ACK_CTRL_IGMP_MLD | + AR8327_FRAME_ACK_CTRL_IGMP_JOIN | + AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) << + AR8327_FRAME_ACK_CTRL_S(port)); + + return (ar8xxx_read(priv, AR8327_REG_FWD_CTRL1) & + fwd_ctrl) == fwd_ctrl && + (ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL(port)) & + frame_ack) == frame_ack; +} + +static void +ar8327_set_port_igmp(struct ar8xxx_priv *priv, int port, int enable) +{ + int reg_frame_ack = AR8327_REG_FRAME_ACK_CTRL(port); + u32 val_frame_ack = (AR8327_FRAME_ACK_CTRL_IGMP_MLD | + AR8327_FRAME_ACK_CTRL_IGMP_JOIN | + AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) << + AR8327_FRAME_ACK_CTRL_S(port); + + 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_reg_set(priv, reg_frame_ack, val_frame_ack); + } else { + ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1, + BIT(port) << AR8327_FWD_CTRL1_IGMP_S, + BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S); + ar8xxx_reg_clear(priv, reg_frame_ack, val_frame_ack); + } +} + static void ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) { @@ -1084,6 +1123,110 @@ ar8327_sw_hw_apply(struct switch_dev *dev) return 0; } +int +ar8327_sw_get_port_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 = val->port_vlan; + + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->reg_mutex); + val->value.i = ar8327_get_port_igmp(priv, port); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +int +ar8327_sw_set_port_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 = val->port_vlan; + + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->reg_mutex); + ar8327_set_port_igmp(priv, port, val->value.i); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +int +ar8327_sw_get_igmp_snooping(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + int port; + + for (port = 0; port < dev->ports; port++) { + val->port_vlan = port; + if (ar8327_sw_get_port_igmp_snooping(dev, attr, val) || + !val->value.i) + break; + } + + return 0; +} + +int +ar8327_sw_set_igmp_snooping(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + int port; + + for (port = 0; port < dev->ports; port++) { + val->port_vlan = port; + if (ar8327_sw_set_port_igmp_snooping(dev, attr, val)) + break; + } + + return 0; +} + +int +ar8327_sw_get_igmp_v3(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + u32 val_reg; + + mutex_lock(&priv->reg_mutex); + val_reg = ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL1); + val->value.i = ((val_reg & AR8327_FRAME_ACK_CTRL_IGMP_V3_EN) != 0); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +int +ar8327_sw_set_igmp_v3(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + + mutex_lock(&priv->reg_mutex); + if (val->value.i) + ar8xxx_reg_set(priv, AR8327_REG_FRAME_ACK_CTRL1, + AR8327_FRAME_ACK_CTRL_IGMP_V3_EN); + else + ar8xxx_reg_clear(priv, AR8327_REG_FRAME_ACK_CTRL1, + AR8327_FRAME_ACK_CTRL_IGMP_V3_EN); + mutex_unlock(&priv->reg_mutex); + + return 0; +} + static const struct switch_attr ar8327_sw_attr_globals[] = { { .type = SWITCH_TYPE_INT, @@ -1144,6 +1287,22 @@ static const struct switch_attr ar8327_sw_attr_globals[] = { .description = "Flush ARL table", .set = ar8xxx_sw_set_flush_arl_table, }, + { + .type = SWITCH_TYPE_INT, + .name = "igmp_snooping", + .description = "Enable IGMP Snooping", + .set = ar8327_sw_set_igmp_snooping, + .get = ar8327_sw_get_igmp_snooping, + .max = 1 + }, + { + .type = SWITCH_TYPE_INT, + .name = "igmp_v3", + .description = "Enable IGMPv3 support", + .set = ar8327_sw_set_igmp_v3, + .get = ar8327_sw_get_igmp_v3, + .max = 1 + }, }; static const struct switch_attr ar8327_sw_attr_port[] = { @@ -1174,6 +1333,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_port_igmp_snooping, + .get = ar8327_sw_get_port_igmp_snooping, + .max = 1 + }, }; static const struct switch_dev_ops ar8327_sw_ops = { diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.h b/target/linux/generic/files/drivers/net/phy/ar8327.h index 8d1fb3b943..4aca754bc8 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8327.h +++ b/target/linux/generic/files/drivers/net/phy/ar8327.h @@ -98,6 +98,71 @@ #define AR8327_REG_EEE_CTRL 0x100 #define AR8327_EEE_CTRL_DISABLE_PHY(_i) BIT(4 + (_i) * 2) +#define AR8327_REG_FRAME_ACK_CTRL0 0x210 +#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN0 BIT(0) +#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN0 BIT(1) +#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN0 BIT(2) +#define AR8327_FRAME_ACK_CTRL_EAPOL_EN0 BIT(3) +#define AR8327_FRAME_ACK_CTRL_DHCP_EN0 BIT(4) +#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN0 BIT(5) +#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN0 BIT(6) +#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN1 BIT(8) +#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN1 BIT(9) +#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN1 BIT(10) +#define AR8327_FRAME_ACK_CTRL_EAPOL_EN1 BIT(11) +#define AR8327_FRAME_ACK_CTRL_DHCP_EN1 BIT(12) +#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN1 BIT(13) +#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN1 BIT(14) +#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN2 BIT(16) +#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN2 BIT(17) +#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN2 BIT(18) +#define AR8327_FRAME_ACK_CTRL_EAPOL_EN2 BIT(19) +#define AR8327_FRAME_ACK_CTRL_DHCP_EN2 BIT(20) +#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN2 BIT(21) +#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN2 BIT(22) +#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN3 BIT(24) +#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN3 BIT(25) +#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN3 BIT(26) +#define AR8327_FRAME_ACK_CTRL_EAPOL_EN3 BIT(27) +#define AR8327_FRAME_ACK_CTRL_DHCP_EN3 BIT(28) +#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN3 BIT(29) +#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN3 BIT(30) + +#define AR8327_REG_FRAME_ACK_CTRL1 0x214 +#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN4 BIT(0) +#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN4 BIT(1) +#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN4 BIT(2) +#define AR8327_FRAME_ACK_CTRL_EAPOL_EN4 BIT(3) +#define AR8327_FRAME_ACK_CTRL_DHCP_EN4 BIT(4) +#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN4 BIT(5) +#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN4 BIT(6) +#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN5 BIT(8) +#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN5 BIT(9) +#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN5 BIT(10) +#define AR8327_FRAME_ACK_CTRL_EAPOL_EN5 BIT(11) +#define AR8327_FRAME_ACK_CTRL_DHCP_EN5 BIT(12) +#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN5 BIT(13) +#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN5 BIT(14) +#define AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN6 BIT(16) +#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN6 BIT(17) +#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN6 BIT(18) +#define AR8327_FRAME_ACK_CTRL_EAPOL_EN6 BIT(19) +#define AR8327_FRAME_ACK_CTRL_DHCP_EN6 BIT(20) +#define AR8327_FRAME_ACK_CTRL_ARP_ACK_EN6 BIT(21) +#define AR8327_FRAME_ACK_CTRL_ARP_REQ_EN6 BIT(22) +#define AR8327_FRAME_ACK_CTRL_IGMP_V3_EN BIT(24) +#define AR8327_FRAME_ACK_CTRL_PPPOE_EN BIT(25) + +#define AR8327_REG_FRAME_ACK_CTRL(_i) (0x210 + ((_i) / 4) * 0x4) +#define AR8327_FRAME_ACK_CTRL_IGMP_MLD BIT(0) +#define AR8327_FRAME_ACK_CTRL_IGMP_JOIN BIT(1) +#define AR8327_FRAME_ACK_CTRL_IGMP_LEAVE BIT(2) +#define AR8327_FRAME_ACK_CTRL_EAPOL BIT(3) +#define AR8327_FRAME_ACK_CTRL_DHCP BIT(4) +#define AR8327_FRAME_ACK_CTRL_ARP_ACK BIT(5) +#define AR8327_FRAME_ACK_CTRL_ARP_REQ BIT(6) +#define AR8327_FRAME_ACK_CTRL_S(_i) (((_i) % 4) * 8) + #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 -- 2.30.2