aboutsummaryrefslogtreecommitdiff
path: root/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch')
-rw-r--r--pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch226
1 files changed, 226 insertions, 0 deletions
diff --git a/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch b/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch
new file mode 100644
index 0000000..c4bc2b3
--- /dev/null
+++ b/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch
@@ -0,0 +1,226 @@
+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++) {
+@@ -2592,9 +2690,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);
+ }
+
+@@ -2608,6 +2708,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:
+@@ -2736,6 +2837,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 {