layerscape: add patches-5.4
[openwrt/staging/wigyori.git] / target / linux / layerscape / patches-5.4 / 701-net-0225-enetc-Configure-the-Time-Aware-Scheduler-via-tc-tapr.patch
diff --git a/target/linux/layerscape/patches-5.4/701-net-0225-enetc-Configure-the-Time-Aware-Scheduler-via-tc-tapr.patch b/target/linux/layerscape/patches-5.4/701-net-0225-enetc-Configure-the-Time-Aware-Scheduler-via-tc-tapr.patch
new file mode 100644 (file)
index 0000000..37ed557
--- /dev/null
@@ -0,0 +1,378 @@
+From 6ee2331a3a5627b062daf76aa5ed9f64fbbfa303 Mon Sep 17 00:00:00 2001
+From: Po Liu <po.liu@nxp.com>
+Date: Fri, 15 Nov 2019 03:33:33 +0000
+Subject: [PATCH] enetc: Configure the Time-Aware Scheduler via tc-taprio
+ offload
+
+ENETC supports in hardware for time-based egress shaping according
+to IEEE 802.1Qbv. This patch implement the Qbv enablement by the
+hardware offload method qdisc tc-taprio method.
+Also update cbdr writeback to up level since control bd ring may
+writeback data to control bd ring.
+
+Signed-off-by: Po Liu <Po.Liu@nxp.com>
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/freescale/enetc/Kconfig      |  10 ++
+ drivers/net/ethernet/freescale/enetc/Makefile     |   2 +
+ drivers/net/ethernet/freescale/enetc/enetc.c      |  19 ++-
+ drivers/net/ethernet/freescale/enetc/enetc.h      |   7 ++
+ drivers/net/ethernet/freescale/enetc/enetc_cbdr.c |   5 +-
+ drivers/net/ethernet/freescale/enetc/enetc_hw.h   |  84 ++++++++++---
+ drivers/net/ethernet/freescale/enetc/enetc_qos.c  | 138 ++++++++++++++++++++++
+ 7 files changed, 243 insertions(+), 22 deletions(-)
+ create mode 100644 drivers/net/ethernet/freescale/enetc/enetc_qos.c
+
+--- a/drivers/net/ethernet/freescale/enetc/Kconfig
++++ b/drivers/net/ethernet/freescale/enetc/Kconfig
+@@ -50,3 +50,13 @@ config FSL_ENETC_HW_TIMESTAMPING
+         allocation has not been supported and it is too expensive to use
+         extended RX BDs if timestamping is not used, this option enables
+         extended RX BDs in order to support hardware timestamping.
++
++config FSL_ENETC_QOS
++      bool "ENETC hardware Time-sensitive Network support"
++      depends on (FSL_ENETC || FSL_ENETC_VF) && NET_SCH_TAPRIO
++      help
++        There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci
++        /802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set
++        enable/disable from user space via Qos commands(tc). In the kernel
++        side, it can be loaded by Qos driver. Currently, it is only support
++        taprio(802.1Qbv).
+--- a/drivers/net/ethernet/freescale/enetc/Makefile
++++ b/drivers/net/ethernet/freescale/enetc/Makefile
+@@ -5,9 +5,11 @@ common-objs := enetc.o enetc_cbdr.o enet
+ obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
+ fsl-enetc-y := enetc_pf.o enetc_mdio.o $(common-objs)
+ fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
++fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o
+ obj-$(CONFIG_FSL_ENETC_VF) += fsl-enetc-vf.o
+ fsl-enetc-vf-y := enetc_vf.o $(common-objs)
++fsl-enetc-vf-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o
+ obj-$(CONFIG_FSL_ENETC_MDIO) += fsl-enetc-mdio.o
+ fsl-enetc-mdio-y := enetc_pci_mdio.o enetc_mdio.o
+--- a/drivers/net/ethernet/freescale/enetc/enetc.c
++++ b/drivers/net/ethernet/freescale/enetc/enetc.c
+@@ -1427,8 +1427,7 @@ int enetc_close(struct net_device *ndev)
+       return 0;
+ }
+-int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
+-                 void *type_data)
++int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data)
+ {
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
+       struct tc_mqprio_qopt *mqprio = type_data;
+@@ -1436,9 +1435,6 @@ int enetc_setup_tc(struct net_device *nd
+       u8 num_tc;
+       int i;
+-      if (type != TC_SETUP_QDISC_MQPRIO)
+-              return -EOPNOTSUPP;
+-
+       mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
+       num_tc = mqprio->num_tc;
+@@ -1483,6 +1479,19 @@ int enetc_setup_tc(struct net_device *nd
+       return 0;
+ }
++int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
++                 void *type_data)
++{
++      switch (type) {
++      case TC_SETUP_QDISC_MQPRIO:
++              return enetc_setup_tc_mqprio(ndev, type_data);
++      case TC_SETUP_QDISC_TAPRIO:
++              return enetc_setup_tc_taprio(ndev, type_data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
+ struct net_device_stats *enetc_get_stats(struct net_device *ndev)
+ {
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
+--- a/drivers/net/ethernet/freescale/enetc/enetc.h
++++ b/drivers/net/ethernet/freescale/enetc/enetc.h
+@@ -244,3 +244,10 @@ int enetc_set_fs_entry(struct enetc_si *
+ void enetc_set_rss_key(struct enetc_hw *hw, const u8 *bytes);
+ int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count);
+ int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count);
++int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd);
++
++#ifdef CONFIG_FSL_ENETC_QOS
++int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
++#else
++#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
++#endif
+--- a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c
++++ b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c
+@@ -32,7 +32,7 @@ static int enetc_cbd_unused(struct enetc
+               r->bd_count;
+ }
+-static int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
++int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
+ {
+       struct enetc_cbdr *ring = &si->cbd_ring;
+       int timeout = ENETC_CBDR_TIMEOUT;
+@@ -66,6 +66,9 @@ static int enetc_send_cmd(struct enetc_s
+       if (!timeout)
+               return -EBUSY;
++      /* CBD may writeback data, feedback up level */
++      *cbd = *dest_cbd;
++
+       enetc_clean_cbdr(si);
+       return 0;
+--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
++++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+@@ -18,6 +18,7 @@
+ #define ENETC_SICTR0  0x18
+ #define ENETC_SICTR1  0x1c
+ #define ENETC_SIPCAPR0        0x20
++#define ENETC_SIPCAPR0_QBV    BIT(4)
+ #define ENETC_SIPCAPR0_RSS    BIT(8)
+ #define ENETC_SIPCAPR1        0x24
+ #define ENETC_SITGTGR 0x30
+@@ -440,22 +441,6 @@ union enetc_rx_bd {
+ #define EMETC_MAC_ADDR_FILT_RES       3 /* # of reserved entries at the beginning */
+ #define ENETC_MAX_NUM_VFS     2
+-struct enetc_cbd {
+-      union {
+-              struct {
+-                      __le32 addr[2];
+-                      __le32 opt[4];
+-              };
+-              __le32 data[6];
+-      };
+-      __le16 index;
+-      __le16 length;
+-      u8 cmd;
+-      u8 cls;
+-      u8 _res;
+-      u8 status_flags;
+-};
+-
+ #define ENETC_CBD_FLAGS_SF    BIT(7) /* short format */
+ #define ENETC_CBD_STATUS_MASK 0xf
+@@ -554,3 +539,70 @@ static inline void enetc_set_bdr_prio(st
+       val |= ENETC_TBMR_SET_PRIO(prio);
+       enetc_txbdr_wr(hw, bdr_idx, ENETC_TBMR, val);
+ }
++
++enum bdcr_cmd_class {
++      BDCR_CMD_UNSPEC = 0,
++      BDCR_CMD_MAC_FILTER,
++      BDCR_CMD_VLAN_FILTER,
++      BDCR_CMD_RSS,
++      BDCR_CMD_RFS,
++      BDCR_CMD_PORT_GCL,
++      BDCR_CMD_RECV_CLASSIFIER,
++      __BDCR_CMD_MAX_LEN,
++      BDCR_CMD_MAX_LEN = __BDCR_CMD_MAX_LEN - 1,
++};
++
++/* class 5, command 0 */
++struct tgs_gcl_conf {
++      u8      atc;    /* init gate value */
++      u8      res[7];
++      struct {
++              u8      res1[4];
++              __le16  acl_len;
++              u8      res2[2];
++      };
++};
++
++/* gate control list entry */
++struct gce {
++      __le32  period;
++      u8      gate;
++      u8      res[3];
++};
++
++/* tgs_gcl_conf address point to this data space */
++struct tgs_gcl_data {
++      __le32          btl;
++      __le32          bth;
++      __le32          ct;
++      __le32          cte;
++      struct gce      entry[0];
++};
++
++struct enetc_cbd {
++      union{
++              struct {
++                      __le32  addr[2];
++                      union {
++                              __le32  opt[4];
++                              struct tgs_gcl_conf     gcl_conf;
++                      };
++              };      /* Long format */
++              __le32 data[6];
++      };
++      __le16 index;
++      __le16 length;
++      u8 cmd;
++      u8 cls;
++      u8 _res;
++      u8 status_flags;
++};
++
++/* port time gating control register */
++#define ENETC_QBV_PTGCR_OFFSET                0x11a00
++#define ENETC_QBV_TGE                 BIT(31)
++#define ENETC_QBV_TGPE                        BIT(30)
++
++/* Port time gating capability register */
++#define ENETC_QBV_PTGCAPR_OFFSET      0x11a08
++#define ENETC_QBV_MAX_GCL_LEN_MASK    GENMASK(15, 0)
+--- /dev/null
++++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c
+@@ -0,0 +1,138 @@
++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
++/* Copyright 2019 NXP */
++
++#include "enetc.h"
++
++#include <net/pkt_sched.h>
++
++static u16 enetc_get_max_gcl_len(struct enetc_hw *hw)
++{
++      return enetc_rd(hw, ENETC_QBV_PTGCAPR_OFFSET)
++              & ENETC_QBV_MAX_GCL_LEN_MASK;
++}
++
++static int enetc_setup_taprio(struct net_device *ndev,
++                            struct tc_taprio_qopt_offload *admin_conf)
++{
++      struct enetc_ndev_priv *priv = netdev_priv(ndev);
++      struct enetc_cbd cbd = {.cmd = 0};
++      struct tgs_gcl_conf *gcl_config;
++      struct tgs_gcl_data *gcl_data;
++      struct gce *gce;
++      dma_addr_t dma;
++      u16 data_size;
++      u16 gcl_len;
++      u32 tge;
++      int err;
++      int i;
++
++      if (admin_conf->num_entries > enetc_get_max_gcl_len(&priv->si->hw))
++              return -EINVAL;
++      gcl_len = admin_conf->num_entries;
++
++      tge = enetc_rd(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET);
++      if (!admin_conf->enable) {
++              enetc_wr(&priv->si->hw,
++                       ENETC_QBV_PTGCR_OFFSET,
++                       tge & (~ENETC_QBV_TGE));
++              return 0;
++      }
++
++      if (admin_conf->cycle_time > U32_MAX ||
++          admin_conf->cycle_time_extension > U32_MAX)
++              return -EINVAL;
++
++      /* Configure the (administrative) gate control list using the
++       * control BD descriptor.
++       */
++      gcl_config = &cbd.gcl_conf;
++
++      data_size = struct_size(gcl_data, entry, gcl_len);
++      gcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL);
++      if (!gcl_data)
++              return -ENOMEM;
++
++      gce = (struct gce *)(gcl_data + 1);
++
++      /* Set all gates open as default */
++      gcl_config->atc = 0xff;
++      gcl_config->acl_len = cpu_to_le16(gcl_len);
++
++      if (!admin_conf->base_time) {
++              gcl_data->btl =
++                      cpu_to_le32(enetc_rd(&priv->si->hw, ENETC_SICTR0));
++              gcl_data->bth =
++                      cpu_to_le32(enetc_rd(&priv->si->hw, ENETC_SICTR1));
++      } else {
++              gcl_data->btl =
++                      cpu_to_le32(lower_32_bits(admin_conf->base_time));
++              gcl_data->bth =
++                      cpu_to_le32(upper_32_bits(admin_conf->base_time));
++      }
++
++      gcl_data->ct = cpu_to_le32(admin_conf->cycle_time);
++      gcl_data->cte = cpu_to_le32(admin_conf->cycle_time_extension);
++
++      for (i = 0; i < gcl_len; i++) {
++              struct tc_taprio_sched_entry *temp_entry;
++              struct gce *temp_gce = gce + i;
++
++              temp_entry = &admin_conf->entries[i];
++
++              temp_gce->gate = (u8)temp_entry->gate_mask;
++              temp_gce->period = cpu_to_le32(temp_entry->interval);
++      }
++
++      cbd.length = cpu_to_le16(data_size);
++      cbd.status_flags = 0;
++
++      dma = dma_map_single(&priv->si->pdev->dev, gcl_data,
++                           data_size, DMA_TO_DEVICE);
++      if (dma_mapping_error(&priv->si->pdev->dev, dma)) {
++              netdev_err(priv->si->ndev, "DMA mapping failed!\n");
++              kfree(gcl_data);
++              return -ENOMEM;
++      }
++
++      cbd.addr[0] = lower_32_bits(dma);
++      cbd.addr[1] = upper_32_bits(dma);
++      cbd.cls = BDCR_CMD_PORT_GCL;
++      cbd.status_flags = 0;
++
++      enetc_wr(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET,
++               tge | ENETC_QBV_TGE);
++
++      err = enetc_send_cmd(priv->si, &cbd);
++      if (err)
++              enetc_wr(&priv->si->hw,
++                       ENETC_QBV_PTGCR_OFFSET,
++                       tge & (~ENETC_QBV_TGE));
++
++      dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_TO_DEVICE);
++      kfree(gcl_data);
++
++      return err;
++}
++
++int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data)
++{
++      struct tc_taprio_qopt_offload *taprio = type_data;
++      struct enetc_ndev_priv *priv = netdev_priv(ndev);
++      int err;
++      int i;
++
++      for (i = 0; i < priv->num_tx_rings; i++)
++              enetc_set_bdr_prio(&priv->si->hw,
++                                 priv->tx_ring[i]->index,
++                                 taprio->enable ? i : 0);
++
++      err = enetc_setup_taprio(ndev, taprio);
++
++      if (err)
++              for (i = 0; i < priv->num_tx_rings; i++)
++                      enetc_set_bdr_prio(&priv->si->hw,
++                                         priv->tx_ring[i]->index,
++                                         taprio->enable ? 0 : i);
++
++      return err;
++}