This adds support for IP version 5 of DWMAC. The new introduced
features are the Enhancements to Scheduled Traffic (EST) as
defined by IEEE802.1Qbv-2015 and Frame Preemption (FPE) as
defined by IEEE802.1Qbu.

In order to not break previous setups all the necessary
configuration is only performed when GMAC5 is detected and
only when all the necessary parameters are available in the
Device Tree.

EST:
The IEEE802.1Qbv-2015 defines the schedule for each of the
queues which makes the IP aware of traffic arrival time. This
information can be used to block the lower priority traffic
from transmission in this time window/slot.

FPE:
The IEEE802.1Qbu defines a mechanism which breaks interfering
frames into smaller fragments shuch that we have a more efficient
use of network bandwidth. This needs EST enabled.

Signed-off-by: Jose Abreu <joab...@synopsys.com>
Cc: David S. Miller <da...@davemloft.net>
Cc: Joao Pinto <jpi...@synopsys.com>
Cc: Giuseppe Cavallaro <peppe.cavall...@st.com>
Cc: Alexandre Torgue <alexandre.tor...@st.com>
---
 drivers/net/ethernet/stmicro/stmmac/Makefile       |  2 +-
 drivers/net/ethernet/stmicro/stmmac/common.h       |  4 +
 drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c  | 37 +++++++-
 drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.c   | 99 ++++++++++++++++++++++
 drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.h   | 57 +++++++++++++
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c  |  4 +
 .../net/ethernet/stmicro/stmmac/stmmac_platform.c  | 57 ++++++++++++-
 include/linux/stmmac.h                             | 11 +++
 8 files changed, 268 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.c
 create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.h

diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile 
b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 238307f..45b594e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -3,7 +3,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o 
ring_mode.o  \
              chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
              dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o     \
              mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o  \
-             dwmac4_dma.o dwmac4_lib.o dwmac4_core.o $(stmmac-y)
+             dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5_tsn.o $(stmmac-y)
 
 # Ordering matters. Generic driver must be last.
 obj-$(CONFIG_STMMAC_PLATFORM)  += stmmac-platform.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h 
b/drivers/net/ethernet/stmicro/stmmac/common.h
index e1e5ac0..d052b9f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -38,6 +38,7 @@
 #define        DWMAC_CORE_3_40 0x34
 #define        DWMAC_CORE_3_50 0x35
 #define        DWMAC_CORE_4_00 0x40
+#define DWMAC_CORE_5_00 0x50
 #define STMMAC_CHAN0   0       /* Always supported and default for all chips */
 
 /* These need to be power of two, and >= 4 */
