[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