From c7f3f603795f07fcb7c6c4f6bfa9c74280726bac Mon Sep 17 00:00:00 2001
From: luka <luka@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Date: Fri, 24 May 2013 13:10:22 +0000
Subject: generic: ar8216: add port mirroring

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>

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@36713 3c298f89-4303-0410-b956-a3cf2f4a3e73
---
 .../linux/generic/files/drivers/net/phy/ar8216.c   | 310 +++++++++++++++++++++
 .../linux/generic/files/drivers/net/phy/ar8216.h   |   8 +
 2 files changed, 318 insertions(+)

diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c
index a07dd7848..b6df6425a 100644
--- a/target/linux/generic/files/drivers/net/phy/ar8216.c
+++ b/target/linux/generic/files/drivers/net/phy/ar8216.c
@@ -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;
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h
index 95d9a96e7..086d58fba 100644
--- a/target/linux/generic/files/drivers/net/phy/ar8216.h
+++ b/target/linux/generic/files/drivers/net/phy/ar8216.h
@@ -105,6 +105,10 @@
 #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)
@@ -433,9 +437,13 @@
 #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 */
-- 
cgit v1.2.3