ar8327: add IGMP Snooping support
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
This commit is contained in:
		@@ -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 = {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user