ar8327: add IGMP Snooping support
authorFelix Fietkau <nbd@openwrt.org>
Sun, 17 Jan 2016 10:42:46 +0000 (10:42 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 17 Jan 2016 10:42:46 +0000 (10:42 +0000)
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 <noltari@gmail.com>
SVN-Revision: 48268

target/linux/generic/files/drivers/net/phy/ar8327.c
target/linux/generic/files/drivers/net/phy/ar8327.h

index 90ee411..02fd2e7 100644 (file)
@@ -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 = {
index 8d1fb3b..4aca754 100644 (file)
 #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