generic: sync MediaTek Ethernet driver with upstream
[openwrt/openwrt.git] / target / linux / generic / backport-6.1 / 733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch
diff --git a/target/linux/generic/backport-6.1/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch b/target/linux/generic/backport-6.1/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch
new file mode 100644 (file)
index 0000000..3384086
--- /dev/null
@@ -0,0 +1,403 @@
+From f601293f37c4be618c5efaef85d2ee21f97e82e0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 19 Mar 2023 12:57:35 +0000
+Subject: [PATCH 092/250] net: ethernet: mtk_eth_soc: ppe: add support for flow
+ accounting
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The PPE units found in MT7622 and newer support packet and byte
+accounting of hw-offloaded flows. Add support for reading those counters
+as found in MediaTek's SDK[1].
+
+[1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/bc6a6a375c800dc2b80e1a325a2c732d1737df92
+Tested-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c   |   8 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   3 +
+ drivers/net/ethernet/mediatek/mtk_ppe.c       | 114 +++++++++++++++++-
+ drivers/net/ethernet/mediatek/mtk_ppe.h       |  25 +++-
+ .../net/ethernet/mediatek/mtk_ppe_debugfs.c   |   9 +-
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   |   8 ++
+ drivers/net/ethernet/mediatek/mtk_ppe_regs.h  |  14 +++
+ 7 files changed, 172 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4689,8 +4689,8 @@ static int mtk_probe(struct platform_dev
+               for (i = 0; i < num_ppe; i++) {
+                       u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
+-                      eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr,
+-                                                 eth->soc->offload_version, i);
++                      eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, i);
++
+                       if (!eth->ppe[i]) {
+                               err = -ENOMEM;
+                               goto err_deinit_ppe;
+@@ -4814,6 +4814,7 @@ static const struct mtk_soc_data mt7622_
+       .required_pctl = false,
+       .offload_version = 2,
+       .hash_offset = 2,
++      .has_accounting = true,
+       .foe_entry_size = sizeof(struct mtk_foe_entry) - 16,
+       .txrx = {
+               .txd_size = sizeof(struct mtk_tx_dma),
+@@ -4851,6 +4852,7 @@ static const struct mtk_soc_data mt7629_
+       .hw_features = MTK_HW_FEATURES,
+       .required_clks = MT7629_CLKS_BITMAP,
+       .required_pctl = false,
++      .has_accounting = true,
+       .txrx = {
+               .txd_size = sizeof(struct mtk_tx_dma),
+               .rxd_size = sizeof(struct mtk_rx_dma),
+@@ -4871,6 +4873,7 @@ static const struct mtk_soc_data mt7981_
+       .offload_version = 2,
+       .hash_offset = 4,
+       .foe_entry_size = sizeof(struct mtk_foe_entry),
++      .has_accounting = true,
+       .txrx = {
+               .txd_size = sizeof(struct mtk_tx_dma_v2),
+               .rxd_size = sizeof(struct mtk_rx_dma_v2),
+@@ -4891,6 +4894,7 @@ static const struct mtk_soc_data mt7986_
+       .offload_version = 2,
+       .hash_offset = 4,
+       .foe_entry_size = sizeof(struct mtk_foe_entry),
++      .has_accounting = true,
+       .txrx = {
+               .txd_size = sizeof(struct mtk_tx_dma_v2),
+               .rxd_size = sizeof(struct mtk_rx_dma_v2),
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1011,6 +1011,8 @@ struct mtk_reg_map {
+  *                            the extra setup for those pins used by GMAC.
+  * @hash_offset                       Flow table hash offset.
+  * @foe_entry_size            Foe table entry size.
++ * @has_accounting            Bool indicating support for accounting of
++ *                            offloaded flows.
+  * @txd_size                  Tx DMA descriptor size.
+  * @rxd_size                  Rx DMA descriptor size.
+  * @rx_irq_done_mask          Rx irq done register mask.
+@@ -1028,6 +1030,7 @@ struct mtk_soc_data {
+       u8              hash_offset;
+       u16             foe_entry_size;
+       netdev_features_t hw_features;
++      bool            has_accounting;
+       struct {
+               u32     txd_size;
+               u32     rxd_size;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -74,6 +74,48 @@ static int mtk_ppe_wait_busy(struct mtk_
+       return ret;
+ }
++static int mtk_ppe_mib_wait_busy(struct mtk_ppe *ppe)
++{
++      int ret;
++      u32 val;
++
++      ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
++                               !(val & MTK_PPE_MIB_SER_CR_ST),
++                               20, MTK_PPE_WAIT_TIMEOUT_US);
++
++      if (ret)
++              dev_err(ppe->dev, "MIB table busy");
++
++      return ret;
++}
++
++static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
++{
++      u32 byte_cnt_low, byte_cnt_high, pkt_cnt_low, pkt_cnt_high;
++      u32 val, cnt_r0, cnt_r1, cnt_r2;
++      int ret;
++
++      val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
++      ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
++
++      ret = mtk_ppe_mib_wait_busy(ppe);
++      if (ret)
++              return ret;
++
++      cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
++      cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
++      cnt_r2 = readl(ppe->base + MTK_PPE_MIB_SER_R2);
++
++      byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
++      byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
++      pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
++      pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
++      *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
++      *packets = (pkt_cnt_high << 16) | pkt_cnt_low;
++
++      return 0;
++}
++
+ static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
+ {
+       ppe_set(ppe, MTK_PPE_CACHE_CTL, MTK_PPE_CACHE_CTL_CLEAR);
+@@ -459,6 +501,13 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+               hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
+               dma_wmb();
+               mtk_ppe_cache_clear(ppe);
++              if (ppe->accounting) {
++                      struct mtk_foe_accounting *acct;
++
++                      acct = ppe->acct_table + entry->hash * sizeof(*acct);
++                      acct->packets = 0;
++                      acct->bytes = 0;
++              }
+       }
+       entry->hash = 0xffff;
+@@ -566,6 +615,9 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+       wmb();
+       hwe->ib1 = entry->ib1;
++      if (ppe->accounting)
++              *mtk_foe_entry_ib2(eth, hwe) |= MTK_FOE_IB2_MIB_CNT;
++
+       dma_wmb();
+       mtk_ppe_cache_clear(ppe);
+@@ -757,11 +809,39 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
+       return mtk_ppe_wait_busy(ppe);
+ }
+-struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+-                           int version, int index)
++struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
++                                               struct mtk_foe_accounting *diff)
++{
++      struct mtk_foe_accounting *acct;
++      int size = sizeof(struct mtk_foe_accounting);
++      u64 bytes, packets;
++
++      if (!ppe->accounting)
++              return NULL;
++
++      if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
++              return NULL;
++
++      acct = ppe->acct_table + index * size;
++
++      acct->bytes += bytes;
++      acct->packets += packets;
++
++      if (diff) {
++              diff->bytes = bytes;
++              diff->packets = packets;
++      }
++
++      return acct;
++}
++
++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
+ {
++      bool accounting = eth->soc->has_accounting;
+       const struct mtk_soc_data *soc = eth->soc;
++      struct mtk_foe_accounting *acct;
+       struct device *dev = eth->dev;
++      struct mtk_mib_entry *mib;
+       struct mtk_ppe *ppe;
+       u32 foe_flow_size;
+       void *foe;
+@@ -778,7 +858,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+       ppe->base = base;
+       ppe->eth = eth;
+       ppe->dev = dev;
+-      ppe->version = version;
++      ppe->version = eth->soc->offload_version;
++      ppe->accounting = accounting;
+       foe = dmam_alloc_coherent(ppe->dev,
+                                 MTK_PPE_ENTRIES * soc->foe_entry_size,
+@@ -794,6 +875,23 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_
+       if (!ppe->foe_flow)
+               goto err_free_l2_flows;
++      if (accounting) {
++              mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib),
++                                        &ppe->mib_phys, GFP_KERNEL);
++              if (!mib)
++                      return NULL;
++
++              ppe->mib_table = mib;
++
++              acct = devm_kzalloc(dev, MTK_PPE_ENTRIES * sizeof(*acct),
++                                  GFP_KERNEL);
++
++              if (!acct)
++                      return NULL;
++
++              ppe->acct_table = acct;
++      }
++
+       mtk_ppe_debugfs_init(ppe, index);
+       return ppe;
+@@ -923,6 +1021,16 @@ void mtk_ppe_start(struct mtk_ppe *ppe)
+               ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777);
+               ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f);
+       }
++
++      if (ppe->accounting && ppe->mib_phys) {
++              ppe_w32(ppe, MTK_PPE_MIB_TB_BASE, ppe->mib_phys);
++              ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_EN,
++                      MTK_PPE_MIB_CFG_EN);
++              ppe_m32(ppe, MTK_PPE_MIB_CFG, MTK_PPE_MIB_CFG_RD_CLR,
++                      MTK_PPE_MIB_CFG_RD_CLR);
++              ppe_m32(ppe, MTK_PPE_MIB_CACHE_CTL, MTK_PPE_MIB_CACHE_CTL_EN,
++                      MTK_PPE_MIB_CFG_RD_CLR);
++      }
+ }
+ int mtk_ppe_stop(struct mtk_ppe *ppe)
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -57,6 +57,7 @@ enum {
+ #define MTK_FOE_IB2_MULTICAST         BIT(8)
+ #define MTK_FOE_IB2_WDMA_QID2         GENMASK(13, 12)
++#define MTK_FOE_IB2_MIB_CNT           BIT(15)
+ #define MTK_FOE_IB2_WDMA_DEVIDX               BIT(16)
+ #define MTK_FOE_IB2_WDMA_WINFO                BIT(17)
+@@ -285,16 +286,34 @@ struct mtk_flow_entry {
+       unsigned long cookie;
+ };
++struct mtk_mib_entry {
++      u32     byt_cnt_l;
++      u16     byt_cnt_h;
++      u32     pkt_cnt_l;
++      u8      pkt_cnt_h;
++      u8      _rsv0;
++      u32     _rsv1;
++} __packed;
++
++struct mtk_foe_accounting {
++      u64     bytes;
++      u64     packets;
++};
++
+ struct mtk_ppe {
+       struct mtk_eth *eth;
+       struct device *dev;
+       void __iomem *base;
+       int version;
+       char dirname[5];
++      bool accounting;
+       void *foe_table;
+       dma_addr_t foe_phys;
++      struct mtk_mib_entry *mib_table;
++      dma_addr_t mib_phys;
++
+       u16 foe_check_time[MTK_PPE_ENTRIES];
+       struct hlist_head *foe_flow;
+@@ -303,8 +322,8 @@ struct mtk_ppe {
+       void *acct_table;
+ };
+-struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base,
+-                           int version, int index);
++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index);
++
+ void mtk_ppe_deinit(struct mtk_eth *eth);
+ void mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
+@@ -359,5 +378,7 @@ int mtk_foe_entry_commit(struct mtk_ppe
+ void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+ int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index);
++struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
++                                               struct mtk_foe_accounting *diff);
+ #endif
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -82,6 +82,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
+               struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i);
+               struct mtk_foe_mac_info *l2;
+               struct mtk_flow_addr_info ai = {};
++              struct mtk_foe_accounting *acct;
+               unsigned char h_source[ETH_ALEN];
+               unsigned char h_dest[ETH_ALEN];
+               int type, state;
+@@ -95,6 +96,8 @@ mtk_ppe_debugfs_foe_show(struct seq_file
+               if (bind && state != MTK_FOE_STATE_BIND)
+                       continue;
++              acct = mtk_foe_entry_get_mib(ppe, i, NULL);
++
+               type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1);
+               seq_printf(m, "%05x %s %7s", i,
+                          mtk_foe_entry_state_str(state),
+@@ -153,9 +156,11 @@ mtk_ppe_debugfs_foe_show(struct seq_file
+               *((__be16 *)&h_dest[4]) = htons(l2->dest_mac_lo);
+               seq_printf(m, " eth=%pM->%pM etype=%04x"
+-                            " vlan=%d,%d ib1=%08x ib2=%08x\n",
++                            " vlan=%d,%d ib1=%08x ib2=%08x"
++                            " packets=%llu bytes=%llu\n",
+                          h_source, h_dest, ntohs(l2->etype),
+-                         l2->vlan1, l2->vlan2, entry->ib1, ib2);
++                         l2->vlan1, l2->vlan2, entry->ib1, ib2,
++                         acct ? acct->packets : 0, acct ? acct->bytes : 0);
+       }
+       return 0;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -497,6 +497,7 @@ static int
+ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
+ {
+       struct mtk_flow_entry *entry;
++      struct mtk_foe_accounting diff;
+       u32 idle;
+       entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
+@@ -507,6 +508,13 @@ mtk_flow_offload_stats(struct mtk_eth *e
+       idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
+       f->stats.lastused = jiffies - idle * HZ;
++      if (entry->hash != 0xFFFF &&
++          mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
++                                &diff)) {
++              f->stats.pkts += diff.packets;
++              f->stats.bytes += diff.bytes;
++      }
++
+       return 0;
+ }
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h
+@@ -149,6 +149,20 @@ enum {
+ #define MTK_PPE_MIB_TB_BASE                   0x338
++#define MTK_PPE_MIB_SER_CR                    0x33C
++#define MTK_PPE_MIB_SER_CR_ST                 BIT(16)
++#define MTK_PPE_MIB_SER_CR_ADDR                       GENMASK(13, 0)
++
++#define MTK_PPE_MIB_SER_R0                    0x340
++#define MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW               GENMASK(31, 0)
++
++#define MTK_PPE_MIB_SER_R1                    0x344
++#define MTK_PPE_MIB_SER_R1_PKT_CNT_LOW                GENMASK(31, 16)
++#define MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH      GENMASK(15, 0)
++
++#define MTK_PPE_MIB_SER_R2                    0x348
++#define MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH               GENMASK(23, 0)
++
+ #define MTK_PPE_MIB_CACHE_CTL                 0x350
+ #define MTK_PPE_MIB_CACHE_CTL_EN              BIT(0)
+ #define MTK_PPE_MIB_CACHE_CTL_FLUSH           BIT(2)