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

Reply via email to