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:
Felix Fietkau
2016-01-17 10:42:46 +00:00
parent 1f826e8992
commit f8d2ec6e9d
2 changed files with 232 additions and 0 deletions

View File

@@ -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 = {