@@ -501,6 +502,9 @@ struct stmmac_ops {
        void (*config_cbs)(struct mac_device_info *hw, u32 send_slope,
                           u32 idle_slope, u32 high_credit, u32 low_credit,
                           u32 queue);
+       /* Configure TSN features */
+       void (*config_tsn)(struct mac_device_info *hw,
+                          struct plat_stmmacenet_data *plat);
        /* Dump MAC registers */
        void (*dump_regs)(struct mac_device_info *hw, u32 *reg_space);
        /* Handle extra events on specific interrupts hw dependent */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 2f7d7ec..54370da 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -19,6 +19,7 @@
 #include <linux/io.h>
 #include "stmmac_pcs.h"
 #include "dwmac4.h"
+#include "dwmac5_tsn.h"
 
 static void dwmac4_core_init(struct mac_device_info *hw, int mtu)
 {
@@ -738,6 +739,38 @@ static void dwmac4_debug(void __iomem *ioaddr, struct 
stmmac_extra_stats *x,
        .set_filter = dwmac4_set_filter,
 };
 
+static const struct stmmac_ops dwmac5_ops = {
+       .core_init = dwmac4_core_init,
+       .set_mac = stmmac_dwmac4_set_mac,
+       .rx_ipc = dwmac4_rx_ipc_enable,
+       .rx_queue_enable = dwmac4_rx_queue_enable,
+       .rx_queue_prio = dwmac4_rx_queue_priority,
+       .tx_queue_prio = dwmac4_tx_queue_priority,
+       .rx_queue_routing = dwmac4_tx_queue_routing,
+       .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms,
+       .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms,
+       .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight,
+       .map_mtl_to_dma = dwmac4_map_mtl_dma,
+       .config_cbs = dwmac4_config_cbs,
+       .config_tsn = dwmac5_config_tsn,
+       .dump_regs = dwmac4_dump_regs,
+       .host_irq_status = dwmac4_irq_status,
+       .host_mtl_irq_status = dwmac4_irq_mtl_status,
+       .flow_ctrl = dwmac4_flow_ctrl,
+       .pmt = dwmac4_pmt,
+       .set_umac_addr = dwmac4_set_umac_addr,
+       .get_umac_addr = dwmac4_get_umac_addr,
+       .set_eee_mode = dwmac4_set_eee_mode,
+       .reset_eee_mode = dwmac4_reset_eee_mode,
+       .set_eee_timer = dwmac4_set_eee_timer,
+       .set_eee_pls = dwmac4_set_eee_pls,
+       .pcs_ctrl_ane = dwmac4_ctrl_ane,
+       .pcs_rane = dwmac4_rane,
+       .pcs_get_adv_lp = dwmac4_get_adv_lp,
+       .debug = dwmac4_debug,
+       .set_filter = dwmac4_set_filter,
+};
+
 struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins,
                                     int perfect_uc_entries, int *synopsys_id)
 {
@@ -778,7 +811,9 @@ struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, 
int mcbins,
        else
                mac->dma = &dwmac4_dma_ops;
 
-       if (*synopsys_id >= DWMAC_CORE_4_00)
+       if (*synopsys_id >= DWMAC_CORE_5_00)
+               mac->mac = &dwmac5_ops;
+       else if (*synopsys_id >= DWMAC_CORE_4_00)
                mac->mac = &dwmac410_ops;
        else
                mac->mac = &dwmac4_ops;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.c
new file mode 100644
index 0000000..fa1d506
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.c
@@ -0,0 +1,99 @@
+/*
+ * DWMAC5 TSN Core Functions
+ * Copyright (C) 2017 Synopsys, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Author: Jose Abreu <joab...@synopsys.com>
+ */
+
+#include "common.h"
+#include "dwmac5_tsn.h"
+
+static int dwmac5_est_write(void __iomem *ioaddr, u32 reg, u32 val, bool gcla)
+{
+       int timeout_ms = EST_WRITE_TIMEOUT_MS;
+       u32 ctrl = 0x0;
+
+       writel(val, ioaddr + MTL_EST_GCL_DATA);
+
+       ctrl |= reg;
+       ctrl |= gcla ? 0x0 : MTL_EST_GCRR;
+       writel(ctrl, ioaddr + MTL_EST_GCL_CONTROL);
+
+       ctrl |= MTL_EST_SRWO;
+       writel(ctrl, ioaddr + MTL_EST_GCL_CONTROL);
+
+       while (--timeout_ms) {
+               udelay(1000);
+               if (readl(ioaddr + MTL_EST_GCL_CONTROL) & MTL_EST_SRWO)
+                       continue;
+               break;
+       }
+
+       if (!timeout_ms)
+               return -ETIMEDOUT;
+       return 0;
+}
+
+static void dwmac5_config_est(struct mac_device_info *hw,
+                             struct plat_stmmacenet_data *plat)
+{
+       struct stmmac_est_cfg *est = &plat->est_cfg;
+       void __iomem *ioaddr = hw->pcsr;
+       u32 ctrl = 0x0, btr[2];
+       struct timespec64 now;
+       int i;
+
+       /* Add real time to offset */
+       ktime_get_real_ts64(&now);
+       btr[0] = (u32)now.tv_nsec + est->btr[0];
+       btr[1] = (u32)now.tv_sec + est->btr[1];
+
+       /* Write parameters */
+       dwmac5_est_write(ioaddr, MTL_EST_BTR_LOW, btr[0], false);
+       dwmac5_est_write(ioaddr, MTL_EST_BTR_HIGH, btr[1], false);
+       dwmac5_est_write(ioaddr, MTL_EST_CTR_LOW, est->ctr[0], false);
+       dwmac5_est_write(ioaddr, MTL_EST_CTR_HIGH, est->ctr[1], false);
+       dwmac5_est_write(ioaddr, MTL_EST_TER, est->ter, false);
+       dwmac5_est_write(ioaddr, MTL_EST_LLR, est->llr, false);
+
+       /* Write GCL table */
+       for (i = 0; i < est->llr; i++) {
+               u32 reg = (i << MTL_EST_ADDR_SHIFT) & MTL_EST_ADDR;
+               dwmac5_est_write(ioaddr, reg, est->gcl[i], true);
+       }
+
+       /* Enable EST */
+       ctrl |= MTL_EST_EEST;
+       writel(ctrl, ioaddr + MTL_EST_CONTROL);
+
+       /* Store table */
+       ctrl |= MTL_EST_SSWL;
+       writel(ctrl, ioaddr + MTL_EST_CONTROL);
+}
+
+void dwmac5_config_fp(struct mac_device_info *hw)
+{
+       void __iomem *ioaddr = hw->pcsr;
+       u32 ctrl;
+
+       /* Enable frame preemption */
+       ctrl = readl(ioaddr + GMAC_FPE_CTRL_STS);
+       ctrl |= GMAC_FPE_EFPE;
+       writel(ctrl, ioaddr + GMAC_FPE_CTRL_STS);
+}
+
+void dwmac5_config_tsn(struct mac_device_info *hw,
+                      struct plat_stmmacenet_data *plat)
+{
+       void __iomem *ioaddr = hw->pcsr;
+       u32 value = readl(ioaddr + GMAC_HW_FEATURE3);
+
+       if ((value & GMAC_HW_FEAT_ESTSEL) && plat->est_en)
+               dwmac5_config_est(hw, plat);
+       if ((value & GMAC_HW_FEAT_FPESEL) && plat->fp_en)
+               dwmac5_config_fp(hw);
+}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.h 
b/drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.h
new file mode 100644
index 0000000..07bda8b
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5_tsn.h
@@ -0,0 +1,57 @@
+/*
+ * DWMAC5 TSN Header File
+ * Copyright (C) 2017 Synopsys, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Author: Jose Abreu <joab...@synopsys.com>
+ */
+
+#ifndef __DWMAC5_TSN_H__
+#define __DWMAC5_TSN_H__
+
+#include <linux/stmmac.h>
+
+/* MAC registers */
+#define GMAC_HW_FEATURE3               0x00000128
+#define GMAC_FPE_CTRL_STS              0x00000234
+
+/* MAC HW features3 bitmap */
+#define GMAC_HW_FEAT_FPESEL            BIT(26)
+#define GMAC_HW_FEAT_ESTSEL            BIT(16)
+
+/* MAC FPE control/status bitmap */
+#define GMAC_FPE_EFPE                  BIT(0)
+
+/* MTL registers */
+#define MTL_EST_CONTROL                        0x00000c50
+#define MTL_EST_GCL_CONTROL            0x00000c80
+#define MTL_EST_GCL_DATA               0x00000c84
+
+/* EST control bitmap */
+#define MTL_EST_EEST                   BIT(0)
+#define MTL_EST_SSWL                   BIT(1)
+
+/* EST GCL control bitmap */
+#define MTL_EST_ADDR_SHIFT             8
+#define MTL_EST_ADDR                   GENMASK(19, 8)
+#define MTL_EST_GCRR                   BIT(2)
+#define MTL_EST_SRWO                   BIT(0)
+
+/* EST GCRA addresses */
+#define MTL_EST_BTR_LOW                        (0x0 << MTL_EST_ADDR_SHIFT)
+#define MTL_EST_BTR_HIGH               (0x1 << MTL_EST_ADDR_SHIFT)
+#define MTL_EST_CTR_LOW                        (0x2 << MTL_EST_ADDR_SHIFT)
+#define MTL_EST_CTR_HIGH               (0x3 << MTL_EST_ADDR_SHIFT)
+#define MTL_EST_TER                    (0x4 << MTL_EST_ADDR_SHIFT)
+#define MTL_EST_LLR                    (0x5 << MTL_EST_ADDR_SHIFT)
+
+/* Misc */
+#define EST_WRITE_TIMEOUT_MS           5
+
+void dwmac5_config_tsn(struct mac_device_info *hw,
+                      struct plat_stmmacenet_data *plat);
+
+#endif /* __DWMAC5_TSN_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c 
b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index c7a894e..24861e2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2418,6 +2418,10 @@ static void stmmac_mtl_configuration(struct stmmac_priv 
*priv)
        if (tx_queues_count > 1 && priv->hw->mac->config_cbs)
                stmmac_configure_cbs(priv);
 
+       /* Configure TSN features */
+       if (tx_queues_count > 1 && priv->hw->mac->config_tsn)
+               priv->hw->mac->config_tsn(priv->hw, priv->plat);
+
        /* Map RX MTL to DMA channels */
        if (priv->hw->mac->map_mtl_to_dma)
                stmmac_rx_queue_dma_chan_map(priv);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c 
b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 8a280b4..21f59fe 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -280,6 +280,59 @@ static void stmmac_mtl_setup(struct platform_device *pdev,
        of_node_put(q_node);
 }
 
+static int stmmac_est_setup(struct device_node *node,
+                           struct platform_device *pdev,
+                           struct plat_stmmacenet_data *plat)
+{
+       struct stmmac_est_cfg *est = &plat->est_cfg;
+       int ret;
+
+       if (of_property_read_u32_array(node, "snps,btr", est->btr, 2))
+               return -EINVAL;
+       if (of_property_read_u32_array(node, "snps,ctr", est->ctr, 2))
+               return -EINVAL;
+       if (of_property_read_u32(node, "snps,ter", &est->ter))
+               return -EINVAL;
+       
+       est->llr = of_property_count_u32_elems(node, "snps,gcl");
+       if (!est->llr)
+               return -EINVAL;
+
+       est->gcl = devm_kzalloc(&pdev->dev, sizeof(*est->gcl) * est->llr,
+                       GFP_KERNEL);
+       if (!est->gcl)
+               return -ENOMEM;
+
+       ret = of_property_read_u32_array(node, "snps,gcl", est->gcl, est->llr);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void stmmac_tsn_setup(struct platform_device *pdev,
+                            struct plat_stmmacenet_data *plat)
+{
+       struct device_node *node;
+
+       node = of_parse_phandle(pdev->dev.of_node, "snps,tsn-config", 0);
+       if (!node)
+               return;
+
+       /* Parse EST values, if a property is missing EST is disabled */
+       plat->est_en = of_property_read_bool(node, "snps,est");
+       if (plat->est_en && stmmac_est_setup(node, pdev, plat)) {
+               pr_warn("Disabling EST because of bad DT parameters.");
+               plat->est_en = false;
+       }
+
+       /* FP needs EST enabled */
+       if (plat->est_en)
+               plat->fp_en = of_property_read_bool(node, "snps,fp");
+
+       of_node_put(node);
+}
+
 /**
  * stmmac_dt_phy - parse device-tree driver parameters to allocate PHY 
resources
  * @plat: driver data platform structure
@@ -453,7 +506,8 @@ struct plat_stmmacenet_data *
        }
 
        if (of_device_is_compatible(np, "snps,dwmac-4.00") ||
-           of_device_is_compatible(np, "snps,dwmac-4.10a")) {
+           of_device_is_compatible(np, "snps,dwmac-4.10a") ||
+           of_device_is_compatible(np, "snps,dwmac-5.00")) {
                plat->has_gmac4 = 1;
                plat->has_gmac = 0;
                plat->pmt = 1;
@@ -497,6 +551,7 @@ struct plat_stmmacenet_data *
        plat->axi = stmmac_axi_setup(pdev);
 
        stmmac_mtl_setup(pdev, plat);
+       stmmac_tsn_setup(pdev, plat);
 
        /* clock setup */
        plat->stmmac_clk = devm_clk_get(&pdev->dev,
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 108739f..9b9a102 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -144,6 +144,14 @@ struct stmmac_txq_cfg {
        u32 prio;
 };
 
+struct stmmac_est_cfg {
+       u32 btr[2];
+       u32 ctr[2];
+       u32 ter;
+       u32 llr;
+       u32 *gcl;
+};
+
 struct plat_stmmacenet_data {
        int bus_id;
        int phy_addr;
@@ -190,5 +198,8 @@ struct plat_stmmacenet_data {
        bool tso_en;
        int mac_port_sel_speed;
        bool en_tx_lpi_clockgating;
+       bool est_en;
+       struct stmmac_est_cfg est_cfg;
+       bool fp_en;
 };
 #endif
-- 
1.9.1


Reply via email to