mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-21 06:33:41 +00:00
ddcebda08b
Add kernel tag that introduced the patch on backport patch. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
227 lines
6.5 KiB
Diff
227 lines
6.5 KiB
Diff
From 5c957c7ca78cce5e4b96866722b0115bd758d945 Mon Sep 17 00:00:00 2001
|
|
From: Ansuel Smith <ansuelsmth@gmail.com>
|
|
Date: Wed, 2 Feb 2022 01:03:30 +0100
|
|
Subject: [PATCH 11/16] net: dsa: qca8k: add support for mib autocast in
|
|
Ethernet packet
|
|
|
|
The switch can autocast MIB counter using Ethernet packet.
|
|
Add support for this and provide a handler for the tagger.
|
|
The switch will send packet with MIB counter for each port, the switch
|
|
will use completion API to wait for the correct packet to be received
|
|
and will complete the task only when each packet is received.
|
|
Although the handler will drop all the other packet, we still have to
|
|
consume each MIB packet to complete the request. This is done to prevent
|
|
mixed data with concurrent ethtool request.
|
|
|
|
connect_tag_protocol() is used to add the handler to the tag_qca tagger,
|
|
master_state_change() use the MIB lock to make sure no MIB Ethernet is
|
|
in progress.
|
|
|
|
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
|
|
Signed-off-by: David S. Miller <davem@davemloft.net>
|
|
---
|
|
drivers/net/dsa/qca8k.c | 106 +++++++++++++++++++++++++++++++++++++++-
|
|
drivers/net/dsa/qca8k.h | 17 ++++++-
|
|
2 files changed, 121 insertions(+), 2 deletions(-)
|
|
|
|
--- a/drivers/net/dsa/qca8k.c
|
|
+++ b/drivers/net/dsa/qca8k.c
|
|
@@ -830,7 +830,10 @@ qca8k_mib_init(struct qca8k_priv *priv)
|
|
int ret;
|
|
|
|
mutex_lock(&priv->reg_mutex);
|
|
- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);
|
|
+ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
|
|
+ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
|
|
+ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) |
|
|
+ QCA8K_MIB_BUSY);
|
|
if (ret)
|
|
goto exit;
|
|
|
|
@@ -1901,6 +1904,97 @@ qca8k_get_strings(struct dsa_switch *ds,
|
|
ETH_GSTRING_LEN);
|
|
}
|
|
|
|
+static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb)
|
|
+{
|
|
+ const struct qca8k_match_data *match_data;
|
|
+ struct qca8k_mib_eth_data *mib_eth_data;
|
|
+ struct qca8k_priv *priv = ds->priv;
|
|
+ const struct qca8k_mib_desc *mib;
|
|
+ struct mib_ethhdr *mib_ethhdr;
|
|
+ int i, mib_len, offset = 0;
|
|
+ u64 *data;
|
|
+ u8 port;
|
|
+
|
|
+ mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb);
|
|
+ mib_eth_data = &priv->mib_eth_data;
|
|
+
|
|
+ /* The switch autocast every port. Ignore other packet and
|
|
+ * parse only the requested one.
|
|
+ */
|
|
+ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr));
|
|
+ if (port != mib_eth_data->req_port)
|
|
+ goto exit;
|
|
+
|
|
+ match_data = device_get_match_data(priv->dev);
|
|
+ data = mib_eth_data->data;
|
|
+
|
|
+ for (i = 0; i < match_data->mib_count; i++) {
|
|
+ mib = &ar8327_mib[i];
|
|
+
|
|
+ /* First 3 mib are present in the skb head */
|
|
+ if (i < 3) {
|
|
+ data[i] = mib_ethhdr->data[i];
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ mib_len = sizeof(uint32_t);
|
|
+
|
|
+ /* Some mib are 64 bit wide */
|
|
+ if (mib->size == 2)
|
|
+ mib_len = sizeof(uint64_t);
|
|
+
|
|
+ /* Copy the mib value from packet to the */
|
|
+ memcpy(data + i, skb->data + offset, mib_len);
|
|
+
|
|
+ /* Set the offset for the next mib */
|
|
+ offset += mib_len;
|
|
+ }
|
|
+
|
|
+exit:
|
|
+ /* Complete on receiving all the mib packet */
|
|
+ if (refcount_dec_and_test(&mib_eth_data->port_parsed))
|
|
+ complete(&mib_eth_data->rw_done);
|
|
+}
|
|
+
|
|
+static int
|
|
+qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data)
|
|
+{
|
|
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
|
+ struct qca8k_mib_eth_data *mib_eth_data;
|
|
+ struct qca8k_priv *priv = ds->priv;
|
|
+ int ret;
|
|
+
|
|
+ mib_eth_data = &priv->mib_eth_data;
|
|
+
|
|
+ mutex_lock(&mib_eth_data->mutex);
|
|
+
|
|
+ reinit_completion(&mib_eth_data->rw_done);
|
|
+
|
|
+ mib_eth_data->req_port = dp->index;
|
|
+ mib_eth_data->data = data;
|
|
+ refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS);
|
|
+
|
|
+ mutex_lock(&priv->reg_mutex);
|
|
+
|
|
+ /* Send mib autocast request */
|
|
+ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB,
|
|
+ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY,
|
|
+ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) |
|
|
+ QCA8K_MIB_BUSY);
|
|
+
|
|
+ mutex_unlock(&priv->reg_mutex);
|
|
+
|
|
+ if (ret)
|
|
+ goto exit;
|
|
+
|
|
+ ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT);
|
|
+
|
|
+exit:
|
|
+ mutex_unlock(&mib_eth_data->mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static void
|
|
qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,
|
|
uint64_t *data)
|
|
@@ -1912,6 +2006,10 @@ qca8k_get_ethtool_stats(struct dsa_switc
|
|
u32 hi = 0;
|
|
int ret;
|
|
|
|
+ if (priv->mgmt_master &&
|
|
+ qca8k_get_ethtool_stats_eth(ds, port, data) > 0)
|
|
+ return;
|
|
+
|
|
match_data = of_device_get_match_data(priv->dev);
|
|
|
|
for (i = 0; i < match_data->mib_count; i++) {
|
|
@@ -2611,9 +2709,11 @@ qca8k_master_change(struct dsa_switch *d
|
|
return;
|
|
|
|
mutex_lock(&priv->mgmt_eth_data.mutex);
|
|
+ mutex_lock(&priv->mib_eth_data.mutex);
|
|
|
|
priv->mgmt_master = operational ? (struct net_device *)master : NULL;
|
|
|
|
+ mutex_unlock(&priv->mib_eth_data.mutex);
|
|
mutex_unlock(&priv->mgmt_eth_data.mutex);
|
|
}
|
|
|
|
@@ -2627,6 +2727,7 @@ static int qca8k_connect_tag_protocol(st
|
|
tagger_data = ds->tagger_data;
|
|
|
|
tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler;
|
|
+ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler;
|
|
|
|
break;
|
|
default:
|
|
@@ -2755,6 +2856,9 @@ qca8k_sw_probe(struct mdio_device *mdiod
|
|
mutex_init(&priv->mgmt_eth_data.mutex);
|
|
init_completion(&priv->mgmt_eth_data.rw_done);
|
|
|
|
+ mutex_init(&priv->mib_eth_data.mutex);
|
|
+ init_completion(&priv->mib_eth_data.rw_done);
|
|
+
|
|
priv->ds->dev = &mdiodev->dev;
|
|
priv->ds->num_ports = QCA8K_NUM_PORTS;
|
|
priv->ds->priv = priv;
|
|
--- a/drivers/net/dsa/qca8k.h
|
|
+++ b/drivers/net/dsa/qca8k.h
|
|
@@ -67,7 +67,7 @@
|
|
#define QCA8K_REG_MODULE_EN 0x030
|
|
#define QCA8K_MODULE_EN_MIB BIT(0)
|
|
#define QCA8K_REG_MIB 0x034
|
|
-#define QCA8K_MIB_FLUSH BIT(24)
|
|
+#define QCA8K_MIB_FUNC GENMASK(26, 24)
|
|
#define QCA8K_MIB_CPU_KEEP BIT(20)
|
|
#define QCA8K_MIB_BUSY BIT(17)
|
|
#define QCA8K_MDIO_MASTER_CTRL 0x3c
|
|
@@ -317,6 +317,12 @@ enum qca8k_vlan_cmd {
|
|
QCA8K_VLAN_READ = 6,
|
|
};
|
|
|
|
+enum qca8k_mid_cmd {
|
|
+ QCA8K_MIB_FLUSH = 1,
|
|
+ QCA8K_MIB_FLUSH_PORT = 2,
|
|
+ QCA8K_MIB_CAST = 3,
|
|
+};
|
|
+
|
|
struct ar8xxx_port_status {
|
|
int enabled;
|
|
};
|
|
@@ -340,6 +346,14 @@ struct qca8k_mgmt_eth_data {
|
|
u32 data[4];
|
|
};
|
|
|
|
+struct qca8k_mib_eth_data {
|
|
+ struct completion rw_done;
|
|
+ struct mutex mutex; /* Process one command at time */
|
|
+ refcount_t port_parsed; /* Counter to track parsed port */
|
|
+ u8 req_port;
|
|
+ u64 *data; /* pointer to ethtool data */
|
|
+};
|
|
+
|
|
struct qca8k_ports_config {
|
|
bool sgmii_rx_clk_falling_edge;
|
|
bool sgmii_tx_clk_falling_edge;
|
|
@@ -367,6 +381,7 @@ struct qca8k_priv {
|
|
unsigned int port_mtu[QCA8K_NUM_PORTS];
|
|
struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
|
|
struct qca8k_mgmt_eth_data mgmt_eth_data;
|
|
+ struct qca8k_mib_eth_data mib_eth_data;
|
|
};
|
|
|
|
struct qca8k_mib_desc {
|