ag71xx: add support for port mirroring
authorMilan Krstić <milan.krstic@gmail.com>
Wed, 26 Apr 2017 19:52:44 +0000 (21:52 +0200)
committerJo-Philipp Wich <jo@mein.io>
Fri, 21 Jul 2017 06:10:38 +0000 (08:10 +0200)
This exposes hardware port mirroring in ag71xx driver (e.g. TL-WR841ND) via
swconfig API.

Signed-off-by: Milan Krstić <milan.krstic@gmail.com>
target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c

index e457acb50cf03e2ece5584843d6ce437da8bb8cb..3f2f64e2aef80075b8ca804af1cb90d248b913f7 100644 (file)
@@ -77,6 +77,7 @@
 
 #define AR7240_REG_CPU_PORT            0x78
 #define AR7240_MIRROR_PORT_S           4
+#define AR7240_MIRROR_PORT_M           BITM(4)
 #define AR7240_CPU_PORT_EN             BIT(8)
 
 #define AR7240_REG_MIB_FUNCTION0       0x80
@@ -1013,6 +1014,134 @@ ar7240_get_port_stats(struct switch_dev *dev, int port,
        return 0;
 }
 
+static int
+ar7240_set_mirror_monitor_port(struct switch_dev *dev,
+                               const struct switch_attr *attr,
+                               struct switch_val *val)
+{
+       struct ar7240sw *as = sw_to_ar7240(dev);
+       struct mii_bus *mii = as->mii_bus;
+
+       int port = val->value.i;
+
+       if (port > 15)
+               return -EINVAL;
+
+       ar7240sw_reg_rmw(mii, AR7240_REG_CPU_PORT,
+               AR7240_MIRROR_PORT_M << AR7240_MIRROR_PORT_S,
+               port << AR7240_MIRROR_PORT_S);
+
+       return 0;
+}
+
+static int
+ar7240_get_mirror_monitor_port(struct switch_dev *dev,
+                               const struct switch_attr *attr,
+                               struct switch_val *val)
+{
+       struct ar7240sw *as = sw_to_ar7240(dev);
+       struct mii_bus *mii = as->mii_bus;
+
+       u32 ret;
+
+       ret = ar7240sw_reg_read(mii, AR7240_REG_CPU_PORT);
+       val->value.i = (ret >> AR7240_MIRROR_PORT_S) & AR7240_MIRROR_PORT_M;
+
+       return 0;
+}
+
+static int
+ar7240_set_mirror_rx(struct switch_dev *dev, const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar7240sw *as = sw_to_ar7240(dev);
+       struct mii_bus *mii = as->mii_bus;
+
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       if (val && val->value.i == 1)
+               ar7240sw_reg_set(mii, AR7240_REG_PORT_CTRL(port),
+                       AR7240_PORT_CTRL_MIRROR_RX);
+       else
+               ar7240sw_reg_rmw(mii, AR7240_REG_PORT_CTRL(port),
+                       AR7240_PORT_CTRL_MIRROR_RX, 0);
+
+       return 0;
+}
+
+static int
+ar7240_get_mirror_rx(struct switch_dev *dev, const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar7240sw *as = sw_to_ar7240(dev);
+       struct mii_bus *mii = as->mii_bus;
+
+       u32 ctrl;
+
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       ctrl = ar7240sw_reg_read(mii, AR7240_REG_PORT_CTRL(port));
+
+       if ((ctrl & AR7240_PORT_CTRL_MIRROR_RX) == AR7240_PORT_CTRL_MIRROR_RX)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int
+ar7240_set_mirror_tx(struct switch_dev *dev, const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar7240sw *as = sw_to_ar7240(dev);
+       struct mii_bus *mii = as->mii_bus;
+
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       if (val && val->value.i == 1)
+               ar7240sw_reg_set(mii, AR7240_REG_PORT_CTRL(port),
+                       AR7240_PORT_CTRL_MIRROR_TX);
+       else
+               ar7240sw_reg_rmw(mii, AR7240_REG_PORT_CTRL(port),
+                       AR7240_PORT_CTRL_MIRROR_TX, 0);
+
+       return 0;
+}
+
+static int
+ar7240_get_mirror_tx(struct switch_dev *dev, const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar7240sw *as = sw_to_ar7240(dev);
+       struct mii_bus *mii = as->mii_bus;
+
+       u32 ctrl;
+
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       ctrl = ar7240sw_reg_read(mii, AR7240_REG_PORT_CTRL(port));
+
+       if ((ctrl & AR7240_PORT_CTRL_MIRROR_TX) == AR7240_PORT_CTRL_MIRROR_TX)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
 static struct switch_attr ar7240_globals[] = {
        {
                .type = SWITCH_TYPE_INT,
@@ -1022,9 +1151,33 @@ static struct switch_attr ar7240_globals[] = {
                .get = ar7240_get_vlan,
                .max = 1
        },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar7240_set_mirror_monitor_port,
+               .get = ar7240_get_mirror_monitor_port,
+               .max = 15
+       },
 };
 
 static struct switch_attr ar7240_port[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar7240_set_mirror_rx,
+               .get = ar7240_get_mirror_rx,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar7240_set_mirror_tx,
+               .get = ar7240_get_mirror_tx,
+               .max = 1
+       },
 };
 
 static struct switch_attr ar7240_vlan[] = {