generic: ar8216: add port mirroring
authorLuka Perkov <luka@openwrt.org>
Fri, 24 May 2013 13:10:22 +0000 (13:10 +0000)
committerLuka Perkov <luka@openwrt.org>
Fri, 24 May 2013 13:10:22 +0000 (13:10 +0000)
Tested on Buffalo WZR-600DHP with ar8316 switch. Commands used to mirror both
RX and TX traffic from LAN port 1 to LAN port 4:

$ swconfig dev switch0 set enable_mirror_rx 1
$ swconfig dev switch0 set enable_mirror_tx 1
$ swconfig dev switch0 set mirror_monitor_port 4
$ swconfig dev switch0 set mirror_source_port 1

Signed-off-by: Colin Leitner <colin.leitner@googlemail.com>
Tested-by: Luka Perkov <luka@openwrt.org>
SVN-Revision: 36713

target/linux/generic/files/drivers/net/phy/ar8216.c
target/linux/generic/files/drivers/net/phy/ar8216.h

index a07dd784881657841d3b3868c2500326e504297b..b6df6425acf20828770a6df066291e6cdcdea379 100644 (file)
@@ -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;
index 95d9a96e75737f6415fa15d8d603ca6b339d3bb5..086d58fbab45c729feed3d95e826bc8457ee32b6 100644 (file)
 #define   AR8216_MIB_FUNC_CAPTURE      0x3
 #define   AR8236_MIB_EN                        BIT(30)
 
+#define AR8216_REG_GLOBAL_CPUPORT              0x0078
+#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT    BITS(4, 4)
+#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S  4
+
 #define AR8216_PORT_OFFSET(_i)         (0x0100 * (_i + 1))
 #define AR8216_REG_PORT_STATUS(_i)     (AR8216_PORT_OFFSET(_i) + 0x0000)
 #define   AR8216_PORT_STATUS_SPEED     BITS(0,2)
 #define   AR8327_PORT_LOOKUP_STATE             BITS(16, 3)
 #define   AR8327_PORT_LOOKUP_STATE_S           16
 #define   AR8327_PORT_LOOKUP_LEARN             BIT(20)
+#define   AR8327_PORT_LOOKUP_ING_MIRROR_EN     BIT(25)
 
 #define AR8327_REG_PORT_PRIO(_i)               (0x664 + (_i) * 0xc)
 
+#define AR8327_REG_PORT_HOL_CTRL1(_i)          (0x974 + (_i) * 0x8)
+#define   AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN   BIT(16)
+
 #define AR8327_REG_PORT_STATS_BASE(_i)         (0x1000 + (_i) * 0x100)
 
 /* port speed */