[OpenWrt-Devel] [PATCH] ar8327: add IGMP Snooping support
Álvaro Fernández Rojas
noltari at gmail.com
Tue Jan 5 14:40:37 EST 2016
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 <noltari at 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 at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list