|
|
@@ -120,6 +120,12 @@ struct ar8xxx_priv {
|
|
|
u8 vlan_table[AR8X16_MAX_VLANS];
|
|
|
u8 vlan_tagged;
|
|
|
u16 pvid[AR8X16_MAX_PORTS];
|
|
|
+
|
|
|
+ /* mirroring */
|
|
|
+ bool mirror_rx;
|
|
|
+ bool mirror_tx;
|
|
|
+ int source_port;
|
|
|
+ int monitor_port;
|
|
|
};
|
|
|
|
|
|
#define MIB_DESC(_s , _o, _n) \
|
|
|
@@ -1457,6 +1463,98 @@ ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
|
|
|
+{
|
|
|
+ int port;
|
|
|
+
|
|
|
+ /* reset all mirror registers */
|
|
|
+ ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
|
|
|
+ AR8327_FWD_CTRL0_MIRROR_PORT,
|
|
|
+ (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
|
|
|
+ for (port = 0; port < AR8327_NUM_PORTS; port++) {
|
|
|
+ ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(port),
|
|
|
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN,
|
|
|
+ 0);
|
|
|
+
|
|
|
+ ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(port),
|
|
|
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
|
|
|
+ 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* now enable mirroring if necessary */
|
|
|
+ if (priv->source_port >= AR8327_NUM_PORTS ||
|
|
|
+ priv->monitor_port >= AR8327_NUM_PORTS ||
|
|
|
+ priv->source_port == priv->monitor_port) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
|
|
|
+ AR8327_FWD_CTRL0_MIRROR_PORT,
|
|
|
+ (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S));
|
|
|
+
|
|
|
+ if (priv->mirror_rx)
|
|
|
+ ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(priv->source_port),
|
|
|
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN,
|
|
|
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN);
|
|
|
+
|
|
|
+ if (priv->mirror_tx)
|
|
|
+ ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port),
|
|
|
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
|
|
|
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
|
|
|
+{
|
|
|
+ int port;
|
|
|
+
|
|
|
+ /* reset all mirror registers */
|
|
|
+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
|
|
|
+ AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
|
|
|
+ (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
|
|
|
+ for (port = 0; port < AR8216_NUM_PORTS; port++) {
|
|
|
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
|
|
|
+ AR8216_PORT_CTRL_MIRROR_RX,
|
|
|
+ 0);
|
|
|
+
|
|
|
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
|
|
|
+ AR8216_PORT_CTRL_MIRROR_TX,
|
|
|
+ 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* now enable mirroring if necessary */
|
|
|
+ if (priv->source_port >= AR8216_NUM_PORTS ||
|
|
|
+ priv->monitor_port >= AR8216_NUM_PORTS ||
|
|
|
+ priv->source_port == priv->monitor_port) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
|
|
|
+ AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
|
|
|
+ (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
|
|
|
+
|
|
|
+ if (priv->mirror_rx)
|
|
|
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
|
|
|
+ AR8216_PORT_CTRL_MIRROR_RX,
|
|
|
+ AR8216_PORT_CTRL_MIRROR_RX);
|
|
|
+
|
|
|
+ if (priv->mirror_tx)
|
|
|
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
|
|
|
+ AR8216_PORT_CTRL_MIRROR_TX,
|
|
|
+ AR8216_PORT_CTRL_MIRROR_TX);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+ar8xxx_set_mirror_regs(struct ar8xxx_priv *priv)
|
|
|
+{
|
|
|
+ if (chip_is_ar8327(priv)) {
|
|
|
+ ar8327_set_mirror_regs(priv);
|
|
|
+ } else {
|
|
|
+ ar8216_set_mirror_regs(priv);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
ar8xxx_sw_hw_apply(struct switch_dev *dev)
|
|
|
{
|
|
|
@@ -1520,6 +1618,9 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev)
|
|
|
priv->chip->setup_port(priv, i, egress, ingress, portmask[i],
|
|
|
pvid);
|
|
|
}
|
|
|
+
|
|
|
+ ar8xxx_set_mirror_regs(priv);
|
|
|
+
|
|
|
mutex_unlock(&priv->reg_mutex);
|
|
|
return 0;
|
|
|
}
|
|
|
@@ -1541,7 +1642,13 @@ ar8xxx_sw_reset_switch(struct switch_dev *dev)
|
|
|
for (i = 0; i < dev->ports; i++)
|
|
|
priv->chip->init_port(priv, i);
|
|
|
|
|
|
+ priv->mirror_rx = false;
|
|
|
+ priv->mirror_tx = false;
|
|
|
+ priv->source_port = 0;
|
|
|
+ priv->monitor_port = 0;
|
|
|
+
|
|
|
priv->chip->init_globals(priv);
|
|
|
+
|
|
|
mutex_unlock(&priv->reg_mutex);
|
|
|
|
|
|
return ar8xxx_sw_hw_apply(dev);
|
|
|
@@ -1575,6 +1682,106 @@ unlock:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+ar8xxx_sw_set_mirror_rx_enable(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);
|
|
|
+ priv->mirror_rx = !!val->value.i;
|
|
|
+ ar8xxx_set_mirror_regs(priv);
|
|
|
+ mutex_unlock(&priv->reg_mutex);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
|
|
|
+ const struct switch_attr *attr,
|
|
|
+ struct switch_val *val)
|
|
|
+{
|
|
|
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
|
|
|
+ val->value.i = priv->mirror_rx;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ar8xxx_sw_set_mirror_tx_enable(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);
|
|
|
+ priv->mirror_tx = !!val->value.i;
|
|
|
+ ar8xxx_set_mirror_regs(priv);
|
|
|
+ mutex_unlock(&priv->reg_mutex);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
|
|
|
+ const struct switch_attr *attr,
|
|
|
+ struct switch_val *val)
|
|
|
+{
|
|
|
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
|
|
|
+ val->value.i = priv->mirror_tx;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ar8xxx_sw_set_mirror_monitor_port(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);
|
|
|
+ priv->monitor_port = val->value.i;
|
|
|
+ ar8xxx_set_mirror_regs(priv);
|
|
|
+ mutex_unlock(&priv->reg_mutex);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
|
|
|
+ const struct switch_attr *attr,
|
|
|
+ struct switch_val *val)
|
|
|
+{
|
|
|
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
|
|
|
+ val->value.i = priv->monitor_port;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ar8xxx_sw_set_mirror_source_port(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);
|
|
|
+ priv->source_port = val->value.i;
|
|
|
+ ar8xxx_set_mirror_regs(priv);
|
|
|
+ mutex_unlock(&priv->reg_mutex);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
|
|
|
+ const struct switch_attr *attr,
|
|
|
+ struct switch_val *val)
|
|
|
+{
|
|
|
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
|
|
|
+ val->value.i = priv->source_port;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
|
|
|
const struct switch_attr *attr,
|
|
|
@@ -1668,7 +1875,87 @@ static struct switch_attr ar8xxx_sw_attr_globals[] = {
|
|
|
.description = "Reset all MIB counters",
|
|
|
.set = ar8xxx_sw_set_reset_mibs,
|
|
|
},
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "enable_mirror_rx",
|
|
|
+ .description = "Enable mirroring of RX packets",
|
|
|
+ .set = ar8xxx_sw_set_mirror_rx_enable,
|
|
|
+ .get = ar8xxx_sw_get_mirror_rx_enable,
|
|
|
+ .max = 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "enable_mirror_tx",
|
|
|
+ .description = "Enable mirroring of TX packets",
|
|
|
+ .set = ar8xxx_sw_set_mirror_tx_enable,
|
|
|
+ .get = ar8xxx_sw_get_mirror_tx_enable,
|
|
|
+ .max = 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "mirror_monitor_port",
|
|
|
+ .description = "Mirror monitor port",
|
|
|
+ .set = ar8xxx_sw_set_mirror_monitor_port,
|
|
|
+ .get = ar8xxx_sw_get_mirror_monitor_port,
|
|
|
+ .max = AR8216_NUM_PORTS - 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "mirror_source_port",
|
|
|
+ .description = "Mirror source port",
|
|
|
+ .set = ar8xxx_sw_set_mirror_source_port,
|
|
|
+ .get = ar8xxx_sw_get_mirror_source_port,
|
|
|
+ .max = AR8216_NUM_PORTS - 1
|
|
|
+ },
|
|
|
+};
|
|
|
|
|
|
+static struct switch_attr ar8327_sw_attr_globals[] = {
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "enable_vlan",
|
|
|
+ .description = "Enable VLAN mode",
|
|
|
+ .set = ar8xxx_sw_set_vlan,
|
|
|
+ .get = ar8xxx_sw_get_vlan,
|
|
|
+ .max = 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_NOVAL,
|
|
|
+ .name = "reset_mibs",
|
|
|
+ .description = "Reset all MIB counters",
|
|
|
+ .set = ar8xxx_sw_set_reset_mibs,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "enable_mirror_rx",
|
|
|
+ .description = "Enable mirroring of RX packets",
|
|
|
+ .set = ar8xxx_sw_set_mirror_rx_enable,
|
|
|
+ .get = ar8xxx_sw_get_mirror_rx_enable,
|
|
|
+ .max = 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "enable_mirror_tx",
|
|
|
+ .description = "Enable mirroring of TX packets",
|
|
|
+ .set = ar8xxx_sw_set_mirror_tx_enable,
|
|
|
+ .get = ar8xxx_sw_get_mirror_tx_enable,
|
|
|
+ .max = 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "mirror_monitor_port",
|
|
|
+ .description = "Mirror monitor port",
|
|
|
+ .set = ar8xxx_sw_set_mirror_monitor_port,
|
|
|
+ .get = ar8xxx_sw_get_mirror_monitor_port,
|
|
|
+ .max = AR8327_NUM_PORTS - 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .type = SWITCH_TYPE_INT,
|
|
|
+ .name = "mirror_source_port",
|
|
|
+ .description = "Mirror source port",
|
|
|
+ .set = ar8xxx_sw_set_mirror_source_port,
|
|
|
+ .get = ar8xxx_sw_get_mirror_source_port,
|
|
|
+ .max = AR8327_NUM_PORTS - 1
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
static struct switch_attr ar8xxx_sw_attr_port[] = {
|
|
|
@@ -1720,6 +2007,28 @@ static const struct switch_dev_ops ar8xxx_sw_ops = {
|
|
|
.get_port_link = ar8xxx_sw_get_port_link,
|
|
|
};
|
|
|
|
|
|
+static const struct switch_dev_ops ar8327_sw_ops = {
|
|
|
+ .attr_global = {
|
|
|
+ .attr = ar8327_sw_attr_globals,
|
|
|
+ .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals),
|
|
|
+ },
|
|
|
+ .attr_port = {
|
|
|
+ .attr = ar8xxx_sw_attr_port,
|
|
|
+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port),
|
|
|
+ },
|
|
|
+ .attr_vlan = {
|
|
|
+ .attr = ar8xxx_sw_attr_vlan,
|
|
|
+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
|
|
|
+ },
|
|
|
+ .get_port_pvid = ar8xxx_sw_get_pvid,
|
|
|
+ .set_port_pvid = ar8xxx_sw_set_pvid,
|
|
|
+ .get_vlan_ports = ar8xxx_sw_get_ports,
|
|
|
+ .set_vlan_ports = ar8xxx_sw_set_ports,
|
|
|
+ .apply_config = ar8xxx_sw_hw_apply,
|
|
|
+ .reset_switch = ar8xxx_sw_reset_switch,
|
|
|
+ .get_port_link = ar8xxx_sw_get_port_link,
|
|
|
+};
|
|
|
+
|
|
|
static int
|
|
|
ar8xxx_id_chip(struct ar8xxx_priv *priv)
|
|
|
{
|
|
|
@@ -1900,6 +2209,7 @@ ar8xxx_probe_switch(struct ar8xxx_priv *priv)
|
|
|
swdev->name = "Atheros AR8327";
|
|
|
swdev->vlans = AR8X16_MAX_VLANS;
|
|
|
swdev->ports = AR8327_NUM_PORTS;
|
|
|
+ swdev->ops = &ar8327_sw_ops;
|
|
|
} else {
|
|
|
swdev->name = "Atheros AR8216";
|
|
|
swdev->vlans = AR8216_NUM_VLANS;
|