kernel: add mv88e61xx switch port-mirroring support
[openwrt/staging/chunkeey.git] / target / linux / generic / files / drivers / net / phy / mvsw61xx.c
index 9a689e6c81b69cbe29995c929a0de9d35344663d..253ebff835a6e092ee2b58bc862fbd8f1ced1caa 100644 (file)
@@ -454,6 +454,86 @@ static int mvsw61xx_set_enable_vlan(struct switch_dev *dev,
        return 0;
 }
 
+static int mvsw61xx_get_mirror_rx_enable(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->mirror_rx;
+
+       return 0;
+}
+
+static int mvsw61xx_set_mirror_rx_enable(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->mirror_rx = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_get_mirror_tx_enable(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->mirror_tx;
+
+       return 0;
+}
+
+static int mvsw61xx_set_mirror_tx_enable(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->mirror_tx = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_get_mirror_monitor_port(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->monitor_port;
+
+       return 0;
+}
+
+static int mvsw61xx_set_mirror_monitor_port(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->monitor_port = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_get_mirror_source_port(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->source_port;
+
+       return 0;
+}
+
+static int mvsw61xx_set_mirror_source_port(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->source_port = val->value.i;
+
+       return 0;
+}
+
 static int mvsw61xx_vtu_program(struct switch_dev *dev)
 {
        struct mvsw61xx_state *state = get_state(dev);
@@ -604,6 +684,40 @@ static int mvsw61xx_update_state(struct switch_dev *dev)
 
        mvsw61xx_vtu_program(dev);
 
+       /* port mirroring */
+       /* reset all mirror registers */
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(CONTROL2, i));
+               reg &= ~(MV_MIRROR_RX_SRC_MASK | MV_MIRROR_TX_SRC_MASK);
+               sw16(dev, MV_PORTREG(CONTROL2, i), reg);
+       }
+       reg = sr16(dev, MV_GLOBALREG(MONITOR_CTRL));
+       reg |= MV_MIRROR_RX_DEST_MASK | MV_MIRROR_TX_DEST_MASK;
+       sw16(dev, MV_GLOBALREG(MONITOR_CTRL), reg);
+
+       /* now enable mirroring if necessary */
+       if (state->mirror_rx) {
+               /* set ingress monitor source */
+               reg = sr16(dev, MV_PORTREG(CONTROL2, state->source_port)) & ~MV_MIRROR_RX_SRC_MASK;
+               reg |= state->mirror_rx << MV_MIRROR_RX_SRC_SHIFT;
+               sw16(dev, MV_PORTREG(CONTROL2, state->source_port), reg);
+               /* set ingress monitor destination */
+               reg = sr16(dev, MV_GLOBALREG(MONITOR_CTRL)) & ~MV_MIRROR_RX_DEST_MASK;
+               reg |= state->monitor_port << MV_MIRROR_RX_DEST_SHIFT;
+               sw16(dev, MV_GLOBALREG(MONITOR_CTRL), reg);
+       }
+
+       if (state->mirror_tx) {
+               /* set egress monitor source */
+               reg = sr16(dev, MV_PORTREG(CONTROL2, state->source_port)) & ~MV_MIRROR_TX_SRC_MASK;
+               reg |= state->mirror_tx << MV_MIRROR_TX_SRC_SHIFT;
+               sw16(dev, MV_PORTREG(CONTROL2, state->source_port), reg);
+               /* set egress monitor destination */
+               reg = sr16(dev, MV_GLOBALREG(MONITOR_CTRL)) & ~MV_MIRROR_TX_DEST_MASK;
+               reg |= state->monitor_port << MV_MIRROR_TX_DEST_SHIFT;
+               sw16(dev, MV_GLOBALREG(MONITOR_CTRL), reg);
+       }
+
        return 0;
 }
 
@@ -693,6 +807,11 @@ static int _mvsw61xx_reset(struct switch_dev *dev, bool full)
 
        state->vlan_enabled = 0;
 
+       state->mirror_rx = false;
+       state->mirror_tx = false;
+       state->source_port = 0;
+       state->monitor_port = 0;
+
        mvsw61xx_update_state(dev);
 
        /* Re-enable ports */
@@ -710,10 +829,6 @@ static int mvsw61xx_reset(struct switch_dev *dev)
        return _mvsw61xx_reset(dev, false);
 }
 
-enum {
-       MVSW61XX_ENABLE_VLAN,
-};
-
 enum {
        MVSW61XX_VLAN_PORT_BASED,
        MVSW61XX_VLAN_ID,
@@ -725,14 +840,45 @@ enum {
 };
 
 static const struct switch_attr mvsw61xx_global[] = {
-       [MVSW61XX_ENABLE_VLAN] = {
-               .id = MVSW61XX_ENABLE_VLAN,
+       {
                .type = SWITCH_TYPE_INT,
                .name = "enable_vlan",
                .description = "Enable 802.1q VLAN support",
                .get = mvsw61xx_get_enable_vlan,
                .set = mvsw61xx_set_enable_vlan,
        },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = mvsw61xx_set_mirror_rx_enable,
+               .get = mvsw61xx_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = mvsw61xx_set_mirror_tx_enable,
+               .get = mvsw61xx_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = mvsw61xx_set_mirror_monitor_port,
+               .get = mvsw61xx_get_mirror_monitor_port,
+               .max = MV_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = mvsw61xx_set_mirror_source_port,
+               .get = mvsw61xx_get_mirror_source_port,
+               .max = MV_PORTS - 1
+       },
 };
 
 static const struct switch_attr mvsw61xx_vlan[] = {