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:
parent
1f826e8992
commit
f8d2ec6e9d
2 changed files with 232 additions and 0 deletions
|
@ -783,6 +783,45 @@ ar8327_atu_flush_port(struct ar8xxx_priv *priv, int port)
|
||||||
return ret;
|
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
|
static void
|
||||||
ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
|
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;
|
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[] = {
|
static const struct switch_attr ar8327_sw_attr_globals[] = {
|
||||||
{
|
{
|
||||||
.type = SWITCH_TYPE_INT,
|
.type = SWITCH_TYPE_INT,
|
||||||
|
@ -1144,6 +1287,22 @@ static const struct switch_attr ar8327_sw_attr_globals[] = {
|
||||||
.description = "Flush ARL table",
|
.description = "Flush ARL table",
|
||||||
.set = ar8xxx_sw_set_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[] = {
|
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",
|
.description = "Flush port's ARL table entries",
|
||||||
.set = ar8xxx_sw_set_flush_port_arl_table,
|
.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 = {
|
static const struct switch_dev_ops ar8327_sw_ops = {
|
||||||
|
|
|
@ -98,6 +98,71 @@
|
||||||
#define AR8327_REG_EEE_CTRL 0x100
|
#define AR8327_REG_EEE_CTRL 0x100
|
||||||
#define AR8327_EEE_CTRL_DISABLE_PHY(_i) BIT(4 + (_i) * 2)
|
#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_REG_PORT_VLAN0(_i) (0x420 + (_i) * 0x8)
|
||||||
#define AR8327_PORT_VLAN0_DEF_SVID BITS(0, 12)
|
#define AR8327_PORT_VLAN0_DEF_SVID BITS(0, 12)
|
||||||
#define AR8327_PORT_VLAN0_DEF_SVID_S 0
|
#define AR8327_PORT_VLAN0_DEF_SVID_S 0
|
||||||
|
|
Loading…
Reference in a new issue