kernel/0012-feat-noc-Add-noc-driver.patch

7182 lines
213 KiB
Diff

From 51ad16ca810aa67729aae785bf5c5fb2636ef62f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9Chuangyifeng=E2=80=9D?=
<huangyifeng@eswincomputing.com>
Date: Tue, 21 May 2024 14:17:38 +0800
Subject: [PATCH 012/219] feat(noc):Add noc driver
Changelogs:
1.Added noc driver
---
drivers/interconnect/Kconfig | 2 +-
drivers/interconnect/Makefile | 2 +-
drivers/interconnect/eswin/Kconfig | 24 +
drivers/interconnect/eswin/Makefile | 10 +
drivers/interconnect/eswin/noc.c | 2341 +++++++++++++++++
drivers/interconnect/eswin/noc.h | 363 +++
drivers/interconnect/eswin/noc_profiler.c | 695 +++++
.../interconnect/eswin/noc_profiler_sysfs.c | 362 +++
drivers/interconnect/eswin/noc_regs.h | 633 +++++
drivers/interconnect/eswin/noc_stat.c | 966 +++++++
drivers/interconnect/eswin/noc_stat_sysfs.c | 350 +++
drivers/interconnect/eswin/noc_sysfs.c | 783 ++++++
drivers/interconnect/eswin/of_noc.c | 95 +
drivers/interconnect/eswin/win2030_noc_wdt.c | 189 ++
.../eswin/win2030_sideband_manager.c | 172 ++
include/linux/win2030_noc.h | 46 +
16 files changed, 7031 insertions(+), 2 deletions(-)
create mode 100644 drivers/interconnect/eswin/Kconfig
create mode 100644 drivers/interconnect/eswin/Makefile
create mode 100644 drivers/interconnect/eswin/noc.c
create mode 100644 drivers/interconnect/eswin/noc.h
create mode 100644 drivers/interconnect/eswin/noc_profiler.c
create mode 100644 drivers/interconnect/eswin/noc_profiler_sysfs.c
create mode 100644 drivers/interconnect/eswin/noc_regs.h
create mode 100644 drivers/interconnect/eswin/noc_stat.c
create mode 100644 drivers/interconnect/eswin/noc_stat_sysfs.c
create mode 100644 drivers/interconnect/eswin/noc_sysfs.c
create mode 100644 drivers/interconnect/eswin/of_noc.c
create mode 100644 drivers/interconnect/eswin/win2030_noc_wdt.c
create mode 100644 drivers/interconnect/eswin/win2030_sideband_manager.c
create mode 100644 include/linux/win2030_noc.h
diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
index 5faa8d2aecff..4fe3d2484919 100644
--- a/drivers/interconnect/Kconfig
+++ b/drivers/interconnect/Kconfig
@@ -14,7 +14,7 @@ if INTERCONNECT
source "drivers/interconnect/imx/Kconfig"
source "drivers/interconnect/qcom/Kconfig"
source "drivers/interconnect/samsung/Kconfig"
-
+source "drivers/interconnect/eswin/Kconfig"
config INTERCONNECT_CLK
tristate
depends on COMMON_CLK
diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
index d0888babb9a1..402059461226 100644
--- a/drivers/interconnect/Makefile
+++ b/drivers/interconnect/Makefile
@@ -7,5 +7,5 @@ obj-$(CONFIG_INTERCONNECT) += icc-core.o
obj-$(CONFIG_INTERCONNECT_IMX) += imx/
obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/
obj-$(CONFIG_INTERCONNECT_SAMSUNG) += samsung/
-
+obj-$(CONFIG_INTERCONNECT_ESWIN) += eswin/
obj-$(CONFIG_INTERCONNECT_CLK) += icc-clk.o
diff --git a/drivers/interconnect/eswin/Kconfig b/drivers/interconnect/eswin/Kconfig
new file mode 100644
index 000000000000..5c77446dfaa6
--- /dev/null
+++ b/drivers/interconnect/eswin/Kconfig
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config INTERCONNECT_ESWIN
+ tristate "Enable Eswin SoC NOC Management driver"
+ default y
+ help
+ Say yes to enable NOC management support for Eswin SoC.
+ say M to compile it as a module.
+ If in doubt, say N.
+
+config INTERCONNECT_ESWIN_DEBUG
+ bool "Debug Eswin NOC"
+ depends on INTERCONNECT_ESWIN
+ help
+ This enables verbose debug output of the eswin NOC driver.
+ If unsure, say N.
+
+config INTERCONNECT_WIN2030_WDT
+ bool "win2030 interconnect wdt driver"
+ default y
+ help
+ This driver will log the NOC wdt interrupt, usually indicate a timeout on NOC
+ system.
+ depends on INTERCONNECT_ESWIN
diff --git a/drivers/interconnect/eswin/Makefile b/drivers/interconnect/eswin/Makefile
new file mode 100644
index 000000000000..01e458b6e3c3
--- /dev/null
+++ b/drivers/interconnect/eswin/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for Arteris FlexNOC on-chip interconnect
+#
+
+obj-$(CONFIG_INTERCONNECT_WIN2030_WDT) += win2030_noc_wdt.o
+ccflags-$(CONFIG_INTERCONNECT_ESWIN_DEBUG) += -DDEBUG
+
+obj-$(CONFIG_INTERCONNECT_ESWIN) += eswin_noc_drv.o
+eswin_noc_drv-objs:= noc.o noc_sysfs.o noc_stat_sysfs.o noc_stat.o noc_profiler.o noc_profiler_sysfs.o of_noc.o win2030_sideband_manager.o
+
diff --git a/drivers/interconnect/eswin/noc.c b/drivers/interconnect/eswin/noc.c
new file mode 100644
index 000000000000..6712cf02f5f2
--- /dev/null
+++ b/drivers/interconnect/eswin/noc.c
@@ -0,0 +1,2341 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/debugfs.h>
+
+#include "noc.h"
+#include "noc_regs.h"
+
+//#pragma GCC optimize("O0")
+
+#define XGOLD632_NOC_ERROR_ID 0x011B0B00
+#define XGOLD631_NOC_ERROR_ID 0x00FA1200
+#define XGOLD726_NOC_ERROR_ID 0x010C0C00
+
+struct win2030_noc_control win2030_noc_ctrl;
+
+/**
+ * Tasklet called by interrupt when a measurement is finished.
+ * @param data a pointer to the struct win2030_noc_probe which generated the
+ * interrupt
+ */
+void win2030_noc_stat_measure_tasklet(unsigned long data)
+{
+ struct win2030_noc_probe *probe = (struct win2030_noc_probe *)data;
+
+ if (probe_t_trans == probe->type) {
+ win2030_noc_prof_do_measure(probe);
+ } else {
+ win2030_noc_stat_do_measure(probe);
+ }
+}
+
+/**
+ *
+ * @param _dev
+ * @param np
+ * @param property_name
+ * @return
+ */
+const char **win2030_get_strings_from_dts(struct device *_dev,
+ struct device_node *np,
+ const char *property_name)
+{
+ struct property *prop;
+ const char *s;
+ const char **lut = NULL;
+ int i = 0, lut_len;
+
+ if (of_property_read_bool(np, property_name)) {
+ lut_len = of_property_count_strings(np, property_name);
+
+ if (lut_len < 0) {
+ dev_err(_dev, "Invalid description of %s for %s\n",
+ property_name, np->name);
+ return ERR_PTR(lut_len);
+ }
+
+ lut = kcalloc(lut_len + 1, sizeof(char *), GFP_KERNEL);
+ if (!lut)
+ return ERR_PTR(-ENOMEM);
+
+ of_property_for_each_string(np, property_name, prop, s)
+ lut[i++] = s;
+
+ /* To find out the end of the list */
+ lut[lut_len] = NULL;
+ }
+
+ return lut;
+}
+
+static struct win2030_bitfield *win2030_alloc_bitfield(void)
+{
+ struct win2030_bitfield *bitfield;
+
+ bitfield = kzalloc(sizeof(*bitfield), GFP_KERNEL);
+ if (!bitfield)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&bitfield->lock);
+ INIT_LIST_HEAD(&bitfield->link);
+ return bitfield;
+}
+
+struct win2030_bitfield *win2030_new_bitfield(struct win2030_register *reg,
+ const char *name,
+ unsigned offset,
+ unsigned width,
+ const char **lut)
+{
+ struct win2030_bitfield *bf;
+ unsigned long flags;
+
+ if (!reg)
+ return ERR_PTR(-EINVAL);
+
+ bf = win2030_alloc_bitfield();
+ if (!bf)
+ return ERR_PTR(-ENOMEM);
+
+ bf->parent = reg;
+ bf->name = kstrdup(name, GFP_KERNEL);
+ bf->offset = offset;
+ bf->length = width;
+ bf->mask = ((BIT(width) - 1) << offset);
+ bf->lut = lut;
+
+ spin_lock_irqsave(&reg->lock, flags);
+ list_add_tail(&bf->link, &reg->bitfields);
+ spin_unlock_irqrestore(&reg->lock, flags);
+
+ return bf;
+}
+
+static void win2030_free_bitfield(struct win2030_bitfield *bf)
+{
+ kfree(bf->name);
+ kfree(bf);
+}
+
+static struct win2030_bitfield *win2030_get_bitfield(struct device *_dev,
+ struct device_node *np)
+{
+ int ret, i;
+ unsigned values[2];
+ struct win2030_bitfield *bitfield;
+ u64 *values_tab;
+
+ if (!np->name)
+ return ERR_PTR(-EINVAL);
+
+ bitfield = win2030_alloc_bitfield();
+ if (!bitfield)
+ return ERR_PTR(-ENOMEM);
+
+ bitfield->name = np->name;
+
+ ret = of_property_read_u32_array(np, "offset,length", values, 2);
+ if (ret) {
+ dev_err(_dev,
+ "\"Offset,length\" properties of register %s\n missing",
+ bitfield->name);
+ ret = -EINVAL;
+ goto free_bf;
+ }
+
+ bitfield->offset = values[0];
+ bitfield->length = values[1];
+ bitfield->mask = (((1UL << bitfield->length) - 1) << bitfield->offset);
+
+ if ((bitfield->offset > 31) || (bitfield->length > 32)) {
+ ret = -EINVAL;
+ goto free_bf;
+ }
+
+ of_property_read_string(np, "description", &bitfield->description);
+
+ bitfield->lut = win2030_get_strings_from_dts(_dev, np, "lut");
+ if (IS_ERR(bitfield->lut)) {
+ ret = PTR_ERR(bitfield->lut);
+ dev_err(_dev, "Error %d while parsing lut of %s\n", ret,
+ bitfield->name);
+ bitfield->lut = NULL;
+ }
+ bitfield->aperture_size = of_property_count_elems_of_size(np,
+ "aperture-idx,aperture-base", sizeof(u64) * 4);
+ if (bitfield->aperture_size > 0) {
+ bitfield->init_flow = devm_kcalloc(_dev, bitfield->aperture_size * 4,
+ sizeof(u64), GFP_KERNEL);
+ if (!bitfield->init_flow) {
+ ret = -ENOMEM;
+ goto free_all;
+ }
+ bitfield->target_flow = bitfield->init_flow + bitfield->aperture_size;
+ bitfield->target_sub_range = bitfield->target_flow + bitfield->aperture_size;
+ bitfield->aperture_base = bitfield->target_sub_range + bitfield->aperture_size;
+
+ values_tab = devm_kcalloc(_dev, bitfield->aperture_size * 4,
+ sizeof(*values_tab), GFP_KERNEL);
+ if (!values_tab) {
+ ret = -ENOMEM;
+ goto free_all;
+ }
+ ret = of_property_read_u64_array(np, "aperture-idx,aperture-base",
+ values_tab, bitfield->aperture_size * 4);
+ if (ret) {
+ dev_err(_dev,
+ "Error while parsing aperture of %s bitfield, ret %d!\n",
+ bitfield->name, ret);
+ goto free_all;
+ }
+ for (i = 0; i < bitfield->aperture_size; i++) {
+ bitfield->init_flow[i] = values_tab[4 * i];
+ bitfield->target_flow[i] = values_tab[4 * i + 1];
+ bitfield->target_sub_range[i] = values_tab[4 * i + 2];
+ bitfield->aperture_base[i] = values_tab[4 * i + 3];
+ }
+ devm_kfree(_dev, values_tab);
+ }
+
+ dev_dbg(_dev, "Bitfield %s, offset %x, length %d created\n",
+ bitfield->name, bitfield->offset, bitfield->length);
+
+ return bitfield;
+
+free_all:
+ kfree(bitfield->lut);
+free_bf:
+ kfree(bitfield);
+ return ERR_PTR(ret);
+}
+
+static struct win2030_register *win2030_alloc_register(void)
+{
+ struct win2030_register *reg;
+
+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+ if (!reg)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock_init(&reg->lock);
+ spin_lock_init(&reg->hw_lock);
+ INIT_LIST_HEAD(&reg->link);
+ INIT_LIST_HEAD(&reg->bitfields);
+
+ return reg;
+}
+
+struct win2030_register *win2030_new_register(struct device *parent,
+ const char *name,
+ void __iomem *base,
+ unsigned offset,
+ unsigned width)
+{
+ struct win2030_register *reg;
+
+ reg = win2030_alloc_register();
+ if (!reg)
+ return ERR_PTR(-ENOMEM);
+
+ reg->parent = parent;
+ reg->name = kstrdup(name, GFP_KERNEL);
+ reg->base = base;
+ reg->offset = offset;
+ reg->length = width;
+
+ return reg;
+}
+
+static void win2030_free_register(struct win2030_register *reg)
+{
+ struct win2030_bitfield *bf, *tmp;
+
+ list_for_each_entry_safe(bf, tmp, &reg->bitfields, link)
+ win2030_free_bitfield(bf);
+
+ kfree(reg->name);
+ kfree(reg);
+}
+
+/**
+ * Create ErrorLog register from dts file
+ * @param _dev
+ * @param np
+ * @return
+ */
+static struct win2030_register *win2030_get_err_register(struct device *_dev,
+ struct device_node *np)
+{
+ int ret;
+ struct device_node *child = NULL;
+ unsigned values[2];
+ struct win2030_register *reg;
+
+ if (!np->name)
+ return ERR_PTR(-EINVAL);
+
+ reg = win2030_alloc_register();
+ if (!reg)
+ return ERR_PTR(-ENOMEM);
+
+ reg->name = np->name;
+ reg->parent = _dev;
+ reg->base = of_iomap(_dev->of_node, 0);
+
+ ret = of_property_read_u32_array(np, "offset,length", values, 2);
+ if (ret) {
+ dev_err(_dev,
+ "\"Offset,length\" properties of register %s\n missing",
+ reg->name);
+ ret = -EINVAL;
+ goto free_reg;
+ }
+
+ reg->offset = values[0];
+ reg->length = values[1];
+ if (reg->length > 32) {
+ ret = -EINVAL;
+ goto free_reg;
+ }
+
+ /* Not mandatory properties */
+ of_property_read_string(np, "description", &reg->description);
+
+ ret = of_property_read_u32(np, "aperture-link", values);
+ if (!ret)
+ reg->aperture_link = values[0];
+ else
+ reg->aperture_link = -1;
+
+ ret = of_property_read_u32(np, "msb-link", values);
+ if (!ret)
+ reg->msb_link = values[0];
+ else
+ reg->msb_link = -1;
+
+ /* Create bitfields */
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, "eswin,win2030,bitfield")) {
+ struct win2030_bitfield *bitfield;
+ bitfield = win2030_get_bitfield(_dev, child);
+ if (!(IS_ERR(bitfield))) {
+ list_add_tail(&bitfield->link, &reg->bitfields);
+ bitfield->parent = reg;
+ }
+ }
+ }
+ dev_dbg(_dev, "Register %s, offset %x, length %d created\n", reg->name,
+ reg->offset, reg->length);
+ return reg;
+
+free_reg:
+ kfree(reg);
+ return ERR_PTR(ret);
+}
+
+int win2030_find_register_by_name(
+ const struct win2030_noc_device *noc_device,
+ const char *name, struct win2030_register **reg)
+{
+ struct win2030_register *tmp_reg = NULL;
+
+ list_for_each_entry(tmp_reg, &noc_device->registers, link) {
+ if (!strcmp(name, tmp_reg->name)) {
+ *reg = tmp_reg;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+#if 0
+int win2030_find_routeid_idx_by_name(struct device *_dev,
+ const char *sub_route_id, const char *name, int *idx)
+{
+ int ret;
+ struct win2030_register *reg;
+ struct win2030_bitfield *bf;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+
+ ret = win2030_find_register_by_name(noc_device, "ErrorLogger1", &reg);
+ if (0 != ret) {
+ dev_err(_dev, "%s register not found!\n", "ErrorLogger1");
+ return -EINVAL;
+ }
+ list_for_each_entry(bf, &reg->bitfields, link) {
+ if (strcmp(bf->name, sub_route_id) == 0) {
+ ret = xgold_bitfield_get_value_from_enum(bf, name, idx);
+ if (0 == ret) {
+ break;
+ }
+ }
+ }
+ return ret;
+}
+#endif
+
+/**
+ * Create ErrorLogx register set from DTS file
+ * @param _dev
+ * @param regs
+ * @return
+ */
+static int win2030_get_err_registers(struct device *_dev, struct list_head *regs)
+{
+ struct device_node *parent = _dev->of_node;
+ struct device_node *np = NULL;
+ struct win2030_register *reg;
+
+ for_each_child_of_node(parent, np) {
+ if (of_device_is_compatible(np, "eswin,win2030,register")) {
+ reg = win2030_get_err_register(_dev, np);
+ if (!(IS_ERR(reg)))
+ list_add_tail(&reg->link, regs);
+ }
+ }
+
+ return 0;
+}
+
+static const char *noc_cnt_src_evt_lut[] = {
+ "off",
+ "cycle",
+ "idle",
+ "xfer",
+ "busy",
+ "wait",
+ "pkt",
+ "lut",
+ "byte",
+ "press0",
+ "disabled",
+ "disabled",
+ "filt0",
+ "filt1",
+ "filt2",
+ "filt3",
+ "chain",
+ "lut_byte_en",
+ "lut_byte",
+ "filt_byte_en",
+ "filt_byte",
+ "disabled",
+ "disabled",
+ "disabled",
+ "disabled",
+ "disabled",
+ "disabled",
+ "disabled",
+ "disabled",
+ "disabled",
+ "disabled",
+ "disabled",
+ NULL,
+};
+
+static const char *noc_cnt_alarm_mode_lut[] = {
+ "off",
+ "min",
+ "max",
+ "min_max",
+ NULL
+};
+
+static const char *win2030_noc_enable_enum[] = {
+ "disable",
+ "enable",
+ NULL,
+};
+
+static const char *win2030_noc_on_off_enum[] = {
+ "off",
+ "on",
+ NULL
+};
+
+static const char *win2030_noc_qos_mode_enum[] = {
+ "fixed",
+ "limiter",
+ "bypass",
+ "regulator",
+ NULL
+};
+
+static struct of_device_id win2030_noc_of_match[] = {
+ {
+ .compatible = "eswin,win2030-noc",
+ },
+ {},
+};
+
+#include <linux/sched.h>
+#include <linux/sched/debug.h>
+void stack_dump(void)
+{
+ struct task_struct *task_list = NULL;
+
+ for_each_process(task_list) {
+ show_stack(task_list, NULL, KERN_EMERG);
+ }
+}
+static int win2030_noc_get_error(struct win2030_noc_device *noc_device)
+{
+ void __iomem *base = noc_device->hw_base;
+ struct win2030_noc_error *noc_err;
+ unsigned long flags;
+ int i;
+ char buf[1024] = {'\0'};
+
+ struct device *mydev = noc_device->dev;
+
+ noc_err = kzalloc(sizeof(struct win2030_noc_error), GFP_ATOMIC);
+ if (!noc_err)
+ return -ENOMEM;
+
+ noc_err->timestamp = get_jiffies_64();
+ dev_err(mydev, "Error interrupt happen!\n");
+ for (i = 0; i < noc_device->error_logger_cnt; i++) {
+ noc_err->err[noc_device->err_log_lut[i]] = ioread32(ERRLOG_0_ERRLOG0(base) +
+ noc_device->err_log_lut[i] * sizeof(u32));
+ dev_err(mydev, "ErrLog%d 0x%08x", noc_device->err_log_lut[i],
+ noc_err->err[noc_device->err_log_lut[i]]);
+ }
+ spin_lock_irqsave(&noc_device->lock, flags);
+ list_add_tail(&noc_err->link, &noc_device->err_queue);
+ spin_unlock_irqrestore(&noc_device->lock, flags);
+
+ noc_error_dump(buf, noc_device, noc_err);
+ dev_err(mydev, "%s\n", buf);
+ //stack_dump();
+ iowrite32(1, ERRLOG_0_ERRCLR(base));
+ return 0;
+}
+
+static irqreturn_t win2030_noc_error_irq(int irq, void *dev)
+{
+ struct win2030_noc_device *noc_device = dev;
+ void __iomem *base = noc_device->hw_base;
+
+ while (ioread32(ERRLOG_0_ERRVLD(base)))
+ win2030_noc_get_error(noc_device);
+
+ BUG_ON(noc_device->trap_on_error == true);
+
+ return IRQ_HANDLED;
+}
+
+void zebu_stop(void)
+{
+ void __iomem *base = ioremap(0x51810000, 0x1000);
+ printk("%s %d\n",__func__,__LINE__);
+ writel_relaxed(0x8000, base + 0x668);
+}
+
+/**
+ * Copy ErrorLog registers into error_registers field + Enable error interrupt
+ * @param _dev
+ * @return
+ */
+static int win2030_noc_error_init(struct device *_dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ char reg_name[16];
+ struct win2030_register *reg;
+ int i;
+ int ret;
+ int cnt;
+ u32 *err_log_lut = NULL;
+ struct platform_device *pdev = to_platform_device(_dev);
+ struct device_node *np = _dev->of_node;
+
+ cnt = of_property_count_u32_elems(np, "errlogger,idx");
+ if (cnt <= 0) {
+ return cnt;
+ }
+ noc_device->error_logger_cnt = cnt;
+ err_log_lut = devm_kcalloc(_dev, cnt, sizeof(u32), GFP_KERNEL);
+ if (!err_log_lut) {
+ return -ENOMEM;
+ }
+ ret = of_property_read_u32_array(np, "errlogger,idx", err_log_lut, cnt);
+ if (ret) {
+ dev_err(_dev, "Error while parsing errlogger,idx, ret %d!\n", ret);
+ return ret;
+ }
+ noc_device->err_log_lut = err_log_lut;
+
+ for (i = 0; i < noc_device->error_logger_cnt; i++) {
+ scnprintf(reg_name, ARRAY_SIZE(reg_name), "ErrorLogger%d",
+ err_log_lut[i]);
+ ret = win2030_find_register_by_name(noc_device, reg_name, &reg);
+ if (0 != ret) {
+ dev_err(_dev, "%s register not found!\n", reg_name);
+ return -EINVAL;
+ }
+ noc_device->error_registers[noc_device->err_log_lut[i]] = reg;
+ }
+ noc_device->err_irq = platform_get_irq_byname(pdev, "error");
+ if (noc_device->err_irq <= 0) {
+ dev_warn(_dev, "error irq not defined!\n");
+ ret = noc_device->err_irq;
+ return ret;
+ } else {
+ ret = devm_request_irq(_dev, noc_device->err_irq, win2030_noc_error_irq,
+ IRQF_SHARED, dev_name(_dev), noc_device);
+ if (ret) {
+ dev_err(_dev, "Error %d while installing error irq\n",
+ (int)noc_device->err_irq);
+ return ret;
+ }
+ }
+ dev_info(_dev, "enable error logging\n");
+ iowrite32(1, ERRLOG_0_FAULTEN(noc_device->hw_base));
+ return 0;
+}
+
+static struct win2030_bitfield *win2030_noc_create_register_and_bitfield(
+ struct win2030_noc_probe *probe,
+ const char *name,
+ void *hw_base,
+ unsigned reg_offset,
+ unsigned bf_offset,
+ unsigned bf_width,
+ const char **bf_lut)
+{
+ struct win2030_register *reg;
+
+ if (probe == NULL)
+ return ERR_PTR(-EINVAL);
+
+ reg = win2030_new_register(probe->dev, name, hw_base, reg_offset, 32);
+ if (IS_ERR_OR_NULL(reg))
+ return ERR_PTR(-EINVAL);
+
+ return win2030_new_bitfield(reg, name, bf_offset, bf_width, bf_lut);
+}
+
+static irqreturn_t win2030_noc_stat_irq(int irq, void *dev)
+{
+ unsigned status;
+ struct win2030_noc_probe *probe = dev;
+ int ret = 0;
+
+ status = ioread32(probe->hw_base + PROBE_STATALARMSTATUS);
+ dev_dbg_once(probe->dev, "counter irq occur, status %d\n", status);
+ if (status) {
+ /* Disable alarm and statics*/
+ ret |= win2030_noc_reg_write(probe->main_ctl, PROBE_MAINCTL_ALARMEN_MASK,
+ PROBE_MAINCTL_ALARMEN_OFFSET, 0);
+
+ ret |= win2030_noc_reg_write(probe->main_ctl, PROBE_MAINCTL_STATEN_MASK,
+ PROBE_MAINCTL_STATEN_OFFSET, 0);
+ if (ret < 0) {
+ dev_err(probe->dev, "%s failed to disable alarm and "
+ "statics, ret %d", probe->id, ret);
+ }
+ //iowrite32(0, probe->hw_base + PROBE_MAINCTL);
+
+ /* Clear alarm */
+ iowrite32(1, probe->hw_base + PROBE_STATALARMCLR);
+ tasklet_schedule(&probe->tasklet);
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * Create a counter register set (includes PortSel, AlarmMode, Src, Val)
+ * @param cnt
+ * @param probe
+ * @param cnt_id
+ * @return
+ */
+static int win2030_noc_init_counter(struct win2030_noc_counter *cnt,
+ struct win2030_noc_probe *probe,
+ unsigned cnt_id)
+{
+ struct win2030_bitfield *bf;
+
+ if ((cnt == NULL) || (probe == NULL))
+ return -EINVAL;
+
+ cnt->id = cnt_id;
+ cnt->parent = probe;
+ INIT_LIST_HEAD(&cnt->register_link);
+#if 0
+ /* Port Selection */
+ bf = win2030_noc_create_register_and_bitfield(probe, "PortSel",
+ PROBE_COUNTERS_PORTSEL(cnt->id),
+ PROBE_COUNTERS_PORTSEL_COUNTERS_PORTSEL_OFFSET,
+ PROBE_COUNTERS_PORTSEL_COUNTERS_PORTSEL_WIDTH,
+ probe->available_portsel);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ cnt->portsel = bf;
+#endif
+ /* AlarmMode */
+ bf = win2030_noc_create_register_and_bitfield(probe, "AlarmMode",
+ probe->hw_base,
+ PROBE_COUNTERS_ALARMMODE(cnt->id),
+ PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_OFFSET,
+ PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_WIDTH,
+ noc_cnt_alarm_mode_lut);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ cnt->alarm_mode = bf;
+ list_add_tail(&bf->parent->parent_link, &cnt->register_link);
+
+ /* Src */
+ bf = win2030_noc_create_register_and_bitfield(probe, "Src",
+ probe->hw_base,
+ PROBE_COUNTERS_SRC(cnt->id),
+ PROBE_COUNTERS_SRC_INTEVENT_OFFSET,
+ PROBE_COUNTERS_SRC_INTEVENT_WIDTH,
+ noc_cnt_src_evt_lut);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ cnt->source_event = bf;
+ list_add_tail(&bf->parent->parent_link, &cnt->register_link);
+
+ /* Val */
+ bf = win2030_noc_create_register_and_bitfield(probe, "Val",
+ probe->hw_base,
+ PROBE_COUNTERS_VAL(cnt->id),
+ PROBE_COUNTERS_VAL_COUNTERS_VAL_OFFSET,
+ PROBE_COUNTERS_VAL_COUNTERS_VAL_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ cnt->value = bf;
+ list_add_tail(&bf->parent->parent_link, &cnt->register_link);
+
+ return 0;
+}
+
+static struct win2030_register *win2030_noc_get_compatible_register(struct
+ win2030_noc_device
+ *noc_device,
+ const char
+ *compatible)
+{
+ struct device *_dev = noc_device->dev;
+ struct device_node *parent = _dev->of_node;
+ struct device_node *np = NULL;
+ struct win2030_register *reg = NULL;
+ int ret;
+
+ for_each_child_of_node(parent, np) {
+ if (of_device_is_compatible(np, compatible)) {
+ list_for_each_entry(reg, &noc_device->registers, link) {
+ ret = strcmp(np->name, reg->name);
+ if (ret == 0)
+ return reg;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Create a filter register set (includes RouteIdBase, RouteIdMask, AddrBase_Low,
+ * WindowSize, SecurityBase, SecurityMask, Opcode, Status, Length, Urgency)
+ * @param noc_device
+ * @param filter
+ * @param probe
+ * @param id
+ * @return
+ */
+static int win2030_noc_init_filter(struct win2030_noc_device *noc_device,
+ struct win2030_noc_filter *filter,
+ struct win2030_noc_probe *probe, unsigned id)
+{
+ struct win2030_bitfield *bf, *bf_template = NULL;
+ struct win2030_register *reg, *reg_template;
+ unsigned route_idmask_width;
+
+ if ((noc_device == NULL) || (filter == NULL) || (probe == NULL))
+ return -EINVAL;
+
+ filter->id = id;
+ filter->parent = probe;
+ INIT_LIST_HEAD(&filter->register_link);
+
+ /*"RouteIdBase"*/
+ reg_template = win2030_noc_get_compatible_register(noc_device,
+ "eswin,win2030,noc,filter,routeid");
+ if (!reg_template)
+ return -ENODEV;
+
+ reg = win2030_new_register(probe->dev, "RouteIdBase", probe->hw_base,
+ PROBE_FILTERS_ROUTEIDBASE(id), 32);
+
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ route_idmask_width = 0;
+ list_for_each_entry(bf_template, &reg_template->bitfields, link) {
+ bf = win2030_new_bitfield(reg, bf_template->name,
+ bf_template->offset,
+ bf_template->length, bf_template->lut);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+ if ((bf_template->offset + bf_template->length)
+ > route_idmask_width)
+ route_idmask_width = (bf_template->offset
+ + bf_template->length);
+ }
+ filter->route_id_base = reg;
+ list_add_tail(&reg->parent_link, &filter->register_link);
+
+ /* RouteIdMask*/
+ bf = win2030_noc_create_register_and_bitfield(probe, "RouteIdMask",
+ probe->hw_base,
+ PROBE_FILTERS_ROUTEIDMASK(id), 0,
+ route_idmask_width, NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->route_id_mask = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* AddressBase_Low*/
+ bf = win2030_noc_create_register_and_bitfield(probe, "AddressBase_Low",
+ probe->hw_base,
+ PROBE_FILTERS_ADDRBASE_LOW(id),
+ PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_OFFSET,
+ PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->addr_base_low = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* AddressBase_High*/
+ bf = win2030_noc_create_register_and_bitfield(probe, "AddressBase_High",
+ probe->hw_base,
+ PROBE_FILTERS_ADDRBASE_HIGH(id),
+ PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_OFFSET,
+ PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->addr_base_high = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* Window Size */
+ bf = win2030_noc_create_register_and_bitfield(probe, "WindowSize",
+ probe->hw_base,
+ PROBE_FILTERS_WINDOWSIZE(id),
+ PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_OFFSET,
+ PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->window_size = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+#if 0
+ /*no security deployment*/
+
+ unsigned security_mask_width;
+
+ /* Security Base */
+ reg_template = win2030_noc_get_compatible_register(noc_device,
+ "eswin,win2030,noc,filter,security");
+ if (!reg_template)
+ return -ENODEV;
+
+ scnprintf(_name, ARRAY_SIZE(_name), "probe%d_filter%d_security_base",
+ probe->id, id);
+
+ reg = win2030_new_register(noc_device->dev, _name, noc_device->hw_base,
+ PROBE_FILTERS_SECURITYBASE(0, probe->id, id), 32);
+
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ filter->security_base = reg;
+ security_mask_width = 0;
+ list_for_each_entry(bf_template, &reg_template->bitfields, link) {
+ bf = win2030_new_bitfield(reg, bf_template->name,
+ bf_template->offset,
+ bf_template->length, bf_template->lut);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+ if ((bf_template->offset + bf_template->length)
+ > security_mask_width)
+ security_mask_width = (bf_template->offset
+ + bf_template->length);
+ }
+
+ /* Security Mask */
+ scnprintf(_name, ARRAY_SIZE(_name), "probe%d_filter%d_security_mask",
+ probe->id, id);
+
+ bf = win2030_noc_create_register_and_bitfield(probe, _name,
+ PROBE_FILTERS_SECURITYMASK
+ (0,
+ probe->id, id), 0,
+ security_mask_width, NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->security_mask = bf;
+#endif
+ /* Opcode*/
+ reg = win2030_new_register(probe->dev, "Opcode",
+ probe->hw_base,
+ PROBE_FILTERS_OPCODE(id), 32);
+
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "RdEn",
+ PROBE_FILTERS_OPCODE_RDEN_OFFSET,
+ PROBE_FILTERS_OPCODE_RDEN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "WrEn",
+ PROBE_FILTERS_OPCODE_WREN_OFFSET,
+ PROBE_FILTERS_OPCODE_WREN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "LockEn",
+ PROBE_FILTERS_OPCODE_LOCKEN_OFFSET,
+ PROBE_FILTERS_OPCODE_LOCKEN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "UrgEn",
+ PROBE_FILTERS_OPCODE_URGEN_OFFSET,
+ PROBE_FILTERS_OPCODE_URGEN_WIDTH,
+ win2030_noc_enable_enum);
+
+ filter->op_code = reg;
+ list_add_tail(&reg->parent_link, &filter->register_link);
+
+ /* Status */
+ reg = win2030_new_register(probe->dev, "Status", probe->hw_base,
+ PROBE_FILTERS_STATUS(id), 32);
+
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "status_request_enable",
+ PROBE_FILTERS_STATUS_REQEN_OFFSET,
+ PROBE_FILTERS_STATUS_REQEN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "status_response_enable",
+ PROBE_FILTERS_STATUS_RSPEN_OFFSET,
+ PROBE_FILTERS_STATUS_RSPEN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->status = reg;
+ list_add_tail(&reg->parent_link, &filter->register_link);
+
+ /* Length */
+ bf = win2030_noc_create_register_and_bitfield(probe, "Length",
+ probe->hw_base,
+ PROBE_FILTERS_LENGTH(id),
+ PROBE_FILTERS_LENGTH_FILTERS_LENGTH_OFFSET,
+ PROBE_FILTERS_LENGTH_FILTERS_LENGTH_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->length = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* Urgency */
+ bf = win2030_noc_create_register_and_bitfield(probe, "Urgency",
+ probe->hw_base,
+ PROBE_FILTERS_URGENCY(id),
+ PROBE_FILTERS_URGENCY_FILTERS_URGENCY_OFFSET,
+ PROBE_FILTERS_URGENCY_FILTERS_URGENCY_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->urgency = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ return 0;
+}
+
+/**
+ * Create a filter register set (includes RouteIdBase, RouteIdMask, AddrBase_Low,
+ * WindowSize, SecurityBase, SecurityMask, Opcode, Status, Length, Urgency)
+ * @param noc_device
+ * @param filter
+ * @param probe
+ * @param id
+ * @return
+ */
+static int win2030_noc_init_trans_filter(struct win2030_noc_device *noc_device,
+ struct win2030_noc_trans_filter *filter, void *hw_base,
+ struct win2030_noc_probe *probe, unsigned id)
+{
+ struct win2030_bitfield *bf;
+ struct win2030_register *reg;
+
+ if ((noc_device == NULL) || (filter == NULL) || (probe == NULL))
+ return -EINVAL;
+
+ filter->id = id;
+ filter->parent = probe;
+ INIT_LIST_HEAD(&filter->register_link);
+
+ /* Mode */
+ bf = win2030_noc_create_register_and_bitfield(probe, "Mode",
+ hw_base,
+ PROBE_TRANS_FILTERS_MODE,
+ PROBE_TRANS_FILTERS_MODE_FILTERS_MODE_OFFSET,
+ PROBE_TRANS_FILTERS_MODE_FILTERS_MODE_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->mode = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* AddressBase_Low */
+ bf = win2030_noc_create_register_and_bitfield(probe, "AddressBase_Low",
+ hw_base,
+ PROBE_TRANS_FILTERS_ADDRBASE_LOW,
+ PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_OFFSET,
+ PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->addr_base_low = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* AddressBase_High */
+ bf = win2030_noc_create_register_and_bitfield(probe, "AddressBase_High",
+ hw_base,
+ PROBE_TRANS_FILTERS_ADDRBASE_HIGH,
+ PROBE_TRANS_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_OFFSET,
+ PROBE_TRANS_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->addr_base_high = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* WindowSize */
+ bf = win2030_noc_create_register_and_bitfield(probe, "WindowSize",
+ hw_base,
+ PROBE_TRANS_FILTERS_WINDOWSIZE,
+ PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_OFFSET,
+ PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->window_size = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* OpCode */
+ reg = win2030_new_register(probe->dev, "OpCode", hw_base,
+ PROBE_TRANS_FILTERS_OPCODE, 32);
+
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "opcode_rden",
+ PROBE_TRANS_FILTERS_OPCODE_RDEN_OFFSET,
+ PROBE_TRANS_FILTERS_OPCODE_RDEN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "opcode_wren",
+ PROBE_TRANS_FILTERS_OPCODE_WREN_OFFSET,
+ PROBE_TRANS_FILTERS_OPCODE_WREN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->op_code = reg;
+ list_add_tail(&reg->parent_link, &filter->register_link);
+
+ /* UserBase */
+ bf = win2030_noc_create_register_and_bitfield(probe, "UserBase",
+ hw_base,
+ PROBE_TRANS_FILTERS_USER_BASE,
+ PROBE_TRANS_FILTERS_USER_BASE_FILTERS_USER_BASE,
+ PROBE_TRANS_FILTERS_USER_BASE_FILTERS_USER_BASE_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->user_base = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ /* UserMask */
+ bf = win2030_noc_create_register_and_bitfield(probe, "UserMask",
+ hw_base,
+ PROBE_TRANS_FILTERS_USER_MASK,
+ PROBE_TRANS_FILTERS_USER_MASK_FILTERS_USER_MASK,
+ PROBE_TRANS_FILTERS_USER_MASK_FILTERS_USER_MASK_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ filter->user_mask = bf;
+ list_add_tail(&bf->parent->parent_link, &filter->register_link);
+
+ return 0;
+}
+
+/**
+ * Create a profiler register set (includes En, Mode, ObservedSel,
+ * NTenureLines, Thresholds, OverFlowStatus, OverFlowReset, PendingEventMode, PreScaler)
+ * @param noc_device
+ * @param profiler
+ * @param probe
+ * @param id
+ * @return
+ */
+static int win2030_noc_init_trans_profiler(struct win2030_noc_device *noc_device,
+ struct win2030_noc_trans_profiler *profiler, void *hw_base,
+ struct win2030_noc_probe *probe, unsigned id)
+{
+ struct win2030_bitfield *bf;
+ int i, j;
+ char reg_name[32];
+
+ if ((noc_device == NULL) || (profiler == NULL) || (probe == NULL))
+ return -EINVAL;
+
+ profiler->id = id;
+ profiler->parent = probe;
+ INIT_LIST_HEAD(&profiler->register_link);
+
+ /* En */
+ bf = win2030_noc_create_register_and_bitfield(probe, "En",
+ hw_base,
+ PROBE_TRANS_PROFILER_EN,
+ PROBE_TRANS_PROFILER_EN_PROFILER_EN_OFFSET,
+ PROBE_TRANS_PROFILER_EN_PROFILER_EN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->en = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+
+ /* Mode */
+ bf = win2030_noc_create_register_and_bitfield(probe, "Mode",
+ hw_base,
+ PROBE_TRANS_PROFILER_MODE,
+ PROBE_TRANS_PROFILER_MODE_PROFILER_MODE_OFFSET,
+ PROBE_TRANS_PROFILER_MODE_PROFILER_MODE_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->mode = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+
+ /* ObservedSel */
+ for (i = 0; i < probe->nr_portsel; i++) {
+ scnprintf(reg_name, ARRAY_SIZE(reg_name), "ObservedSel_%d", i);
+ bf = win2030_noc_create_register_and_bitfield(probe, reg_name,
+ hw_base,
+ PROBE_TRANS_PROFILER_OBSERVED_SEL + i * 0x4,
+ PROBE_TRANS_PROFILER_OBSERVED_SEL_PROFILER_OBSERVED_SEL_OFFSET,
+ PROBE_TRANS_PROFILER_OBSERVED_SEL_PROFILER_OBSERVED_SEL_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->observed_sel[i] = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+ }
+
+ /* NTenureLines */
+ for (i = 0; i < probe->nr_portsel - 1; i++) {
+ scnprintf(reg_name, ARRAY_SIZE(reg_name), "NTenureLines_%d", i);
+ bf = win2030_noc_create_register_and_bitfield(probe, reg_name,
+ hw_base,
+ PROBE_TRANS_PROFILER_N_TENURE_LINES + i * 0x4,
+ PROBE_TRANS_PROFILER_N_TENURE_LINES_PROFILER_N_TENURE_LINES_OFFSET,
+ PROBE_TRANS_PROFILER_N_TENURE_LINES_PROFILER_N_TENURE_LINES_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->n_tenure_lines[i] = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+ }
+
+ /* Thresholds */
+ for (i = 0; i < probe->nr_portsel; i++) {
+ for (j = 0; j < WIN2030_NOC_BIN_CNT - 1; j++) {
+ scnprintf(reg_name, ARRAY_SIZE(reg_name), "Thresholds_%d_%d", i, j);
+ bf = win2030_noc_create_register_and_bitfield(probe, reg_name,
+ hw_base,
+ PROBE_TRANS_PROFILER_THRESHOLDS + i * 0x10 + j * 0x4,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_OFFSET,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->thresholds[i][j] = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+ }
+ }
+
+ /* OverFlowStatus */
+ bf = win2030_noc_create_register_and_bitfield(probe, "OverFlowStatus",
+ hw_base,
+ PROBE_TRANS_PROFILER_OVER_FLOW_STATUS,
+ PROBE_TRANS_PROFILER_OVER_FLOW_STATUS_PROFILER_OVER_FLOW_STATUS_OFFSET,
+ PROBE_TRANS_PROFILER_OVER_FLOW_STATUS_PROFILER_OVER_FLOW_STATUS_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->over_flow_status = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+
+ /* OverFlowReset */
+ bf = win2030_noc_create_register_and_bitfield(probe, "OverFlowReset",
+ hw_base,
+ PROBE_TRANS_PROFILER_OVER_FLOW_RESET,
+ PROBE_TRANS_PROFILER_OVER_FLOW_RESET_PROFILER_OVER_FLOW_RESET_OFFSET,
+ PROBE_TRANS_PROFILER_OVER_FLOW_RESET_PROFILER_OVER_FLOW_RESET_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->over_flow_reset = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+
+ /* PendingEventMode */
+ bf = win2030_noc_create_register_and_bitfield(probe, "PendingEventMode",
+ hw_base,
+ PROBE_TRANS_PROFILER_PENDING_EVENT_MODE,
+ PROBE_TRANS_PROFILER_PENDING_EVENT_MODE_PROFILER_PENDING_EVENT_MODE_OFFSET,
+ PROBE_TRANS_PROFILER_PENDING_EVENT_MODE_PROFILER_PENDING_EVENT_MODE_OFFSET,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->pending_event_mode = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+
+ /* PreScaler */
+ bf = win2030_noc_create_register_and_bitfield(probe, "PreScaler",
+ hw_base,
+ PROBE_TRANS_PROFILER_PRE_SCALER,
+ PROBE_TRANS_PROFILER_PRE_SCALER_PROFILER_PRE_SCALER_OFFSET,
+ PROBE_TRANS_PROFILER_PRE_SCALER_PROFILER_PRE_SCALER_WIDTH,
+ NULL);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ profiler->pre_scaler = bf;
+ list_add_tail(&bf->parent->parent_link, &profiler->register_link);
+
+ return 0;
+}
+
+static void win2030_noc_remove_probe(struct win2030_noc_probe *probe)
+{
+ unsigned i;
+ struct win2030_noc_counter *counter;
+ struct win2030_register *reg = NULL;
+
+ /*TODO free registers */
+ if (probe_t_pkt == probe->type) {
+ struct win2030_noc_filter *filter = probe->filters;
+ for (i = 0; i < probe->nr_filters; i++) {
+ list_for_each_entry(reg, &filter->register_link, parent_link) {
+ win2030_free_register(reg);
+ }
+ filter++;
+ }
+ } else {
+ struct win2030_noc_trans_filter *filter = probe->trans_filters;
+ for (i = 0; i < probe->nr_filters; i++) {
+ list_for_each_entry(reg, &filter->register_link, parent_link) {
+ win2030_free_register(reg);
+ }
+ filter++;
+ }
+ }
+
+ for (i = 0, counter = probe->counters; i < probe->nr_counters; i++) {
+ list_for_each_entry(reg, &counter->register_link, parent_link) {
+ win2030_free_register(reg);
+ }
+ counter++;
+ }
+ kfree(probe->filters);
+ kfree(probe->counters);
+}
+
+static struct win2030_register *win2030_noc_new_probe_register(char *reg_name,
+ unsigned offset, struct win2030_noc_probe *probe)
+{
+ struct win2030_register *reg;
+
+ reg = win2030_new_register(probe->dev, reg_name, probe->hw_base,
+ offset, 32);
+ if (!IS_ERR_OR_NULL(reg))
+ list_add_tail(&reg->parent_link, &probe->register_link);
+
+ return reg;
+}
+
+/**
+ * Create a probe register set (includes MainCtl, ConfigCtl, StatPeriod, StatAlarmMin, StatAlarmMax
+ * @param noc_device
+ * @param probe
+ * @param cnt_id
+ * @return
+ */
+static int win2030_noc_init_probe(struct win2030_noc_device *noc_device,
+ struct win2030_noc_probe *probe,
+ struct device_node *np)
+{
+ struct device *_dev = probe->dev;
+ struct win2030_register *reg;
+ struct win2030_bitfield *bf;
+
+ probe->parent = noc_device;
+ tasklet_init(&probe->tasklet, win2030_noc_stat_measure_tasklet,
+ (unsigned long)probe);
+
+ probe->nr_portsel = of_property_count_strings(np, "portsel");
+ if (probe->nr_portsel <= 0) {
+ return -EINVAL;
+ }
+ probe->available_portsel = win2030_get_strings_from_dts(_dev, np, "portsel");
+
+ /**
+ * Register: MainCtl
+ */
+ reg = probe->main_ctl = win2030_noc_new_probe_register("MainCtl",
+ PROBE_MAINCTL, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatEn",
+ PROBE_MAINCTL_STATEN_OFFSET,
+ PROBE_MAINCTL_STATEN_WIDTH,
+ win2030_noc_enable_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "AlarmEn",
+ PROBE_MAINCTL_ALARMEN_OFFSET,
+ PROBE_MAINCTL_ALARMEN_WIDTH,
+ win2030_noc_enable_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatCondDump",
+ PROBE_MAINCTL_STATCONDDUMP_OFFSET,
+ PROBE_MAINCTL_STATCONDDUMP_WIDTH,
+ win2030_noc_on_off_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "FiltByteAlwaysChainableEn",
+ PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_OFFSET,
+ PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_WIDTH,
+ win2030_noc_on_off_enum);
+
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ /**
+ * Register: CfgCtl
+ */
+ reg = probe->cfg_ctl = win2030_noc_new_probe_register("CfgCtl",
+ PROBE_CFGCTL, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "Global_Enable",
+ PROBE_CFGCTL_GLOBALEN_OFFSET,
+ PROBE_CFGCTL_GLOBALEN_WIDTH,
+ win2030_noc_enable_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "Active",
+ PROBE_CFGCTL_GLOBALEN_OFFSET,
+ PROBE_CFGCTL_GLOBALEN_WIDTH,
+ win2030_noc_on_off_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+#if 0
+ /**
+ * Register: TracePortSel
+ */
+ scnprintf(mystr, ARRAY_SIZE(mystr), "probe(%s)_TracePortSel", probe->id);
+ reg = win2030_new_register(noc_device->dev, mystr, probe->hw_base,
+ PROBE_TRACEPORTSEL,
+ 32);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ probe->trace_port_sel = reg;
+
+ bf = win2030_new_bitfield(reg, "TracePortSel",
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_OFFSET,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_WIDTH,
+ probe->available_portsel);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+#endif
+ /**
+ * Register: FilterLut
+ */
+ reg = probe->filter_lut = win2030_noc_new_probe_register("FilterLut",
+ PROBE_FILTERLUT, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "FilterLut",
+ PROBE_FILTERLUT_FILTERLUT_OFFSET,
+ PROBE_FILTERLUT_FILTERLUT_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ /**
+ * Register: StatPeriod
+ */
+ reg = probe->stat_period = win2030_noc_new_probe_register("StatPeriod",
+ PROBE_STATPERIOD, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatPeriod",
+ PROBE_STATPERIOD_STATPERIOD_OFFSET,
+ PROBE_STATPERIOD_STATPERIOD_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ /**
+ * Register: StatGo
+ */
+ reg = probe->stat_go = win2030_noc_new_probe_register("StatGo",
+ PROBE_STATGO, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatGo",
+ PROBE_STATGO_STATGO_OFFSET,
+ PROBE_STATGO_STATGO_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ /**
+ * Register: StatAlarmMin
+ */
+ reg = probe->stat_alarm_min = win2030_noc_new_probe_register("StatAlarmMin",
+ PROBE_STATALARMMIN, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatAlarmMin",
+ PROBE_STATALARMMIN_STATALARMMIN_OFFSET,
+ PROBE_STATALARMMIN_STATALARMMIN_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ /**
+ * Register: StatAlarmMinHigh
+ */
+ reg = probe->stat_alarm_min_high = win2030_noc_new_probe_register("StatAlarmMinHigh",
+ PROBE_STATALARMMIN_HIGH, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatAlarmMinHigh",
+ PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_OFFSET,
+ PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ /**
+ * Register: StatAlarmMax
+ */
+ reg = probe->stat_alarm_max = win2030_noc_new_probe_register("StatAlarmMax",
+ PROBE_STATALARMMAX, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatAlarmMax",
+ PROBE_STATALARMMAX_STATALARMMAX_OFFSET,
+ PROBE_STATALARMMAX_STATALARMMAX_WIDTH, NULL);
+
+ /**
+ * Register: StatAlarmStatus
+ */
+ reg = probe->stat_alarm_status = win2030_noc_new_probe_register("StatAlarmStatus",
+ PROBE_STATALARMSTATUS, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatAlarmStatus",
+ PROBE_STATALARMSTATUS_STATALARMSTATUS_OFFSET,
+ PROBE_STATALARMSTATUS_STATALARMSTATUS_WIDTH,
+ NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ /**
+ * Register: StatAlarmEn
+ */
+ reg = probe->stat_alarm_en = win2030_noc_new_probe_register("StatAlarmEn",
+ PROBE_STATALARMEN, probe);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "StatAlarmEn",
+ PROBE_STATALARMEN_STATALARMEN_OFFSET,
+ PROBE_STATALARMEN_STATALARMEN_WIDTH, win2030_noc_enable_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int win2030_noc_parse_dts_qoscfg(struct device *_dev,
+ char *propname,
+ struct regcfg **config)
+{
+ int def_len;
+ struct property *prop;
+ struct device_node *np = _dev->of_node;
+
+ prop = of_find_property(np, propname, &def_len);
+ if (prop != NULL) {
+ unsigned i;
+ struct regcfg *reg;
+
+ *config = (struct regcfg *)
+ devm_kzalloc(_dev,
+ sizeof(struct regcfg), GFP_KERNEL);
+ if (*config == NULL) {
+ dev_err(_dev, "%s: Memory allocation failed!",
+ __func__);
+ BUG();
+ }
+ INIT_LIST_HEAD(&(*config)->list);
+
+ for (i = 0; i < (def_len / sizeof(u32)); i += 2) {
+ reg = (struct regcfg *)
+ devm_kzalloc(_dev,
+ sizeof(struct regcfg), GFP_KERNEL);
+ if (reg == NULL) {
+ dev_err(_dev, "%s: Memory allocation failed!",
+ __func__);
+ BUG();
+ }
+ of_property_read_u32_index(np, propname, i,
+ &reg->offset);
+ of_property_read_u32_index(np, propname, i + 1,
+ &reg->value);
+
+ list_add_tail(&reg->list,
+ &(*config)->list);
+ }
+ } else
+ *config = NULL;
+
+ return *config == NULL ? -EINVAL : 0;
+}
+
+static int win2030_noc_qos_create_register(struct device *_dev,
+ void __iomem *base, struct dev_qos_cfg *qos)
+{
+ struct win2030_register *reg;
+ struct win2030_bitfield *bf;
+
+ /**
+ * Register: Priority
+ */
+ reg = win2030_new_register(_dev, "Priority", base, GPU_QOS_GEN_PRIORITY, 32);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "P0", GPU_QOS_GEN_PRIORITY_P0_OFFSET,
+ GPU_QOS_GEN_PRIORITY_P0_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "P1", GPU_QOS_GEN_PRIORITY_P1_OFFSET,
+ GPU_QOS_GEN_PRIORITY_P1_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ list_add_tail(&reg->parent_link, &qos->register_link);
+
+ /**
+ * Register: Mode
+ */
+ reg = win2030_new_register(_dev, "Mode", base, GPU_QOS_GEN_MODE, 32);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "Mode", GPU_QOS_GEN_MODE_MODE_OFFSET,
+ GPU_QOS_GEN_MODE_MODE_WIDTH, win2030_noc_qos_mode_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+ list_add_tail(&reg->parent_link, &qos->register_link);
+
+ /**
+ * Register: Bandwidth
+ */
+ reg = win2030_new_register(_dev, "Bandwidth", base, GPU_QOS_GEN_BANDWIDTH, 32);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "Bandwidth", GPU_QOS_GEN_BANDWIDTH_BANDWIDTH_OFFSET,
+ GPU_QOS_GEN_BANDWIDTH_BANDWIDTH_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+ list_add_tail(&reg->parent_link, &qos->register_link);
+
+ /**
+ * Register: Saturation
+ */
+ reg = win2030_new_register(_dev, "Saturation", base, GPU_QOS_GEN_SATURATION, 32);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "Saturation", GPU_QOS_GEN_SATURATION_SATURATION_OFFSET,
+ GPU_QOS_GEN_SATURATION_SATURATION_WIDTH, NULL);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+ list_add_tail(&reg->parent_link, &qos->register_link);
+
+ /**
+ * Register: ExtControl
+ */
+ reg = win2030_new_register(_dev, "ExtControl", base, GPU_QOS_GEN_EXTCONTROL, 32);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "SocketEn", GPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN_OFFSET,
+ GPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN_WIDTH, win2030_noc_enable_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "ExtThrEn", GPU_QOS_GEN_EXTCONTROL_EXTTHREN_OFFSET,
+ GPU_QOS_GEN_EXTCONTROL_EXTTHREN_WIDTH, win2030_noc_enable_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "IntClkEn", GPU_QOS_GEN_EXTCONTROL_INTCLKEN_OFFSET,
+ GPU_QOS_GEN_EXTCONTROL_INTCLKEN_WIDTH, win2030_noc_enable_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+
+ bf = win2030_new_bitfield(reg, "ExtLimitEn", GPU_QOS_GEN_EXTCONTROL_EXTLIMITEN_OFFSET,
+ GPU_QOS_GEN_EXTCONTROL_EXTLIMITEN_WIDTH, win2030_noc_enable_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EINVAL;
+ list_add_tail(&reg->parent_link, &qos->register_link);
+
+ return 0;
+}
+
+static int win2030_noc_parse_dts_qoslist(struct device *_dev)
+{
+ int def_len;
+ struct property *prop;
+ struct device_node *np = _dev->of_node;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ u32 qos_base;
+
+ INIT_LIST_HEAD(&noc_device->qos_list);
+ prop = of_find_property(np, "eswin,qos-configs", &def_len);
+ if (prop != NULL) {
+ char str[64] = "\0";
+ struct dev_qos_cfg *devqos;
+ int i, ncfg = of_property_count_strings(np, "eswin,qos-configs");
+
+ for (i = 0; i < ncfg; i++) {
+ int ret = 0;
+ devqos = (struct dev_qos_cfg *)devm_kzalloc(_dev, sizeof(struct dev_qos_cfg), GFP_KERNEL);
+ if (devqos == NULL) {
+ dev_err(_dev, "%s: Memory allocation failed!", __func__);
+ BUG();
+ }
+ INIT_LIST_HEAD(&devqos->register_link);
+ of_property_read_string_index(np, "eswin,qos-configs", i, &devqos->name);
+
+ /* test if controlled by noc driver */
+ sprintf(str, "eswin,%s-qos-owner", devqos->name);
+ if (of_property_read_bool(np, str))
+ devqos->noc_owner = 1;
+ else
+ devqos->noc_owner = 0;
+
+ sprintf(str, "eswin,%s-qos-base", devqos->name);
+ ret = of_property_read_u32(np, str, &qos_base);
+ if (ret) {
+ dev_info(_dev, "%s could not get reg base\n", str);
+ devm_kfree(_dev, devqos);
+ return ret;
+ } else {
+ devqos->hw_base = ioremap(qos_base, 0x100);
+ ret = win2030_noc_qos_create_register(_dev, devqos->hw_base, devqos);
+ if (ret) {
+ dev_info(_dev, "%s could not create qos register\n", str);
+ devm_kfree(_dev, devqos);
+ return ret;
+ }
+ }
+
+ sprintf(str, "eswin,%s-qos-settings", devqos->name);
+ ret = win2030_noc_parse_dts_qoscfg(_dev, str,
+ &devqos->config);
+ if (ret) {
+ dev_info(_dev, "%s not add to list\n", str);
+ devm_kfree(_dev, devqos);
+ return -EINVAL;
+ } else {
+ dev_info(_dev, "%s added to list(%d)\n",
+ str, devqos->noc_owner);
+ list_add_tail(&devqos->list, &noc_device->qos_list);
+ }
+ }
+ } else {
+ dev_info(_dev, "no QoS config list\n");
+ }
+ return 0;
+}
+#define NOC_DEV_NUM 10
+
+static struct win2030_noc_device *noc_dev_glob[NOC_DEV_NUM] = {NULL, NULL};
+static int noc_idev_glob;
+/*
+ notice: the "is_enalbe" flag only affect the NOC internal priority.
+ when is_enalbe == true, the NOC qos will be set according to the dts config.
+ when is_enalbe == false, the NOC qos will be fair, but the IP qos config will still pass to ddr controller.
+ If you want completely fair mode, set bypass mode in noc dts node and set the same qos priority in the IP config.
+*/
+void win2030_noc_qos_set(const char *name, bool is_enalbe)
+{
+ struct dev_qos_cfg *qos = NULL;
+ struct regcfg *reg = NULL;
+ int idev = 0;
+
+ for (idev = 0; idev < NOC_DEV_NUM; idev++) {
+ struct win2030_noc_device *noc_dev = noc_dev_glob[idev];
+
+ if (!noc_dev)
+ continue;
+ list_for_each_entry(qos, &noc_dev->qos_list, list) {
+ if (strcmp(qos->name, name) == 0) {
+ if (qos->config) {
+ dev_info(noc_dev->dev, "%s QoS config %s\n",
+ is_enalbe ? "Enable" : "Disable", qos->name);
+ list_for_each_entry(reg, &qos->config->list, list) {
+ iowrite32(reg->value, qos->hw_base + reg->offset);
+ /*when disable qos, set the mode register to bypass and disable socket*/
+ if (false == is_enalbe) {
+ if (0xC == reg->offset) {
+ iowrite32(0x2, qos->hw_base + reg->offset);
+ }
+ if (0x18 == reg->offset) {
+ iowrite32(0x0, qos->hw_base + reg->offset);
+ }
+ }
+ }
+ }
+ return;
+ }
+ }
+ }
+ pr_err("QoS config %s not found\n", name);
+}
+EXPORT_SYMBOL(win2030_noc_qos_set);
+
+int noc_device_qos_set(struct win2030_noc_device *noc_device, bool is_enalbe)
+{
+ struct dev_qos_cfg *qos = NULL;
+
+ if (!noc_device) {
+ dev_err(noc_device->dev, "%s NULL noc device\n", __func__);
+ return -ENXIO;
+ }
+ list_for_each_entry(qos, &noc_device->qos_list, list) {
+ if (qos->noc_owner) {
+ win2030_noc_qos_set(qos->name, is_enalbe);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Parse dts to get number of probes and number of filters and counters per probe
+ * @param _dev
+ * @return
+ */
+static int win2030_noc_parse_dts(struct device *_dev)
+{
+ struct device_node *np = _dev->of_node;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+
+ noc_device->trap_on_error = of_property_read_bool(np, "errors,trap");
+
+ dev_info(_dev, "error policy: %s\n", noc_device->trap_on_error ? "trap" : "print");
+
+ win2030_noc_parse_dts_qoslist(_dev);
+ noc_dev_glob[noc_idev_glob] = noc_device;
+ noc_idev_glob++;
+ return 0;
+}
+
+static int win2030_noc_register_probe_common(struct device_node *np,
+ struct device *_dev, struct win2030_noc_probe **_probe)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct win2030_noc_counter *counters;
+ int ret, j;
+ struct win2030_noc_probe *probe;
+
+ /* Initialise probes */
+ probe = devm_kcalloc(_dev, 1, sizeof(struct win2030_noc_probe), GFP_KERNEL);
+ probe->hw_base = of_iomap(np, 0);
+ if (IS_ERR_OR_NULL(probe->hw_base)) {
+ return -EINVAL;
+ }
+ INIT_LIST_HEAD(&probe->register_link);
+
+ probe->dev = _dev;
+ probe->id = np->name;
+
+ probe->clock = of_clk_get_by_name(np, "clk");
+ if (IS_ERR_OR_NULL(probe->clock)) {
+ dev_dbg(probe->dev, "Error %px while getting clock of probe %s\n",
+ probe->clock, probe->id);
+ return -ENXIO;
+ }
+
+ if (!IS_ERR_OR_NULL(probe->clock)) {
+ ret = clk_prepare_enable(probe->clock);
+ if (ret) {
+ dev_err(probe->dev, "Error %d while enabling clock of probe %s\n",
+ ret, probe->id);
+ return ret;
+ }
+ }
+ if (!IS_ERR_OR_NULL(probe->clock))
+ dev_dbg(probe->dev, "Clock of probe %s rate %lu\n",
+ probe->id, clk_get_rate(probe->clock));
+
+ probe->stat_irq = of_irq_get_byname(np, "stat");
+ if (probe->stat_irq < 0) {
+ dev_err(_dev, "Error %d while extracting stat alarm irq\n",
+ (int)probe->stat_irq);
+ ret = probe->stat_irq;
+ return ret;
+
+ }
+ ret = devm_request_irq(_dev, probe->stat_irq, win2030_noc_stat_irq,
+ IRQF_SHARED, "noc stat", probe);
+ if (ret) {
+ dev_err(_dev, "Error while installing stat irq %d\n",
+ (int)probe->stat_irq);
+ return ret;
+ }
+ list_add_tail(&probe->link, &noc_device->probes);
+
+ /*initialize register*/
+ ret = win2030_noc_init_probe(noc_device, probe, np);
+ if (ret)
+ return -EINVAL;
+
+ /* initialize counters */
+ ret = of_property_read_u32(np, "counter,nr", &probe->nr_counters);
+ if (ret) {
+ dev_err(_dev, "\"counter,nr\" property missing");
+ return -EINVAL;
+ }
+ counters = devm_kcalloc(_dev, probe->nr_counters,
+ sizeof(struct win2030_noc_counter), GFP_KERNEL);
+ if (!counters)
+ return -ENOMEM;
+
+ probe->counters = counters;
+ for (j = 0; j < probe->nr_counters; j++) {
+ ret = win2030_noc_init_counter(&counters[j], probe, j);
+ if (ret)
+ return ret;
+ }
+ *_probe = probe;
+ return 0;
+}
+
+static int win2030_noc_register_packet_probe(struct device_node *np,
+ struct device *_dev)
+{
+ int ret, j;
+ struct win2030_noc_filter *filters;
+ struct win2030_noc_probe *probe;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+
+ ret = win2030_noc_register_probe_common(np, _dev, &probe);
+ if (ret) {
+ return ret;
+ }
+ probe->type = probe_t_pkt;
+
+ /* Initialise filters */
+ ret = of_property_read_u32(np, "filter,nr", &probe->nr_filters);
+ if (ret) {
+ dev_err(_dev, "\"filter,nr\" property missing");
+ return -EINVAL;
+ }
+ filters = devm_kcalloc(_dev, probe->nr_filters,
+ sizeof(struct win2030_noc_filter), GFP_KERNEL);
+ if (!filters)
+ return -ENOMEM;
+
+ for (j = 0; j < probe->nr_filters; j++) {
+ ret = win2030_noc_init_filter(noc_device, &filters[j],
+ probe, j);
+ if (ret)
+ return ret;
+ }
+ probe->filters = filters;
+ return 0;
+}
+
+static int win2030_noc_register_trans_probe(struct device_node *np,
+ struct device *_dev)
+{
+ int ret, i;
+ struct win2030_noc_trans_filter *filters;
+ struct win2030_noc_trans_profiler *profilers;
+ struct win2030_noc_probe *probe;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct device_node *child = NULL;
+ void __iomem *hw_base;
+
+ ret = win2030_noc_register_probe_common(np, _dev, &probe);
+ if (0 != ret) {
+ return ret;
+ }
+ probe->type = probe_t_trans;
+
+ /* Initialise filters */
+ i = 0;
+ ret = of_property_read_u32(np, "filter,nr", &probe->nr_filters);
+ if (ret) {
+ dev_err(_dev, "\"filter,nr\" property missing");
+ return -EINVAL;
+ }
+ filters = devm_kcalloc(_dev, probe->nr_filters,
+ sizeof(struct win2030_noc_trans_filter), GFP_KERNEL);
+ if (!filters)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, "eswin,win2xxx-noc-trans-filter")) {
+ if (!of_device_is_available(child)) {
+ probe->nr_filters--;
+ continue;
+ }
+ hw_base = of_iomap(child, 0);
+ if (IS_ERR_OR_NULL(hw_base)) {
+ return -EINVAL;
+ }
+ ret = win2030_noc_init_trans_filter(noc_device,
+ &filters[i],
+ hw_base,
+ probe, i);
+ if (ret)
+ return ret;
+ i++;
+ }
+ }
+ probe->trans_filters = filters;
+
+ /* Initialise profilers */
+ i = 0;
+ ret = of_property_read_u32(np, "profiler,nr", &probe->nr_profilers);
+ if (ret) {
+ dev_err(_dev, "\"profiler,nr\" property missing");
+ return -EINVAL;
+ }
+ profilers = devm_kcalloc(_dev, probe->nr_profilers,
+ sizeof(struct win2030_noc_trans_profiler), GFP_KERNEL);
+ if (!profilers)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, "eswin,win2xxx-noc-trans-profiler")) {
+ hw_base = of_iomap(child, 0);
+ if (IS_ERR_OR_NULL(hw_base)) {
+ return -EINVAL;
+ }
+ ret = win2030_noc_init_trans_profiler(noc_device,
+ &profilers[i],
+ hw_base,
+ probe, i);
+ if (ret)
+ return ret;
+ i++;
+ }
+ }
+ probe->trans_profilers = profilers;
+
+ return 0;
+}
+
+/**
+ * Create probes, filters and counters registers
+ * @param _dev
+ * @return
+ */
+static int win2030_noc_init_device(struct device *_dev)
+{
+ int ret;
+ struct device_node *child = NULL;
+
+ for_each_child_of_node(_dev->of_node, child) {
+ if (of_device_is_compatible(child, "eswin,win2xxx-noc-packet-probe")) {
+ ret = win2030_noc_register_packet_probe(child, _dev);
+ if (0 != ret) {
+ dev_err(_dev, "Error while register packet probe\n");
+ return ret;
+ }
+ }
+ }
+
+ for_each_child_of_node(_dev->of_node, child) {
+ if (of_device_is_compatible(child, "eswin,win2xxx-noc-trans-probe")) {
+ ret = win2030_noc_register_trans_probe(child, _dev);
+ if (0 != ret) {
+ dev_err(_dev, "Error while register trans probe\n");
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+static int win2030_noc_probe(struct platform_device *pdev)
+{
+ struct device *mydev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct win2030_noc_device *noc_device;
+ int ret;
+ struct resource *res;
+
+ dev_info(mydev, "Begin initialization\n");
+ noc_device = devm_kcalloc(mydev, 1,
+ sizeof(struct win2030_noc_device), GFP_KERNEL);
+ if (!noc_device)
+ return -ENOMEM;
+
+ noc_device->dev = mydev;
+ dev_set_drvdata(mydev, noc_device);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(mydev, "Error while get mem resource\n");
+ return -ENODEV;
+ }
+ noc_device->hw_base = devm_ioremap_resource(&pdev->dev, res);;
+ if (IS_ERR_OR_NULL(noc_device->hw_base)) {
+ dev_err(mydev, "Error while mapping %s reg base 0x%llx!\n",
+ np->name, res->start);
+ ret = -EINVAL;
+ goto free_noc_device;
+ }
+ INIT_LIST_HEAD(&noc_device->err_queue);
+ INIT_LIST_HEAD(&noc_device->registers);
+ INIT_LIST_HEAD(&noc_device->probes);
+ spin_lock_init(&noc_device->lock);
+
+ ret = win2030_get_err_registers(mydev, &noc_device->registers);
+ if (ret) {
+ dev_err(mydev, "Error while gettings registers\n");
+ goto put_clock;
+ }
+
+ /* Get these parameters from the dts */
+ ret = win2030_noc_parse_dts(mydev);
+ if (ret) {
+ dev_err(mydev, "Parsing Dts failed\n");
+ goto put_clock;
+ }
+
+ ret = win2030_noc_init_device(mydev);
+ if (ret) {
+ dev_err(mydev, "Failed to init internal NoC modelling\n");
+ goto put_clock;
+ }
+
+ ret = win2030_noc_error_init(mydev);
+ if (ret) {
+ dev_err(mydev, "Initialisation of error handling failed\n");
+ goto put_clock;
+ }
+
+ if (IS_ERR_OR_NULL(win2030_noc_ctrl.win2030_noc_root_debug_dir)) {
+ win2030_noc_ctrl.win2030_noc_root_debug_dir = debugfs_create_dir("noc", NULL);
+ if (IS_ERR_OR_NULL(win2030_noc_ctrl.win2030_noc_root_debug_dir)) {
+ ret = -ENOENT;
+ goto free_noc_device;
+ }
+ INIT_LIST_HEAD(&win2030_noc_ctrl.sbm_link);
+ }
+ ret = win2030_noc_init_sideband_mgr(mydev);
+ if (ret) {
+ dev_err(mydev, "Error while gettings registers\n");
+ goto put_clock;
+ }
+
+ ret = win2030_noc_stat_init(mydev);
+ if (ret)
+ dev_err(mydev, "No NOC sniffer or NOC sniffer init failure\n");
+ else {
+ ret = win2030_noc_stat_debugfs_init(mydev);
+ if (ret)
+ dev_err(mydev,
+ "Initialisation of NOC sniffer sysfs failed\n");
+ }
+ ret = win2030_noc_prof_init(mydev);
+ if (ret)
+ dev_err(mydev, "No NOC profiler or NOC profiler init failure\n");
+ else {
+ ret = win2030_noc_prof_debugfs_init(mydev);
+ if (ret)
+ dev_err(mydev,
+ "Initialisation of NOC profiler sysfs failed\n");
+ }
+ ret = win2030_noc_debug_init(mydev);
+ if (0 != ret) {
+ dev_err(mydev, "Error %d while init debug\n", ret);
+ return ret;
+ }
+ dev_info(mydev, "initialization sucess\n");
+
+ return 0;
+put_clock:
+free_noc_device:
+ return ret;
+}
+
+static int win2030_noc_remove(struct platform_device *pdev)
+{
+ struct device *mydev = &pdev->dev;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(mydev);
+ struct win2030_noc_error *noc_err = NULL, *tmp_err = NULL;
+ struct win2030_register *reg = NULL, *tmp_reg = NULL;
+ struct win2030_noc_probe *probe = NULL;
+ struct win2030_noc_stat_measure *stat_measure, *tmp_stat_measure = NULL;
+ struct win2030_noc_stat_probe *stat_probe = NULL, *tmp_stat_probe = NULL;
+ struct win2030_noc_prof_probe *prof_probe = NULL, *tmp_prof_probe = NULL;
+ struct win2030_noc_prof_measure *prof_measure, *tmp_prof_measure = NULL;
+
+ dev_info(mydev, "Removing %s\n", dev_name(mydev));
+
+ list_for_each_entry_safe(stat_probe, tmp_stat_probe,
+ &noc_device->stat->probe, link) {
+ list_for_each_entry_safe(stat_measure, tmp_stat_measure,
+ &stat_probe->measure, link) {
+ list_del(&stat_measure->link);
+ kfree(stat_measure);
+ }
+ list_del(&stat_probe->link);
+ kfree(stat_probe);
+ }
+
+ list_for_each_entry_safe(prof_probe, tmp_prof_probe,
+ &noc_device->prof->prof_probe, link) {
+ list_for_each_entry_safe(prof_measure, tmp_prof_measure,
+ &prof_probe->measure, link) {
+ list_del(&prof_measure->link);
+ kfree(prof_measure);
+ }
+ list_del(&prof_probe->link);
+ kfree(prof_probe);
+ }
+
+ list_for_each_entry_safe(noc_err, tmp_err,
+ &noc_device->err_queue, link) {
+ list_del(&noc_err->link);
+ kfree(noc_err);
+ }
+
+ list_for_each_entry_safe(reg, tmp_reg, &noc_device->registers, link) {
+ list_del(&reg->link);
+ win2030_free_register(reg);
+ }
+
+ list_for_each_entry(probe, &noc_device->probes, link) {
+ win2030_noc_remove_probe(probe);
+ }
+
+ kfree(noc_device->stat);
+ kfree(noc_device->prof);
+ return 0;
+}
+#ifdef CONFIG_PM
+static int noc_resume(struct device *dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(dev);
+ void __iomem *base = noc_device->hw_base;
+
+ noc_device_qos_set(noc_device, true);
+
+ while (ioread32(ERRLOG_0_ERRVLD(base))) {
+ dev_err(dev, "NoC Error pending during resume\n");
+ win2030_noc_get_error(noc_device);
+ }
+
+ iowrite32(1, ERRLOG_0_FAULTEN(base));
+
+ return 0;
+}
+static int noc_suspend(struct device *dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(dev);
+ void __iomem *base = noc_device->hw_base;
+
+ iowrite32(0, ERRLOG_0_FAULTEN(base));
+
+ while (ioread32(ERRLOG_0_ERRVLD(base))) {
+ dev_err(dev, "NoC Error pending during suspend\n");
+ win2030_noc_get_error(noc_device);
+ }
+
+ return 0;
+}
+static const struct dev_pm_ops win2030_noc_driver_pm_ops = {
+
+ .suspend = noc_suspend,
+ .resume = noc_resume,
+
+};
+#endif
+
+static struct platform_driver win2030_noc_driver = {
+ .probe = win2030_noc_probe,
+ .remove = win2030_noc_remove,
+ .driver = {
+ .name = "win2030-noc",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &win2030_noc_driver_pm_ops,
+#endif
+ .of_match_table = of_match_ptr(win2030_noc_of_match),},
+};
+
+static int __init win2030_noc_init(void)
+{
+ return platform_driver_register(&win2030_noc_driver);
+}
+
+static void __exit win2030_noc_exit(void)
+{
+ platform_driver_unregister(&win2030_noc_driver);
+}
+
+subsys_initcall(win2030_noc_init);
+module_exit(win2030_noc_exit);
+
+MODULE_AUTHOR("huangyifeng@eswincomputing.com");
+MODULE_VERSION("1.0.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/interconnect/eswin/noc.h b/drivers/interconnect/eswin/noc.h
new file mode 100644
index 000000000000..37cbf964169a
--- /dev/null
+++ b/drivers/interconnect/eswin/noc.h
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#ifndef NOC_H_
+#define NOC_H_
+
+#define WIN2030_NOC_ERROR_REGISTERS_MAX 8
+#define WIN2030_NOC_REG_ADDR(_reg) ((void __iomem *) (reg->base + reg->offset))
+#define MAX_ITERATION (1 << 28) /* Arbitrary value */
+#define DURATION 20
+#define WIN2030_NOC_TRACE_PORT_MAX 3
+#define WIN2030_NOC_BIN_CNT 4
+#define SIZE_BIG_BUF 4096
+#define SIZE_SMALL_BUF 1500
+
+extern struct win2030_noc_control win2030_noc_ctrl;
+extern int latency_bin[3];
+
+struct win2030_bitfield {
+ struct win2030_register *parent;
+ const char *name;
+ const char *description;
+ unsigned offset;
+ unsigned length;
+ unsigned mask;
+ const char **lut;
+ int aperture_size;
+ u64 *init_flow;
+ u64 *target_flow;
+ u64 *target_sub_range;
+ u16 *aperture_idx;
+ u64 *aperture_base;
+ u32 sbm_id;
+ struct list_head link;
+ spinlock_t lock;
+};
+
+struct win2030_register {
+ struct device *parent;
+ void __iomem *base;
+ const char *name;
+ const char *description;
+ unsigned offset;
+ unsigned length;
+ int aperture_link;
+ int msb_link;
+ struct list_head bitfields;
+ struct list_head link;
+ struct list_head parent_link; //link register's parent(probe, filter or counter)
+ spinlock_t lock;
+ spinlock_t hw_lock;
+};
+
+struct win2030_noc_probe;
+
+struct win2030_noc_stat_traceport_data {
+ const char *name;
+ u16 idx_trace_port_sel;
+ char *init_flow_name;
+ u8 init_flow_idx;
+ unsigned init_flow_offset;
+ unsigned init_flow_mask;
+ char *target_flow_name;
+ u8 target_flow_idx;
+ unsigned target_flow_offset;
+ unsigned target_flow_mask;
+ int addrBase_low;
+ int addrBase_high;
+ int addrWindowSize;
+ int Opcode;
+ int Status;
+ int Length;
+ int Urgency;
+};
+
+/* One measure for the NOC sniffer */
+struct win2030_noc_stat_measure {
+ u64 min[WIN2030_NOC_TRACE_PORT_MAX];
+ u64 max[WIN2030_NOC_TRACE_PORT_MAX];
+ u64 mean[WIN2030_NOC_TRACE_PORT_MAX];
+ u64 now[WIN2030_NOC_TRACE_PORT_MAX];
+ unsigned iteration[WIN2030_NOC_TRACE_PORT_MAX];
+ struct list_head link;
+ spinlock_t lock;
+ struct win2030_noc_stat_traceport_data data;
+};
+
+/* A set of statistics assigned to a probe
+ * (as a probe can measure severals paths)
+ */
+struct win2030_noc_stat_probe {
+ struct win2030_noc_probe *probe;
+ struct list_head measure;
+ struct list_head link;
+ spinlock_t lock;
+};
+
+/* The main structure for the NOC sniffer */
+struct win2030_noc_stat {
+ struct list_head probe;
+ struct dentry *dir;
+ spinlock_t lock;
+ bool run;
+ unsigned clock_rate;
+};
+
+struct win2030_noc_filter {
+ unsigned id;
+ struct win2030_noc_probe *parent;
+ struct win2030_register *route_id_base;
+ struct win2030_bitfield *route_id_mask;
+ struct win2030_bitfield *addr_base_low;
+ struct win2030_bitfield *addr_base_high;
+ struct win2030_bitfield *window_size;
+ //struct win2030_register *security_base;
+ //struct win2030_bitfield *security_mask;
+ struct win2030_register *op_code;
+ struct win2030_register *status;
+ struct win2030_bitfield *length;
+ struct win2030_bitfield *urgency;
+ struct list_head register_link;
+};
+
+struct win2030_noc_trans_filter {
+ unsigned id;
+ struct win2030_noc_probe *parent;
+ struct win2030_bitfield *mode;
+ struct win2030_bitfield *addr_base_low;
+ struct win2030_bitfield *addr_base_high;
+ struct win2030_bitfield *window_size;
+ struct win2030_register *op_code;
+ struct win2030_bitfield *user_base;
+ struct win2030_bitfield *user_mask;
+ struct list_head register_link;
+};
+
+typedef enum {
+ latency_t,
+ pending_t,
+}measure_type;
+
+struct win2030_noc_prof_traceport_data {
+ const char *name;
+ int mode;
+ int addrBase_low;
+ int addrBase_high;
+ int addrWindowSize;
+ int Opcode;
+};
+
+/* One measure for the NOC prof */
+struct win2030_noc_prof_measure {
+ measure_type type;
+ u64 min[WIN2030_NOC_TRACE_PORT_MAX][WIN2030_NOC_BIN_CNT];
+ u64 max[WIN2030_NOC_TRACE_PORT_MAX][WIN2030_NOC_BIN_CNT];
+ u64 mean[WIN2030_NOC_TRACE_PORT_MAX][WIN2030_NOC_BIN_CNT];
+ u64 now[WIN2030_NOC_TRACE_PORT_MAX][WIN2030_NOC_BIN_CNT];
+ unsigned iteration[WIN2030_NOC_TRACE_PORT_MAX];
+ struct list_head link;
+ spinlock_t lock;
+ struct win2030_noc_prof_traceport_data data;
+};
+
+/* A set of statistics assigned to a probe
+ * (as a probe can measure severals paths)
+ */
+struct win2030_noc_prof_probe {
+ struct win2030_noc_probe *probe;
+ struct list_head measure;
+ struct list_head link;
+ spinlock_t lock;
+};
+
+/* The main structure for the NOC profiler */
+struct win2030_noc_prof {
+ struct list_head prof_probe;
+ struct dentry *dir;
+ spinlock_t lock;
+ bool run;
+};
+
+struct win2030_noc_trans_profiler {
+ unsigned id;
+ struct win2030_noc_probe *parent;
+ struct win2030_bitfield *en;
+ struct win2030_bitfield *mode;
+ struct win2030_bitfield *observed_sel[WIN2030_NOC_TRACE_PORT_MAX];
+ struct win2030_bitfield *n_tenure_lines[WIN2030_NOC_TRACE_PORT_MAX];
+ struct win2030_bitfield *thresholds[WIN2030_NOC_TRACE_PORT_MAX][WIN2030_NOC_BIN_CNT];
+ struct win2030_bitfield *over_flow_status;
+ struct win2030_bitfield *over_flow_reset;
+ struct win2030_bitfield *pending_event_mode;
+ struct win2030_bitfield *pre_scaler;
+ struct list_head register_link;
+};
+
+struct win2030_noc_counter {
+ unsigned id;
+ struct win2030_noc_probe *parent;
+ struct win2030_bitfield *portsel;
+ struct win2030_bitfield *alarm_mode;
+ struct win2030_bitfield *source_event;
+ struct win2030_bitfield *value;
+ struct list_head register_link;
+};
+
+enum probe_t {
+ probe_t_pkt = 0x8,
+ probe_t_trans,
+};
+
+struct win2030_noc_probe {
+ struct device *dev;
+ const char *id;
+ struct clk *clock;
+ enum probe_t type;
+ int nr_portsel; /* How many trace port per probe */
+ const char **available_portsel;
+ struct win2030_register *main_ctl;
+ struct win2030_register *cfg_ctl;
+ //struct win2030_register *trace_port_sel;
+ struct win2030_register *filter_lut;
+ struct win2030_register *stat_period;
+ struct win2030_register *stat_go;
+ struct win2030_register *stat_alarm_max;
+ struct win2030_register *stat_alarm_min;
+ struct win2030_register *stat_alarm_min_high;
+ struct win2030_register *stat_alarm_status;
+ struct win2030_register *stat_alarm_en;
+ struct list_head register_link;
+ struct win2030_noc_device *parent;
+ unsigned nr_counters; /* How many counters per probe */
+ struct win2030_noc_counter *counters;
+ unsigned nr_filters; /* How many filters per probe */
+ struct win2030_noc_filter *filters;
+ struct win2030_noc_trans_filter *trans_filters;
+ unsigned nr_profilers; /* How many profiler per probe */
+ struct win2030_noc_trans_profiler *trans_profilers;
+ struct tasklet_struct tasklet;
+ struct list_head link;
+ int stat_irq;
+ void __iomem *hw_base;
+};
+
+struct dev_qos_cfg {
+ struct list_head list;
+ const char *name;
+ void __iomem *hw_base;
+ struct regcfg *config;
+ int noc_owner;
+ bool run;
+ struct list_head register_link;
+};
+
+struct regcfg {
+ struct list_head list;
+ unsigned offset;
+ unsigned value;
+};
+
+struct win2030_noc_device {
+ struct device *dev;
+ void __iomem *hw_base;
+ int err_irq;
+ struct list_head err_queue;
+ spinlock_t lock;
+ struct list_head registers;
+ int error_logger_cnt;
+ u32 *err_log_lut;
+ struct win2030_register *error_registers[WIN2030_NOC_ERROR_REGISTERS_MAX];
+ struct dentry *dir;
+ struct win2030_noc_stat *stat;
+ struct win2030_noc_prof *prof;
+ struct list_head qos_list;
+ int qos_run;
+ bool trap_on_error;
+ struct list_head probes;
+ struct win2030_noc_sideband_magr *sbm;
+};
+
+struct win2030_noc_error {
+ struct list_head link;
+ u64 timestamp;
+ unsigned err[WIN2030_NOC_ERROR_REGISTERS_MAX];
+};
+
+struct win2030_noc_sideband_magr {
+ struct device *dev;
+ void __iomem *hw_base;
+ struct win2030_register *SenseIn0;
+ struct list_head link;
+};
+
+struct win2030_noc_control {
+ struct dentry *win2030_noc_root_debug_dir;
+ struct list_head sbm_link;
+};
+
+int win2030_noc_debug_init(struct device *);
+int win2030_noc_stat_init(struct device *);
+int win2030_noc_prof_init(struct device *_dev);
+
+void win2030_noc_stat_do_measure(struct win2030_noc_probe *);
+void win2030_noc_prof_do_measure(struct win2030_noc_probe *probe);
+int win2030_noc_stat_debugfs_init(struct device *);
+int win2030_noc_prof_debugfs_init(struct device *_dev);
+int win2030_noc_stat_trigger(struct device *_dev);
+void win2030_noc_stat_reset_statistics(struct device *_dev);
+int win2030_noc_prof_trigger(struct device *_dev);
+void win2030_noc_prof_reset_statistics(struct device *_dev);
+
+
+unsigned win2030_noc_reg_read(struct win2030_register *reg, unsigned mask,
+ unsigned offset);
+int win2030_noc_reg_write(struct win2030_register *reg, unsigned mask,
+ unsigned offset, unsigned value);
+int win2030_bitfield_read(struct win2030_bitfield *bf, unsigned *value);
+int win2030_noc_init_sideband_mgr(struct device *_dev);
+int win2030_noc_sideband_mgr_debug_init(struct device *_dev);
+int win2030_noc_debug_reg_init(struct win2030_register *reg, struct dentry *dir);
+
+struct win2030_register *win2030_new_register(struct device *parent,
+ const char *name,
+ void __iomem *base,
+ unsigned offset,
+ unsigned width);
+
+const char **win2030_get_strings_from_dts(struct device *_dev,
+ struct device_node *np,
+ const char *property_name);
+
+struct win2030_bitfield *win2030_new_bitfield(struct win2030_register *reg,
+ const char *name,
+ unsigned offset,
+ unsigned width,
+ const char **lut);
+
+int noc_error_dump(char *buf,
+ struct win2030_noc_device *noc_device,
+ struct win2030_noc_error *noc_err);
+
+int noc_device_qos_set(struct win2030_noc_device *noc_device, bool is_enalbe);
+
+#endif /* NOC_H_ */
diff --git a/drivers/interconnect/eswin/noc_profiler.c b/drivers/interconnect/eswin/noc_profiler.c
new file mode 100644
index 000000000000..c6621ba4155c
--- /dev/null
+++ b/drivers/interconnect/eswin/noc_profiler.c
@@ -0,0 +1,695 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/bitfield.h>
+
+#include "noc.h"
+#include "noc_regs.h"
+
+void zebu_stop(void);
+
+int latency_bin[3] = {256, 320, 384};
+
+/**
+ * Configure NOC registers from a prof_measure
+ * @param noc_device A reference to the device
+ * @param prof_probe The prof_probe to find the measure
+ * @return 0 if ok, -EINVAL otherwise
+ */
+static int win2030_noc_prof_launch_measure(struct win2030_noc_device *noc_device,
+ struct win2030_noc_prof_probe *prof_probe)
+{
+ struct win2030_noc_prof_measure *prof_measure;
+ struct win2030_noc_probe *probe;
+ int i, ret = 0;
+ struct win2030_noc_trans_profiler *profiler;
+ struct win2030_noc_prof_traceport_data *data;
+
+ prof_measure = list_first_entry(&prof_probe->measure,
+ struct win2030_noc_prof_measure, link);
+
+ data = &prof_measure->data;
+ dev_dbg_once(noc_device->dev, "measurment %s begin, type %s\n", data->name,
+ pending_t == prof_measure->type ? "pending" : "latency");
+
+ //zebu_stop();
+ probe = prof_probe->probe;
+
+ profiler = &probe->trans_profilers[0];
+
+ /*
+ Disable probe
+ This will reset all fileds of register MailCtl
+ */
+ ret = win2030_noc_reg_write(probe->cfg_ctl, PROBE_CFGCTL_GLOBALEN_MASK,
+ PROBE_CFGCTL_GLOBALEN_OFFSET, 0);
+ while (win2030_noc_reg_read(probe->cfg_ctl, PROBE_CFGCTL_ACTIVE_WIDTH,
+ PROBE_CFGCTL_ACTIVE_OFFSET) != 0) {
+ ;
+ }
+ /* --> to program the transaction filter,
+ */
+ for (i = 0; i < probe->nr_filters; i++) {
+ ret |= win2030_noc_reg_write(probe->trans_filters[i].mode->parent,
+ PROBE_TRANS_FILTERS_MODE_FILTERS_MODE_MASK,
+ PROBE_TRANS_FILTERS_MODE_FILTERS_MODE_OFFSET,
+ data->mode);
+
+ ret |= win2030_noc_reg_write(probe->trans_filters[i].addr_base_low->parent,
+ PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_MASK,
+ PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_OFFSET,
+ data->addrBase_low);
+
+ ret |= win2030_noc_reg_write(probe->trans_filters[i].addr_base_high->parent,
+ PROBE_TRANS_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_MASK,
+ PROBE_TRANS_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_OFFSET,
+ data->addrBase_high);
+
+ ret |=
+ win2030_noc_reg_write(probe->trans_filters[i].window_size->parent,
+ PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_MASK,
+ PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_OFFSET,
+ data->addrWindowSize);
+
+ ret |= win2030_noc_reg_write(probe->trans_filters[i].op_code,
+ PROBE_TRANS_FILTERS_OPCODE_RDEN_MASK,
+ PROBE_TRANS_FILTERS_OPCODE_RDEN_OFFSET,
+ FIELD_GET(GENMASK(0, 0), data->Opcode));
+
+ ret |= win2030_noc_reg_write(probe->trans_filters[i].op_code,
+ PROBE_TRANS_FILTERS_OPCODE_WREN_MASK,
+ PROBE_TRANS_FILTERS_OPCODE_WREN_OFFSET,
+ FIELD_GET(GENMASK(1, 1), data->Opcode));
+
+ }
+
+ /*--> to program the transaction profiler unit*/
+ /* Disable profiler */
+ ret |= win2030_noc_reg_write(profiler->en->parent,
+ PROBE_TRANS_PROFILER_EN_PROFILER_EN_MASK,
+ PROBE_TRANS_PROFILER_EN_PROFILER_EN_OFFSET,
+ 0);
+
+ /* Select TracePort*/
+ for (i = 0; i < probe->nr_portsel; i++) {
+ ret |= win2030_noc_reg_write(profiler->observed_sel[i]->parent,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_MASK,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_OFFSET,
+ i);
+ }
+ if (pending_t == prof_measure->type) {
+ /*Set Profiler Mode to Pending*/
+ ret |= win2030_noc_reg_write(profiler->mode->parent,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_MASK,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_OFFSET,
+ 1);
+ } else {
+ /*Set Profiler Mode to Delay*/
+ ret |= win2030_noc_reg_write(profiler->mode->parent,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_MASK,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_OFFSET,
+ 0);
+ }
+
+ /* Each line has 16 counters*/
+ for (i = 0; i < probe->nr_portsel - 1; i++) {
+ ret |= win2030_noc_reg_write(profiler->n_tenure_lines[i]->parent,
+ PROBE_TRANS_PROFILER_N_TENURE_LINES_PROFILER_N_TENURE_LINES_MASK,
+ PROBE_TRANS_PROFILER_N_TENURE_LINES_PROFILER_N_TENURE_LINES_OFFSET,
+ 2);
+ }
+ if (pending_t == prof_measure->type) {
+ /* Thresholds*/
+ for (i = 0; i < probe->nr_portsel; i++) {
+ /*
+ for read/wrtie transration, the pending binis 2/5/10/10 up cycles
+ */
+ ret |= win2030_noc_reg_write(profiler->thresholds[i][0]->parent,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_MASK,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_OFFSET,
+ 2);
+ ret |= win2030_noc_reg_write(profiler->thresholds[i][1]->parent,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_MASK,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_OFFSET,
+ 5);
+ ret |= win2030_noc_reg_write(profiler->thresholds[i][2]->parent,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_MASK,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_OFFSET,
+ 10);
+ }
+ } else {
+ /* Thresholds*/
+ for (i = 0; i < probe->nr_portsel; i++) {
+ /*
+ for read/wrtie transration, the delay bin is 30/100/200/200 up cycles
+ */
+ ret |= win2030_noc_reg_write(profiler->thresholds[i][0]->parent,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_MASK,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_OFFSET,
+ latency_bin[0]);
+ ret |= win2030_noc_reg_write(profiler->thresholds[i][1]->parent,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_MASK,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_OFFSET,
+ latency_bin[1]);
+ ret |= win2030_noc_reg_write(profiler->thresholds[i][2]->parent,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_MASK,
+ PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_OFFSET,
+ latency_bin[2]);
+ }
+ }
+
+ /*--> to program the packet probe statistics subsystemt*/
+ for (i = 0; i < probe->nr_counters; i++) {
+ /*maping counters to count profiler outputs*/
+ ret |= win2030_noc_reg_write(probe->counters[i].source_event->parent,
+ PROBE_COUNTERS_SRC_INTEVENT_MASK,
+ PROBE_COUNTERS_SRC_INTEVENT_OFFSET,
+ 0x20 + i);
+
+ /*set counters alarm mode to Min,
+ If the value of the counter is less than the StatAlarmMin register at the
+ dump period, the StatAlarmStatus bit is set
+ */
+ ret |= win2030_noc_reg_write(probe->counters[i].alarm_mode->parent,
+ PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_MASK,
+ PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_OFFSET,
+ 1);
+ }
+
+ /* Set alarm min value */
+ ret |= win2030_noc_reg_write(probe->stat_alarm_min,
+ PROBE_STATALARMMIN_STATALARMMIN_MASK,
+ PROBE_STATALARMMIN_STATALARMMIN_OFFSET,
+ 0xfffffff);
+
+ ret |= win2030_noc_reg_write(probe->stat_alarm_min_high,
+ PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_MASK,
+ PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_OFFSET,
+ 0xfffffff);
+
+ /* Set period to max value */
+ ret |= win2030_noc_reg_write(probe->stat_period,
+ PROBE_STATPERIOD_STATPERIOD_MASK,
+ PROBE_STATPERIOD_STATPERIOD_OFFSET,
+ DURATION);
+
+ /* Enable Alarm */
+ ret |= win2030_noc_reg_write(probe->main_ctl,
+ PROBE_MAINCTL_ALARMEN_MASK,
+ PROBE_MAINCTL_ALARMEN_OFFSET,
+ 1);
+
+ /* Set field StatEn of register MainCtl to 1 to enable the statistics counters to count delay events.*/
+ ret |= win2030_noc_reg_write(probe->main_ctl,
+ PROBE_MAINCTL_STATEN_MASK,
+ PROBE_MAINCTL_STATEN_OFFSET,
+ 1);
+
+ /*--> to enable the transaction probe*/
+ /* Set field GlobalEn of register CfgCtl to 1 to enable the statistics counters. */
+ ret |= win2030_noc_reg_write(probe->cfg_ctl,
+ PROBE_CFGCTL_GLOBALEN_MASK,
+ PROBE_CFGCTL_GLOBALEN_OFFSET,
+ 1);
+
+ /* Set register En to 1 to enable the transaction profiling counters.. */
+ ret |= win2030_noc_reg_write(profiler->en->parent,
+ PROBE_TRANS_PROFILER_EN_PROFILER_EN_MASK,
+ PROBE_TRANS_PROFILER_EN_PROFILER_EN_OFFSET,
+ 1);
+ return ret;
+}
+
+void win2030_noc_prof_reset_statistics(struct device *_dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct win2030_noc_prof_probe *prof_probe = NULL;
+ struct win2030_noc_probe *noc_probe = NULL;
+ struct win2030_noc_prof_measure *prof_measure = NULL;
+ struct win2030_noc_prof *noc_prof = noc_device->prof;
+ int i, j;
+ unsigned long flags;
+
+ list_for_each_entry(prof_probe, &noc_prof->prof_probe, link) {
+ noc_probe = prof_probe->probe;
+ list_for_each_entry(prof_measure, &prof_probe->measure, link) {
+ for (j = 0; j < noc_probe->nr_portsel; j++) {
+ spin_lock_irqsave(&prof_measure->lock, flags);
+ prof_measure->iteration[j] = 0;
+ for (i = 0; i < WIN2030_NOC_BIN_CNT; i++) {
+ prof_measure->min[j][i] = -1;
+ prof_measure->max[j][i] = 0;
+ prof_measure->now[j][i] = 0;
+ prof_measure->mean[j][i] = 0;
+ }
+ spin_unlock_irqrestore(&prof_measure->lock, flags);
+ }
+ }
+ }
+}
+
+/**
+ * trigger all noc prof measurment for all probes
+ * @param _dev A reference to the device
+ * @return 0
+ */
+int win2030_noc_prof_trigger(struct device *_dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct win2030_noc_prof_probe *prof_probe = NULL;
+ struct win2030_noc_prof *noc_prof = noc_device->prof;
+ unsigned ret;
+
+ //zebu_stop();
+ /* Loop over all probes */
+ list_for_each_entry(prof_probe, &noc_prof->prof_probe, link) {
+ /* lunch the first measurement */
+ ret = win2030_noc_prof_launch_measure(noc_device, prof_probe);
+ if (ret) {
+ dev_err(noc_device->dev,
+ "Error when trigger prof measures, probe %s!\n",
+ prof_probe->probe->id);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Create a new prof_measure from parsed dts entry
+ * @param idx_trace_port_sel First entry in the dts line latency,x as index
+ * @return The created win2030_noc_prof_measure object
+ */
+static struct win2030_noc_prof_measure *win2030_noc_prof_crate_measure(
+ struct win2030_noc_prof_probe *prof_probe)
+{
+ struct win2030_noc_prof_measure *prof_measure;
+ unsigned long flags;
+
+ prof_measure = kzalloc(sizeof(struct win2030_noc_prof_measure),
+ GFP_KERNEL);
+ if (!prof_measure)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&prof_measure->link);
+ spin_lock_init(&prof_measure->lock);
+
+ spin_lock_irqsave(&prof_probe->lock, flags);
+ list_add_tail(&prof_measure->link, &prof_probe->measure);
+ spin_unlock_irqrestore(&prof_probe->lock, flags);
+
+ return prof_measure;
+}
+
+/**
+ * Create a new prof_probe. A prof_probe contains all measurements which
+ * are linked to the same probe
+ * @param noc_prof the prof_probe will be attached to noc_prof
+ * @param probe the prof_probe will refer to probe
+ * @return The created xgold_noc_stat_probe object
+ */
+static struct win2030_noc_prof_probe *win2030_noc_prof_new_probe(struct win2030_noc_prof
+ *noc_prof,
+ struct win2030_noc_probe
+ *probe)
+{
+ struct win2030_noc_prof_probe *prof_probe;
+ unsigned long flags;
+
+ prof_probe = kzalloc(sizeof(struct win2030_noc_prof_probe), GFP_KERNEL);
+ if (!prof_probe)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&prof_probe->measure);
+ INIT_LIST_HEAD(&prof_probe->link);
+ spin_lock_init(&prof_probe->lock);
+
+ spin_lock_irqsave(&noc_prof->lock, flags);
+ list_add_tail(&prof_probe->link, &noc_prof->prof_probe);
+ spin_unlock_irqrestore(&noc_prof->lock, flags);
+ prof_probe->probe = probe;
+
+ return prof_probe;
+}
+
+enum {
+ FIELD_ID_PORT = 0,
+ FIELD_ID_MODE,
+ FIELD_ID_ADDR,
+ FIELD_ID_SIZE,
+ FIELD_ID_OPCODE,
+ FIELD_ID_MAX,
+};
+
+static int win2030_noc_prof_parse_traceport_field(struct device *dev,
+ const char *str, int field_id,
+ struct win2030_noc_prof_traceport_data *data)
+{
+ u64 addr;
+ u64 size;
+ int ret;
+
+ switch (field_id) {
+ case FIELD_ID_MODE:
+ if (0 == strcmp(str, "Mode:latency")) {
+ data->mode = 1;
+ } else if (0 == strcmp(str, "Mode:handshake")) {
+ data->mode = 0;
+ } else {
+ dev_err(dev, "get unknown mode data %s when parsing traceport dts!\n", str);
+ return -EINVAL;
+ }
+ break;
+ case FIELD_ID_ADDR:
+ ret = kstrtou64(&str[strlen("AddrBase:")], 0, &addr);
+ if (ret) {
+ dev_err(dev, "get unknown addr data %s when parsing traceport dts!\n", str);
+ return ret;
+ }
+ data->addrBase_low = FIELD_GET(GENMASK(31, 0), addr);
+ data->addrBase_high = FIELD_GET(GENMASK(40, 32), addr);
+ break;
+ case FIELD_ID_SIZE:
+ ret = kstrtou64(&str[strlen("AddrSize:")], 0, &size);
+ if (ret) {
+ dev_err(dev, "get unknown mask data %s when parsing traceport dts!\n", str);
+ return ret;
+ }
+ data->addrWindowSize = size;
+ data->addrWindowSize &= PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_MASK;
+ break;
+ case FIELD_ID_OPCODE:
+ if (0 == strcmp(str, "Opcode:RdWr")) {
+ data->Opcode = 3;
+ } else if (0 == strcmp(str, "Opcode:Rd")) {
+ data->Opcode = 1;
+ } else if (0 == strcmp(str, "Opcode:Wr")) {
+ data->Opcode = 2;
+ } else {
+ dev_err(dev, "get unknown Opcode data %s when parsing traceport dts!\n", str);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static int win2030_noc_prof_parse_traceport_dts(struct win2030_noc_device *noc_device,
+ struct device_node *np, const char *name, struct win2030_noc_prof_traceport_data *data)
+{
+ struct property *prop;
+ struct device *dev = noc_device->dev;
+ int lg;
+ int index = 0;
+ const char *str[FIELD_ID_MAX] = {NULL}, *s;
+ int ret;
+ const char *field[] = {"TracePort:", "Mode:", "AddrBase:", "AddrSize:", "Opcode:"};
+ int i, j;
+
+ /* Count number of strings per measurement
+ (normally 1: TracePortSel)
+ */
+ lg = of_property_count_strings(np, name);
+ if (lg > FIELD_ID_MAX || lg < 1)
+ return -EINVAL; /* Invalid data, let's skip this measurement */
+
+ of_property_for_each_string(np, name, prop, s)
+ str[index++] = s;
+
+ data->name = &str[0][strlen("TracePort:")];
+ /*if user not set, default set as below
+ Mode: 1, which means latency
+ addrBase: 0, which means all address
+ addrWindowSize : 0, which means all range
+ Opcode: 3, which means enable both read and write
+ */
+ data->mode = 1;
+ data->addrBase_low = 0;
+ data->addrBase_high = 0;
+ data->addrWindowSize = 0x3f;
+ data->Opcode = 3;
+
+ for (i = 1; i < lg; i++) {
+ for (j = 0; j < ARRAY_SIZE(field); j++) {
+ if (0 == strncmp(str[i], field[j], strlen(field[j]))) {
+ ret = win2030_noc_prof_parse_traceport_field(dev, str[i], j, data);
+ if (ret) {
+ dev_err(dev, "error when parsing traceport dts, ret %d!\n", ret);
+ return ret;
+ }
+ }
+ }
+ }
+ dev_dbg(dev, "get traceport data, name: %s\n"
+ "\t\t\t\t Mode: %d, addrBase_low: 0x%x, addrBase_high: 0x%x\n"
+ "\t\t\t\t addrWindowSize: 0x%x, Opcode: 0x%x\n",data->name, data->mode, data->addrBase_low,
+ data->addrBase_high, data->addrWindowSize, data->Opcode);
+ return 0;
+}
+
+/**
+ * From one line of dts, add a new prof_measure and a prof_probe (if needed)
+ * @param noc_device A ref to the device
+ * @param np A ref to the dts node
+ * @param name the line of the dts
+ * @param type profiler measurment type
+ * @return
+ */
+static int win2030_noc_prof_new_measure(struct win2030_noc_device *noc_device,
+ struct device_node *np, const char *name, measure_type type)
+{
+ int ret;
+ struct win2030_noc_prof_probe *prof_probe = NULL;
+ struct win2030_noc_prof *noc_prof = noc_device->prof;
+ struct win2030_noc_probe *noc_probe = NULL;
+ struct win2030_noc_prof_measure *prof_measure;
+ struct win2030_noc_prof_traceport_data data;
+ bool found;
+
+ ret = win2030_noc_prof_parse_traceport_dts(noc_device, np, name, &data);
+ if (ret) {
+ dev_err(noc_device->dev, "error when parsing traceport dts, ret %d!\n", ret);
+ return ret;
+ }
+
+ /* Search which probe can be used first value in dts */
+ found = false;
+ list_for_each_entry(noc_probe, &noc_device->probes, link) {
+ if (strcmp(data.name, noc_probe->id) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (false == found)
+ return -EINVAL; /* Invalid data, let's skip this measurement. */
+
+ /* Look if the prof probe was already existing */
+ found = false;
+ list_for_each_entry(prof_probe, &noc_prof->prof_probe, link) {
+ if (prof_probe->probe == noc_probe) {
+ found = true;
+ break;
+ }
+ }
+ if (false == found) {
+ /* Not found, we must create a prof_probe */
+ prof_probe = win2030_noc_prof_new_probe(noc_prof, noc_probe);
+ if (IS_ERR(prof_probe))
+ goto error;
+
+ }
+ /* create a new measure */
+ prof_measure = win2030_noc_prof_crate_measure(prof_probe);
+ if (IS_ERR(prof_measure))
+ goto error;
+
+ prof_measure->type = type;
+ memcpy(&prof_measure->data, &data, sizeof(struct win2030_noc_prof_traceport_data));
+ return 0;
+error:
+ return -ENOMEM;
+}
+
+/**
+ * Entry point for the NOC profiler
+ * @param _dev A reference to the device
+ * @return 0 if ok, if error returns 0 but print an error message
+ */
+int win2030_noc_prof_init(struct device *_dev)
+{
+ struct win2030_noc_prof *noc_prof;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct device_node *np = _dev->of_node;
+ int latency_idx = 0;
+ int pending_idx = 0;
+ bool ret;
+ char mystr[32];
+ int err;
+
+ /* Create a prof device. */
+ noc_prof = kzalloc(sizeof(struct win2030_noc_prof), GFP_KERNEL);
+ if (!noc_prof)
+ return -ENOMEM;
+ noc_device->prof = noc_prof;
+ noc_prof->run = false;
+ spin_lock_init(&noc_prof->lock);
+ INIT_LIST_HEAD(&noc_prof->prof_probe);
+
+ /* Parse dts and build matrix node */
+ do {
+ scnprintf(mystr, ARRAY_SIZE(mystr), "latency,%d", latency_idx);
+ ret = of_property_read_bool(np, mystr);
+ if (ret == true) {
+ latency_idx++;
+ err = win2030_noc_prof_new_measure(noc_device, np, mystr, latency_t);
+ if (err)
+ dev_err(_dev, "Error when adding latency measurment: %s!\n", mystr);
+ } else {
+ scnprintf(mystr, ARRAY_SIZE(mystr), "pending,%d", pending_idx);
+ ret = of_property_read_bool(np, mystr);
+ if (ret == true) {
+ pending_idx++;
+ err = win2030_noc_prof_new_measure(noc_device, np, mystr, pending_t);
+ if (err)
+ dev_err(_dev, "Error when adding pending measurment: %s!\n", mystr);
+ }
+ }
+ } while (ret == true);
+
+ /* If no prof line in dts then we can release the noc_prof object */
+ if (latency_idx == 0 && pending_idx == 0)
+ goto no_profiler;
+
+ /* Trigger measurement */
+ if (noc_prof->run) {
+ ret = win2030_noc_prof_trigger(_dev);
+ if (ret)
+ dev_err(_dev,
+ "Error when configuring prof measures!\n");
+ }
+ return 0;
+
+no_profiler:
+ kfree(noc_prof);
+ return -EINVAL;
+}
+
+/**
+ * Interrupt processing when a measurement is done
+ * @param noc_device A reference to the device
+ * @param probe The probe for which a measurement is done
+ */
+void win2030_noc_prof_do_measure(struct win2030_noc_probe *probe)
+{
+ struct win2030_noc_device *noc_device = probe->parent;
+ struct win2030_noc_prof_probe *prof_probe = NULL;
+ struct win2030_noc_prof_measure *prof_measure;
+ unsigned counter[WIN2030_NOC_BIN_CNT] = {0};
+ unsigned ret;
+ unsigned long flags;
+ int i, j;
+ u64 total = 0;
+ int percentage = 0;
+
+ /* Find which prof_probe is linked to the probe
+ * which generated the interrupt
+ */
+ list_for_each_entry(prof_probe, &noc_device->prof->prof_probe, link) {
+ if (prof_probe->probe == probe)
+ break;
+ }
+
+ /* Take measure object */
+ prof_measure = list_first_entry(&prof_probe->measure,
+ struct win2030_noc_prof_measure, link);
+ for (j = 0; j < probe->nr_portsel; j++) {
+ total = 0;
+ for (i = 0; i < WIN2030_NOC_BIN_CNT; i++) {
+ /**
+ Read HW counters
+ */
+ ret = win2030_bitfield_read(probe->counters[j * 4 + i].value, &counter[i]);
+ WARN_ON(0 != ret);
+ total += counter[i];
+ }
+ spin_lock_irqsave(&prof_measure->lock, flags);
+ if (0 == total) {
+ /*if total == 0, each bin's percentage will be 0*/
+ total = (-1U);
+ } else {
+ /*
+ only update iteration when there are traffic,
+ otherwize the mean value will be calculate by a wrong iteration number.
+ */
+ if (prof_measure->iteration[j] != MAX_ITERATION) {
+ prof_measure->iteration[j] += 1;
+ } else {
+ /*OverFlow*/
+ dev_info(noc_device->dev, "TracePort %s iteration overflow, reset measure statistics!\n",
+ prof_probe->probe->available_portsel[j]);
+ prof_measure->iteration[j] = 1;
+ for (i = 0; i < WIN2030_NOC_BIN_CNT; i++) {
+ prof_measure->mean[j][i] = 0;
+ }
+ }
+ }
+ for (i = 0; i < WIN2030_NOC_BIN_CNT; i++) {
+ /* calculate each bin's percentage in the total traffic*/
+ percentage = counter[i] * 100 / total;
+ /* Update prof_measure statistics */
+ if (percentage > prof_measure->max[j][i]) {
+ prof_measure->max[j][i] = percentage;
+ }
+ if (percentage < prof_measure->min[j][i]) {
+ prof_measure->min[j][i] = percentage;
+ }
+ prof_measure->now[j][i] = percentage;
+ prof_measure->mean[j][i] += percentage;
+ }
+ spin_unlock_irqrestore(&prof_measure->lock, flags);
+ }
+
+ /* Program next measure */
+ if (noc_device->prof->run == true) {
+ /* Move to the next measure */
+ spin_lock_irqsave(&prof_probe->lock, flags);
+ list_move(&prof_measure->link, &prof_probe->measure);
+ spin_unlock_irqrestore(&prof_probe->lock, flags);
+
+ /* Program it */
+ ret = win2030_noc_prof_launch_measure(noc_device, prof_probe);
+ if (ret)
+ dev_err(noc_device->dev, "Error when configuring prof measures!\n");
+ }
+}
diff --git a/drivers/interconnect/eswin/noc_profiler_sysfs.c b/drivers/interconnect/eswin/noc_profiler_sysfs.c
new file mode 100644
index 000000000000..e36c6ff61706
--- /dev/null
+++ b/drivers/interconnect/eswin/noc_profiler_sysfs.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <asm/div64.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include "noc.h"
+
+/**
+ * Called when writing run file
+ */
+static ssize_t noc_prof_run_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct win2030_noc_device *noc_device = filp->private_data;
+ char buf[SIZE_SMALL_BUF];
+ unsigned ret;
+
+ if (cnt > SIZE_SMALL_BUF)
+ cnt = SIZE_SMALL_BUF - 1;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ if (buf[0] == '0') {
+ noc_device->prof->run = false; /* FIXME spinlock? */
+ /*wait for the latest measure to be finished*/
+ msleep(5);
+ win2030_noc_prof_reset_statistics(noc_device->dev);
+ } else if (buf[0] == '1') {
+ if (true != noc_device->prof->run) {
+ noc_device->prof->run = true;
+ /* trigger measurements */
+ ret = win2030_noc_prof_trigger(noc_device->dev);
+ WARN_ON(ret != 0);
+ }
+ } else
+ return -EFAULT;
+
+ *ppos += cnt;
+
+ return cnt;
+}
+
+/**
+ * Called when reading run file
+ */
+static ssize_t noc_prof_run_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+#define RUN_STR_SIZE 11
+ struct win2030_noc_device *noc_device = filp->private_data;
+ char buf[RUN_STR_SIZE];
+ int r;
+
+ r = snprintf(buf, RUN_STR_SIZE, "%i\n", noc_device->prof->run);
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+/**
+ * Called when writing config file
+ */
+static ssize_t noc_prof_config_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ char buf[SIZE_SMALL_BUF] = {0};
+ char *bin;
+ int value;
+ int ret;
+ char bin_name[20] = {0};
+ int i;
+
+ if (cnt > SIZE_SMALL_BUF)
+ cnt = SIZE_SMALL_BUF - 1;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ *ppos += cnt;
+ for (i = 0; i < 3; i++) {
+ sprintf(bin_name, "latency_bin%d:", i);
+ bin = strstr(buf, bin_name);
+ if (bin) {
+ ret = kstrtoint(bin + strlen(bin_name), 0, &value);
+ if (!ret) {
+ latency_bin[i] = value;
+ return cnt;
+ }
+ }
+ }
+ pr_err("invalid config %s", buf);
+ return cnt;
+}
+
+/**
+ * Called when reading config file
+ */
+static ssize_t noc_prof_config_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ return 0;
+}
+
+/**
+ * Called when reading readme file
+ * @param m
+ * @param p
+ * @return
+ */
+static int noc_prof_readme_show(struct seq_file *m, void *p)
+{
+ seq_puts(m,
+ "How to use the NOC profiler:\nFile run: write '1' to enable "
+ "profiler (enabling profiler resets statistics),"
+ " write '0' to disable profiler.\n"
+ "File config: Not yet implemented.\n"
+ "File results: contain the results of measurement(each bin's percentage in the total traffic)"
+ "for all nodes defined in dts.\n");
+ return 0;
+}
+
+/**
+ * Called when reading results file.
+ */
+static ssize_t noc_prof_results_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct win2030_noc_device *noc_device = filp->private_data;
+ struct win2030_noc_probe *probe;
+ struct win2030_noc_prof *prof;
+ struct win2030_noc_prof_probe *prof_probe = NULL;
+ struct win2030_noc_prof_measure *prof_measure = NULL;
+ u64 meas_mean;
+ int i, j;
+ char *buf1;
+ char buf2[SIZE_SMALL_BUF + 2];
+ int r;
+ ssize_t ret;
+ int count; /* Used to avoid write over buffer size */
+ char latency_bin_name[4][20] = {{0}};
+ char *pending_bin_name[] = {"0~2", "2~5","5~10", "10~"};
+ unsigned long flags;
+
+ if (noc_device == NULL)
+ return -ENODEV;
+
+ prof = noc_device->prof;
+ if (!prof)
+ return -ENODEV;
+
+ buf1 = kzalloc(sizeof(*buf1) * SIZE_BIG_BUF, GFP_KERNEL);
+ if (!buf1)
+ return -ENODEV;
+
+ r = snprintf(buf1, SIZE_BIG_BUF,"\t##### RESULTS #####\n\n");
+
+ count = SIZE_BIG_BUF - strlen(buf1);
+ list_for_each_entry(prof_probe, &prof->prof_probe, link) {
+ probe = prof_probe->probe;
+ list_for_each_entry(prof_measure, &prof_probe->measure, link) {
+ if (count > 0) {
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "Probe %s\n", prof_probe->probe->id);
+ strncat(buf1, buf2, count);
+ count -= strlen(buf2);
+ }
+ if (pending_t == prof_measure->type) {
+
+ } else {
+ sprintf(latency_bin_name[0], "\"0~%d\"", latency_bin[0]);
+ sprintf(latency_bin_name[1], "\"%d~%d\"",latency_bin[0], latency_bin[1]);
+ sprintf(latency_bin_name[2], "\"%d~%d\"",latency_bin[1], latency_bin[2]);
+ sprintf(latency_bin_name[3], "\"%d~\"", latency_bin[2]);
+ }
+ spin_lock_irqsave(&prof_measure->lock, flags);
+ for (j = 0; j < probe->nr_portsel; j++) {
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "\tTracePort %s, NbOfMeasure=%i, type %s\n",
+ prof_probe->probe->available_portsel[j],
+ prof_measure->iteration[j],
+ prof_measure->type == pending_t ? "pedning" : "latency");
+ strncat(buf1, buf2, count);
+ count -= strlen(buf2);
+ if (count > 0) {
+ if (prof_measure->iteration[j] != 0) {
+ for (i = 0; i < WIN2030_NOC_BIN_CNT; i++) {
+ meas_mean = prof_measure->mean[j][i];
+ do_div(meas_mean, prof_measure->iteration[j]);
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "\t\t%s(cycles) percentage: min=%llu%% max=%llu%% mean=%llu%% "
+ "current=%llu%% \n\n",
+ pending_t == prof_measure->type ? pending_bin_name[i] : latency_bin_name[i],
+ prof_measure->min[j][i],
+ prof_measure->max[j][i],
+ meas_mean,
+ prof_measure->now[j][i]);
+ strncat(buf1, buf2, count);
+ count -= strlen(buf2);
+ }
+ } else {
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "\t\t No results\n\n");
+ strncat(buf1, buf2, count);
+ count -= strlen(buf2);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&prof_measure->lock, flags);
+ }
+ }
+
+ r = strlen(buf1) + 1;
+
+ ret = simple_read_from_buffer(ubuf, cnt, ppos, buf1, r);
+
+ kfree(buf1);
+ return ret;
+
+}
+
+/**
+ * Called when open results file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_prof_results_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * Called when opening config file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_prof_config_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * Called when opening run file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_prof_run_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * Called when reading readme file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_prof_readme_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, noc_prof_readme_show, inode->i_private);
+}
+
+static const struct file_operations noc_prof_results_fops = {
+ .open = noc_prof_results_open,
+ .read = noc_prof_results_read,
+ .llseek = generic_file_llseek,
+};
+
+static const struct file_operations noc_prof_config_fops = {
+ .open = noc_prof_config_open,
+ .read = noc_prof_config_read,
+ .write = noc_prof_config_write,
+ .llseek = generic_file_llseek,
+};
+
+static const struct file_operations noc_prof_run_fops = {
+ .open = noc_prof_run_open,
+ .read = noc_prof_run_read,
+ .write = noc_prof_run_write,
+ .llseek = generic_file_llseek,
+};
+
+static const struct file_operations noc_prof_readme_fops = {
+ .open = noc_prof_readme_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/**
+ * Entry point for NOC profiler sysfs
+ * @param _dev A reference to the device
+ * @return 0
+ */
+int win2030_noc_prof_debugfs_init(struct device *_dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct device_node *np = _dev->of_node;
+ struct dentry *dir, *d;
+ char name[32];
+
+ scnprintf(name, ARRAY_SIZE(name), "%s_prof", np->name);
+
+ dir = debugfs_create_dir(name, win2030_noc_ctrl.win2030_noc_root_debug_dir);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ noc_device->prof->dir = dir;
+
+ d = debugfs_create_file("results", S_IRUGO, dir, noc_device,
+ &noc_prof_results_fops);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+ d = debugfs_create_file("config", S_IRUGO | S_IWUSR, dir, noc_device,
+ &noc_prof_config_fops);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+ d = debugfs_create_file("run", S_IRUGO | S_IWUSR, dir, noc_device,
+ &noc_prof_run_fops);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+ d = debugfs_create_file("readme.txt", S_IRUGO, dir, noc_device,
+ &noc_prof_readme_fops);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+
+ return 0;
+}
diff --git a/drivers/interconnect/eswin/noc_regs.h b/drivers/interconnect/eswin/noc_regs.h
new file mode 100644
index 000000000000..2dc67a042540
--- /dev/null
+++ b/drivers/interconnect/eswin/noc_regs.h
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#ifndef _NOC_REGS_H_
+#define _NOC_REGS_H_
+
+#define ERRLOG_0_ID_COREID(_base) ((_base) + 0x0)
+#define ERRLOG_0_ID_COREID_CORETYPEID_OFFSET 0x0
+#define ERRLOG_0_ID_COREID_CORETYPEID_WIDTH 0x8
+#define ERRLOG_0_ID_COREID_CORETYPEID_MASK 0xff
+#define ERRLOG_0_ID_COREID_CORETYPEID(_reg) (((_reg) & 0xff) >> 0x0)
+#define ERRLOG_0_ID_COREID_CORECHECKSUM_OFFSET 0x8
+#define ERRLOG_0_ID_COREID_CORECHECKSUM_WIDTH 0x18
+#define ERRLOG_0_ID_COREID_CORECHECKSUM_MASK 0xffffff00
+#define ERRLOG_0_ID_COREID_CORECHECKSUM(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define ERRLOG_0_ID_REVISIONID(_base) ((_base) + 0x4)
+#define ERRLOG_0_ID_REVISIONID_USERID_OFFSET 0x0
+#define ERRLOG_0_ID_REVISIONID_USERID_WIDTH 0x8
+#define ERRLOG_0_ID_REVISIONID_USERID_MASK 0xff
+#define ERRLOG_0_ID_REVISIONID_USERID(_reg) (((_reg) & 0xff) >> 0x0)
+#define ERRLOG_0_ID_REVISIONID_FLEXNOCID_OFFSET 0x8
+#define ERRLOG_0_ID_REVISIONID_FLEXNOCID_WIDTH 0x18
+#define ERRLOG_0_ID_REVISIONID_FLEXNOCID_MASK 0xffffff00
+#define ERRLOG_0_ID_REVISIONID_FLEXNOCID(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define ERRLOG_0_FAULTEN(_base) ((_base) + 0x8)
+#define ERRLOG_0_FAULTEN_FAULTEN_OFFSET 0x0
+#define ERRLOG_0_FAULTEN_FAULTEN_WIDTH 0x1
+#define ERRLOG_0_FAULTEN_FAULTEN_MASK 0x1
+#define ERRLOG_0_FAULTEN_FAULTEN(_reg) (((_reg) & 0x1) >> 0x0)
+
+#define ERRLOG_0_ERRVLD(_base) ((_base) + 0xc)
+#define ERRLOG_0_ERRVLD_ERRVLD_OFFSET 0x0
+#define ERRLOG_0_ERRVLD_ERRVLD_WIDTH 0x1
+#define ERRLOG_0_ERRVLD_ERRVLD_MASK 0x1
+#define ERRLOG_0_ERRVLD_ERRVLD(_reg) (((_reg) & 0x1) >> 0x0)
+
+#define ERRLOG_0_ERRCLR(_base) ((_base) + 0x10)
+#define ERRLOG_0_ERRCLR_ERRCLR_OFFSET 0x0
+#define ERRLOG_0_ERRCLR_ERRCLR_WIDTH 0x1
+#define ERRLOG_0_ERRCLR_ERRCLR_MASK 0x1
+#define ERRLOG_0_ERRCLR_ERRCLR(_reg) (((_reg) & 0x1) >> 0x0)
+
+#define ERRLOG_0_ERRLOG0(_base) ((_base) + 0x14)
+#define ERRLOG_0_ERRLOG0_LOCK_OFFSET 0x0
+#define ERRLOG_0_ERRLOG0_LOCK_WIDTH 0x1
+#define ERRLOG_0_ERRLOG0_LOCK_MASK 0x1
+#define ERRLOG_0_ERRLOG0_LOCK(_reg) (((_reg) & 0x1) >> 0x0)
+#define ERRLOG_0_ERRLOG0_OPC_OFFSET 0x1
+#define ERRLOG_0_ERRLOG0_OPC_WIDTH 0x4
+#define ERRLOG_0_ERRLOG0_OPC_MASK 0x1e
+#define ERRLOG_0_ERRLOG0_OPC(_reg) (((_reg) & 0x1e) >> 0x1)
+#define ERRLOG_0_ERRLOG0_ERRCODE_OFFSET 0x8
+#define ERRLOG_0_ERRLOG0_ERRCODE_WIDTH 0x3
+#define ERRLOG_0_ERRLOG0_ERRCODE_MASK 0x700
+#define ERRLOG_0_ERRLOG0_ERRCODE(_reg) (((_reg) & 0x700) >> 0x8)
+#define ERRLOG_0_ERRLOG0_LEN1_OFFSET 0x10
+#define ERRLOG_0_ERRLOG0_LEN1_WIDTH 0x7
+#define ERRLOG_0_ERRLOG0_LEN1_MASK 0x7f0000
+#define ERRLOG_0_ERRLOG0_LEN1(_reg) (((_reg) & 0x7f0000) >> 0x10)
+#define ERRLOG_0_ERRLOG0_FORMAT_OFFSET 0x1f
+#define ERRLOG_0_ERRLOG0_FORMAT_WIDTH 0x1
+#define ERRLOG_0_ERRLOG0_FORMAT_MASK 0x80000000
+#define ERRLOG_0_ERRLOG0_FORMAT(_reg) (((_reg) & 0x80000000) >> 0x1f)
+
+#define ERRLOG_0_ERRLOG1(_base) ((_base) + 0x18)
+#define ERRLOG_0_ERRLOG1_ERRLOG1_OFFSET 0x0
+#define ERRLOG_0_ERRLOG1_ERRLOG1_WIDTH 0x11
+#define ERRLOG_0_ERRLOG1_ERRLOG1_MASK 0x1ffff
+#define ERRLOG_0_ERRLOG1_ERRLOG1(_reg) (((_reg) & 0x1ffff) >> 0x0)
+
+#define ERRLOG_0_ERRLOG3(_base) ((_base) + 0x20)
+#define ERRLOG_0_ERRLOG3_ERRLOG3_OFFSET 0x0
+#define ERRLOG_0_ERRLOG3_ERRLOG3_WIDTH 0x20
+#define ERRLOG_0_ERRLOG3_ERRLOG3_MASK 0xffffffff
+#define ERRLOG_0_ERRLOG3_ERRLOG3(_reg) (((_reg) & 0xffffffff) >> 0x0)
+
+#define ERRLOG_0_ERRLOG5(_base) ((_base) + 0x28)
+#define ERRLOG_0_ERRLOG5_ERRLOG5_OFFSET 0x0
+#define ERRLOG_0_ERRLOG5_ERRLOG5_WIDTH 0x9
+#define ERRLOG_0_ERRLOG5_ERRLOG5_MASK 0x1ff
+#define ERRLOG_0_ERRLOG5_ERRLOG5(_reg) (((_reg) & 0x1ff) >> 0x0)
+
+#define ERRLOG_0_ERRLOG7(_base) ((_base) + 0x30)
+#define ERRLOG_0_ERRLOG7_ERRLOG7_OFFSET 0x0
+#define ERRLOG_0_ERRLOG7_ERRLOG7_WIDTH 0x4
+#define ERRLOG_0_ERRLOG7_ERRLOG7_MASK 0xf
+#define ERRLOG_0_ERRLOG7_ERRLOG7(_reg) (((_reg) & 0xf) >> 0x0)
+
+#define PROBE_ID_COREID(_base) ((_base) + 0x1000)
+#define PROBE_ID_COREID_CORETYPEID_OFFSET 0x0
+#define PROBE_ID_COREID_CORETYPEID_WIDTH 0x8
+#define PROBE_ID_COREID_CORETYPEID_MASK 0xff
+#define PROBE_ID_COREID_CORETYPEID(_reg) (((_reg) & 0xff) >> 0x0)
+#define PROBE_ID_COREID_CORECHECKSUM_OFFSET 0x8
+#define PROBE_ID_COREID_CORECHECKSUM_WIDTH 0x18
+#define PROBE_ID_COREID_CORECHECKSUM_MASK 0xffffff00
+#define PROBE_ID_COREID_CORECHECKSUM(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define PROBE_ID_REVISIONID(_base) ((_base) + 0x1004)
+#define PROBE_ID_REVISIONID_USERID_OFFSET 0x0
+#define PROBE_ID_REVISIONID_USERID_WIDTH 0x8
+#define PROBE_ID_REVISIONID_USERID_MASK 0xff
+#define PROBE_ID_REVISIONID_USERID(_reg) (((_reg) & 0xff) >> 0x0)
+#define PROBE_ID_REVISIONID_FLEXNOCID_OFFSET 0x8
+#define PROBE_ID_REVISIONID_FLEXNOCID_WIDTH 0x18
+#define PROBE_ID_REVISIONID_FLEXNOCID_MASK 0xffffff00
+#define PROBE_ID_REVISIONID_FLEXNOCID(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define PROBE_MAINCTL (0x8)
+#define PROBE_MAINCTL_ERREN_OFFSET 0x0
+#define PROBE_MAINCTL_ERREN_WIDTH 0x1
+#define PROBE_MAINCTL_ERREN_MASK 0x1
+#define PROBE_MAINCTL_ERREN(_reg) (((_reg) & 0x1) >> 0x0)
+#define PROBE_MAINCTL_TRACEEN_OFFSET 0x1
+#define PROBE_MAINCTL_TRACEEN_WIDTH 0x1
+#define PROBE_MAINCTL_TRACEEN_MASK 0x2
+#define PROBE_MAINCTL_TRACEEN(_reg) (((_reg) & 0x2) >> 0x1)
+#define PROBE_MAINCTL_PAYLOADEN_OFFSET 0x2
+#define PROBE_MAINCTL_PAYLOADEN_WIDTH 0x1
+#define PROBE_MAINCTL_PAYLOADEN_MASK 0x4
+#define PROBE_MAINCTL_PAYLOADEN(_reg) (((_reg) & 0x4) >> 0x2)
+#define PROBE_MAINCTL_STATEN_OFFSET 0x3
+#define PROBE_MAINCTL_STATEN_WIDTH 0x1
+#define PROBE_MAINCTL_STATEN_MASK 0x8
+#define PROBE_MAINCTL_STATEN(_reg) (((_reg) & 0x8) >> 0x3)
+#define PROBE_MAINCTL_ALARMEN_OFFSET 0x4
+#define PROBE_MAINCTL_ALARMEN_WIDTH 0x1
+#define PROBE_MAINCTL_ALARMEN_MASK 0x10
+#define PROBE_MAINCTL_ALARMEN(_reg) (((_reg) & 0x10) >> 0x4)
+#define PROBE_MAINCTL_STATCONDDUMP_OFFSET 0x5
+#define PROBE_MAINCTL_STATCONDDUMP_WIDTH 0x1
+#define PROBE_MAINCTL_STATCONDDUMP_MASK 0x20
+#define PROBE_MAINCTL_STATCONDDUMP(_reg) (((_reg) & 0x20) >> 0x5)
+#define PROBE_MAINCTL_INTRUSIVEMODE_OFFSET 0x6
+#define PROBE_MAINCTL_INTRUSIVEMODE_WIDTH 0x1
+#define PROBE_MAINCTL_INTRUSIVEMODE_MASK 0x40
+#define PROBE_MAINCTL_INTRUSIVEMODE(_reg) (((_reg) & 0x40) >> 0x6)
+#define PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_OFFSET 0x7
+#define PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_WIDTH 0x1
+#define PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_MASK 0x80
+#define PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN(_reg) (((_reg) & 0x80) >> 0x7)
+
+#define PROBE_CFGCTL (0xc)
+#define PROBE_CFGCTL_GLOBALEN_OFFSET 0x0
+#define PROBE_CFGCTL_GLOBALEN_WIDTH 0x1
+#define PROBE_CFGCTL_GLOBALEN_MASK 0x1
+#define PROBE_CFGCTL_GLOBALEN(_reg) (((_reg) & 0x1) >> 0x0)
+#define PROBE_CFGCTL_ACTIVE_OFFSET 0x1
+#define PROBE_CFGCTL_ACTIVE_WIDTH 0x1
+#define PROBE_CFGCTL_ACTIVE_MASK 0x2
+#define PROBE_CFGCTL_ACTIVE(_reg) (((_reg) & 0x2) >> 0x1)
+
+#define PROBE_TRACEPORTSEL (0x10)
+#define PROBE_TRACEPORTSEL_TRACEPORTSEL_OFFSET 0x0
+#define PROBE_TRACEPORTSEL_TRACEPORTSEL_WIDTH 0x3
+#define PROBE_TRACEPORTSEL_TRACEPORTSEL_MASK 0x7
+#define PROBE_TRACEPORTSEL_TRACEPORTSEL(_reg) (((_reg) & 0x7) >> 0x0)
+
+#define PROBE_FILTERLUT (0x14)
+#define PROBE_FILTERLUT_FILTERLUT_OFFSET 0x0
+#define PROBE_FILTERLUT_FILTERLUT_WIDTH 0x2
+#define PROBE_FILTERLUT_FILTERLUT_MASK 0x3
+#define PROBE_FILTERLUT_FILTERLUT(_reg) (((_reg) & PROBE_FILTERLUT_FILTERLUT_MASK) >> 0x0)
+
+#define PROBE_TRACEALARMEN (0x18)
+#define PROBE_TRACEALARMEN_TRACEALARMEN_OFFSET 0x0
+#define PROBE_TRACEALARMEN_TRACEALARMEN_WIDTH 0x5
+#define PROBE_TRACEALARMEN_TRACEALARMEN_MASK 0x1f
+#define PROBE_TRACEALARMEN_TRACEALARMEN(_reg) (((_reg) & 0x1f) >> 0x0)
+
+#define PROBE_TRACEALARMSTATUS (0x1c)
+#define PROBE_TRACEALARMSTATUS_TRACEALARMSTATUS_OFFSET 0x0
+#define PROBE_TRACEALARMSTATUS_TRACEALARMSTATUS_WIDTH 0x5
+#define PROBE_TRACEALARMSTATUS_TRACEALARMSTATUS_MASK 0x1f
+#define PROBE_TRACEALARMSTATUS_TRACEALARMSTATUS(_reg) (((_reg) & 0x1f) >> 0x0)
+
+#define PROBE_TRACEALARMCLR (0x20)
+#define PROBE_TRACEALARMCLR_TRACEALARMCLR_OFFSET 0x0
+#define PROBE_TRACEALARMCLR_TRACEALARMCLR_WIDTH 0x5
+#define PROBE_TRACEALARMCLR_TRACEALARMCLR_MASK 0x1f
+#define PROBE_TRACEALARMCLR_TRACEALARMCLR(_reg) (((_reg) & 0x1f) >> 0x0)
+
+#define PROBE_STATPERIOD (0x24)
+#define PROBE_STATPERIOD_STATPERIOD_OFFSET 0x0
+#define PROBE_STATPERIOD_STATPERIOD_WIDTH 0x5
+#define PROBE_STATPERIOD_STATPERIOD_MASK 0x1f
+#define PROBE_STATPERIOD_STATPERIOD(_reg) (((_reg) & 0x1f) >> 0x0)
+
+#define PROBE_STATGO (0x28)
+#define PROBE_STATGO_STATGO_OFFSET 0x0
+#define PROBE_STATGO_STATGO_WIDTH 0x1
+#define PROBE_STATGO_STATGO_MASK 0x1
+#define PROBE_STATGO_STATGO(_reg) (((_reg) & 0x1) >> 0x0)
+
+#define PROBE_STATALARMMIN (0x2c)
+#define PROBE_STATALARMMIN_STATALARMMIN_OFFSET 0x0
+#define PROBE_STATALARMMIN_STATALARMMIN_WIDTH 0x20
+#define PROBE_STATALARMMIN_STATALARMMIN_MASK 0xffffffff
+#define PROBE_STATALARMMIN_STATALARMMIN(_reg) (((_reg) & PROBE_STATALARMMIN_STATALARMMIN_MASK) >> 0x0)
+
+#define PROBE_STATALARMMIN_HIGH (0x30)
+#define PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_OFFSET 0x0
+#define PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_WIDTH 0x20
+#define PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_MASK 0xffffffff
+#define PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH(_reg) \
+ (((_reg) & PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_MASK) >> 0x0)
+#define PROBE_STATALARMMAX (0x34)
+#define PROBE_STATALARMMAX_STATALARMMAX_OFFSET 0x0
+#define PROBE_STATALARMMAX_STATALARMMAX_WIDTH 0x20
+#define PROBE_STATALARMMAX_STATALARMMAX_MASK 0xffffffff
+#define PROBE_STATALARMMAX_STATALARMMAX(_reg) (((_reg) & PROBE_STATALARMMAX_STATALARMMAX_MASK) >> 0x0)
+
+#define PROBE_STATALARMSTATUS (0x3C)
+#define PROBE_STATALARMSTATUS_STATALARMSTATUS_OFFSET 0x0
+#define PROBE_STATALARMSTATUS_STATALARMSTATUS_WIDTH 0x1
+#define PROBE_STATALARMSTATUS_STATALARMSTATUS_MASK 0x1
+#define PROBE_STATALARMSTATUS_STATALARMSTATUS(_reg) (((_reg) & 0x1) >> 0x0)
+
+#define PROBE_STATALARMCLR (0x40)
+#define PROBE_STATALARMCLR_STATALARMCLR_OFFSET 0x0
+#define PROBE_STATALARMCLR_STATALARMCLR_WIDTH 0x1
+#define PROBE_STATALARMCLR_STATALARMCLR_MASK 0x1
+#define PROBE_STATALARMCLR_STATALARMCLR(_reg) (((_reg) & 0x1) >> 0x0)
+
+#define PROBE_STATALARMEN (0x44)
+#define PROBE_STATALARMEN_STATALARMEN_OFFSET 0x0
+#define PROBE_STATALARMEN_STATALARMEN_WIDTH 0x1
+#define PROBE_STATALARMEN_STATALARMEN_MASK 0x1
+#define PROBE_STATALARMEN_STATALARMEN(_reg) (((_reg) & 0x1) >> 0x0)
+
+/*Trans probe filter begin*/
+#define PROBE_TRANS_FILTERS_MODE (0x08)
+#define PROBE_TRANS_FILTERS_MODE_FILTERS_MODE_OFFSET 0x0
+#define PROBE_TRANS_FILTERS_MODE_FILTERS_MODE_WIDTH 0x01
+#define PROBE_TRANS_FILTERS_MODE_FILTERS_MODE_MASK 0x01
+
+#define PROBE_TRANS_FILTERS_ADDRBASE_LOW (0x0C)
+#define PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_OFFSET 0x0
+#define PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_WIDTH 0x20
+#define PROBE_TRANS_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_MASK 0xffffffff
+
+#define PROBE_TRANS_FILTERS_ADDRBASE_HIGH (0x10)
+#define PROBE_TRANS_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_OFFSET 0x0
+#define PROBE_TRANS_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_WIDTH 0x09
+#define PROBE_TRANS_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_MASK 0x1FF
+
+#define PROBE_TRANS_FILTERS_WINDOWSIZE (0x14)
+#define PROBE_TRANS_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_OFFSET 0x0
+#define PROBE_TRANS_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_WIDTH 0x06
+
+#define PROBE_TRANS_FILTERS_OPCODE (0x20)
+#define PROBE_TRANS_FILTERS_OPCODE_RDEN_OFFSET 0x0
+#define PROBE_TRANS_FILTERS_OPCODE_RDEN_WIDTH 0x1
+#define PROBE_TRANS_FILTERS_OPCODE_RDEN_MASK 0x1
+
+#define PROBE_TRANS_FILTERS_OPCODE_WREN_OFFSET 0x1
+#define PROBE_TRANS_FILTERS_OPCODE_WREN_WIDTH 0x1
+#define PROBE_TRANS_FILTERS_OPCODE_WREN_MASK 0x2
+
+#define PROBE_TRANS_FILTERS_USER_BASE (0x24)
+#define PROBE_TRANS_FILTERS_USER_BASE_FILTERS_USER_BASE 0x0
+#define PROBE_TRANS_FILTERS_USER_BASE_FILTERS_USER_BASE_WIDTH 0x12
+
+#define PROBE_TRANS_FILTERS_USER_MASK (0x28)
+#define PROBE_TRANS_FILTERS_USER_MASK_FILTERS_USER_MASK 0x0
+#define PROBE_TRANS_FILTERS_USER_MASK_FILTERS_USER_MASK_WIDTH 0x12
+
+/**trans probe filter end*/
+
+/*Trans probe profiler begin*/
+#define PROBE_TRANS_PROFILER_EN (0x08)
+#define PROBE_TRANS_PROFILER_EN_PROFILER_EN_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_EN_PROFILER_EN_WIDTH 0x01
+#define PROBE_TRANS_PROFILER_EN_PROFILER_EN_MASK 0x01
+
+#define PROBE_TRANS_PROFILER_MODE (0x0C)
+#define PROBE_TRANS_PROFILER_MODE_PROFILER_MODE_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_MODE_PROFILER_MODE_WIDTH 0x2
+
+#define PROBE_TRANS_PROFILER_OBSERVED_SEL (0x10)
+#define PROBE_TRANS_PROFILER_OBSERVED_SEL_PROFILER_OBSERVED_SEL_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_OBSERVED_SEL_PROFILER_OBSERVED_SEL_WIDTH 0x01
+#define PROBE_TRANS_PROFILER_OBSERVED_SEL_PROFILER_OBSERVED_SEL_MASK 0x01
+
+#define PROBE_TRANS_PROFILER_N_TENURE_LINES (0x20)
+#define PROBE_TRANS_PROFILER_N_TENURE_LINES_PROFILER_N_TENURE_LINES_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_N_TENURE_LINES_PROFILER_N_TENURE_LINES_WIDTH 0x2
+#define PROBE_TRANS_PROFILER_N_TENURE_LINES_PROFILER_N_TENURE_LINES_MASK 0x3
+
+#define PROBE_TRANS_PROFILER_THRESHOLDS (0x2C)
+#define PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_WIDTH 0xa
+#define PROBE_TRANS_PROFILER_THRESHOLDS_PROFILER_THRESHOLDS_MASK 0x3FF
+
+#define PROBE_TRANS_PROFILER_OVER_FLOW_STATUS (0x6C)
+#define PROBE_TRANS_PROFILER_OVER_FLOW_STATUS_PROFILER_OVER_FLOW_STATUS_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_OVER_FLOW_STATUS_PROFILER_OVER_FLOW_STATUS_WIDTH 0x2
+
+#define PROBE_TRANS_PROFILER_OVER_FLOW_RESET (0x70)
+#define PROBE_TRANS_PROFILER_OVER_FLOW_RESET_PROFILER_OVER_FLOW_RESET_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_OVER_FLOW_RESET_PROFILER_OVER_FLOW_RESET_WIDTH 0x2
+
+#define PROBE_TRANS_PROFILER_PENDING_EVENT_MODE (0x74)
+#define PROBE_TRANS_PROFILER_PENDING_EVENT_MODE_PROFILER_PENDING_EVENT_MODE_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_PENDING_EVENT_MODE_PROFILER_PENDING_EVENT_MODE_WIDTH 0x1
+
+#define PROBE_TRANS_PROFILER_PRE_SCALER (0x78)
+#define PROBE_TRANS_PROFILER_PRE_SCALER_PROFILER_PRE_SCALER_OFFSET 0x0
+#define PROBE_TRANS_PROFILER_PRE_SCALER_PROFILER_PRE_SCALER_WIDTH 0x8
+/*trans probe profiler end*/
+
+#define PROBE_FILTERS_ROUTEIDBASE(_filter) \
+ ((_filter * 0x3C) + 0x80)
+#define PROBE_FILTERS_ROUTEIDBASE_FILTERS_ROUTEIDBASE_OFFSET 0x0
+#define PROBE_FILTERS_ROUTEIDBASE_FILTERS_ROUTEIDBASE_WIDTH 0x11
+#define PROBE_FILTERS_ROUTEIDBASE_FILTERS_ROUTEIDBASE_MASK 0x1ffff
+#define PROBE_FILTERS_ROUTEIDBASE_FILTERS_ROUTEIDBASE(_reg) \
+ (((_reg) & 0x1ffff) >> 0x0)
+
+#define PROBE_FILTERS_ROUTEIDMASK(_filter) \
+ ((_filter * 0x3C) + 0x84)
+#define PROBE_FILTERS_ROUTEIDMASK_FILTERS_ROUTEIDMASK_OFFSET 0x0
+#define PROBE_FILTERS_ROUTEIDMASK_FILTERS_ROUTEIDMASK_WIDTH 0x11
+#define PROBE_FILTERS_ROUTEIDMASK_FILTERS_ROUTEIDMASK_MASK 0x1ffff
+#define PROBE_FILTERS_ROUTEIDMASK_FILTERS_ROUTEIDMASK(_reg) \
+ (((_reg) & 0x1ffff) >> 0x0)
+
+#define PROBE_FILTERS_ADDRBASE_LOW(_filter) \
+ ((_filter * 0x3C) + 0x88)
+#define PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_OFFSET 0x0
+#define PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_WIDTH 0x20
+#define PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_MASK 0xffffffff
+#define PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW(_reg) \
+ (((_reg) & PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_MASK) >> 0x0)
+
+#define PROBE_FILTERS_ADDRBASE_HIGH(_filter) \
+ ((_filter * 0x3C) + 0x8C)
+#define PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_OFFSET 0x0
+#define PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_WIDTH 0x09
+#define PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_MASK 0x1ff
+#define PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH(_reg) \
+ (((_reg) & PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_MASK) >> 0x0)
+
+#define PROBE_FILTERS_WINDOWSIZE(_filter) \
+ ((_filter * 0x3C) + 0x90)
+#define PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_OFFSET 0x0
+#define PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_WIDTH 0x6
+#define PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_MASK 0x3f
+#define PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE(_reg) \
+ (((_reg) & 0x3f) >> 0x0)
+
+#define PROBE_FILTERS_SECURITYBASE(_filter) \
+ ((_filter * 0x3C) + 0x94)
+#define PROBE_FILTERS_SECURITYBASE_FILTERS_SECURITYBASE_OFFSET 0x0
+#define PROBE_FILTERS_SECURITYBASE_FILTERS_SECURITYBASE_WIDTH 0x4
+#define PROBE_FILTERS_SECURITYBASE_FILTERS_SECURITYBASE_MASK 0xf
+#define PROBE_FILTERS_SECURITYBASE_FILTERS_SECURITYBASE(_reg) \
+ (((_reg) & 0xf) >> 0x0)
+
+#define PROBE_FILTERS_SECURITYMASK(_filter) \
+ ((_filter * 0x3C) + 0x98)
+#define PROBE_FILTERS_SECURITYMASK_FILTERS_SECURITYMASK_OFFSET 0x0
+#define PROBE_FILTERS_SECURITYMASK_FILTERS_SECURITYMASK_WIDTH 0x4
+#define PROBE_FILTERS_SECURITYMASK_FILTERS_SECURITYMASK_MASK 0xf
+#define PROBE_FILTERS_SECURITYMASK_FILTERS_SECURITYMASK(_reg) \
+ (((_reg) & 0xf) >> 0x0)
+
+#define PROBE_FILTERS_OPCODE(_filter) \
+ ((_filter * 0x3C) + 0x9c)
+#define PROBE_FILTERS_OPCODE_RDEN_OFFSET 0x0
+#define PROBE_FILTERS_OPCODE_RDEN_WIDTH 0x1
+#define PROBE_FILTERS_OPCODE_RDEN_MASK 0x1
+#define PROBE_FILTERS_OPCODE_RDEN(_reg) (((_reg) & 0x1) >> 0x0)
+#define PROBE_FILTERS_OPCODE_WREN_OFFSET 0x1
+#define PROBE_FILTERS_OPCODE_WREN_WIDTH 0x1
+#define PROBE_FILTERS_OPCODE_WREN_MASK 0x2
+#define PROBE_FILTERS_OPCODE_WREN(_reg) (((_reg) & 0x2) >> 0x1)
+#define PROBE_FILTERS_OPCODE_LOCKEN_OFFSET 0x2
+#define PROBE_FILTERS_OPCODE_LOCKEN_WIDTH 0x1
+#define PROBE_FILTERS_OPCODE_LOCKEN_MASK 0x4
+#define PROBE_FILTERS_OPCODE_LOCKEN(_reg) (((_reg) & 0x4) >> 0x2)
+#define PROBE_FILTERS_OPCODE_URGEN_OFFSET 0x3
+#define PROBE_FILTERS_OPCODE_URGEN_WIDTH 0x1
+#define PROBE_FILTERS_OPCODE_URGEN_MASK 0x8
+#define PROBE_FILTERS_OPCODE_URGEN(_reg) (((_reg) & 0x8) >> 0x3)
+
+#define PROBE_FILTERS_STATUS(_filter) \
+ ((_filter * 0x3C) + 0xA0)
+#define PROBE_FILTERS_STATUS_REQEN_OFFSET 0x0
+#define PROBE_FILTERS_STATUS_REQEN_WIDTH 0x1
+#define PROBE_FILTERS_STATUS_REQEN_MASK 0x1
+#define PROBE_FILTERS_STATUS_REQEN(_reg) (((_reg) & 0x1) >> 0x0)
+#define PROBE_FILTERS_STATUS_RSPEN_OFFSET 0x1
+#define PROBE_FILTERS_STATUS_RSPEN_WIDTH 0x1
+#define PROBE_FILTERS_STATUS_RSPEN_MASK 0x2
+#define PROBE_FILTERS_STATUS_RSPEN(_reg) (((_reg) & 0x2) >> 0x1)
+
+#define PROBE_FILTERS_LENGTH(_filter) \
+ ((_filter * 0x3C) + 0xA4)
+#define PROBE_FILTERS_LENGTH_FILTERS_LENGTH_OFFSET 0x0
+#define PROBE_FILTERS_LENGTH_FILTERS_LENGTH_WIDTH 0x4
+#define PROBE_FILTERS_LENGTH_FILTERS_LENGTH_MASK 0xf
+#define PROBE_FILTERS_LENGTH_FILTERS_LENGTH(_reg) (((_reg) & 0xf) >> 0x0)
+
+#define PROBE_FILTERS_URGENCY(_filter) \
+ ((_filter * 0x3C) + 0xA8)
+#define PROBE_FILTERS_URGENCY_FILTERS_URGENCY_OFFSET 0x0
+#define PROBE_FILTERS_URGENCY_FILTERS_URGENCY_WIDTH 0x3
+#define PROBE_FILTERS_URGENCY_FILTERS_URGENCY_MASK 0x7
+#define PROBE_FILTERS_URGENCY_FILTERS_URGENCY(_reg) (((_reg) & PROBE_FILTERS_URGENCY_FILTERS_URGENCY_MASK) >> 0x0)
+
+#define PROBE_COUNTERS_PORTSEL(_offset, _probe, _cnt) \
+ ((_cnt * 0x10) + 0x200)
+#define PROBE_COUNTERS_PORTSEL_COUNTERS_PORTSEL_OFFSET 0x0
+#define PROBE_COUNTERS_PORTSEL_COUNTERS_PORTSEL_WIDTH 0x3
+#define PROBE_COUNTERS_PORTSEL_COUNTERS_PORTSEL_MASK 0x7
+#define PROBE_COUNTERS_PORTSEL_COUNTERS_PORTSEL(_reg) (((_reg) & 0x7) >> 0x0)
+
+#define PROBE_COUNTERS_SRC(_cnt) \
+ ((_cnt * 0x10) + 0x204)
+#define PROBE_COUNTERS_SRC_INTEVENT_OFFSET 0x0
+#define PROBE_COUNTERS_SRC_INTEVENT_WIDTH 0x6
+#define PROBE_COUNTERS_SRC_INTEVENT_MASK 0x3f
+#define PROBE_COUNTERS_SRC_INTEVENT(_reg) (((_reg) & PROBE_COUNTERS_SRC_INTEVENT_MASK) >> 0x0)
+
+#define PROBE_COUNTERS_ALARMMODE(_cnt) \
+ ((_cnt * 0x10) + 0x208)
+#define PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_OFFSET 0x0
+#define PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_WIDTH 0x2
+#define PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_MASK 0x3
+#define PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE(_reg) \
+ (((_reg) & 0x3) >> 0x0)
+
+#define PROBE_COUNTERS_VAL(_cnt) \
+ ((_cnt * 0x10) + 0x20C)
+#define PROBE_COUNTERS_VAL_COUNTERS_VAL_OFFSET 0x0
+#define PROBE_COUNTERS_VAL_COUNTERS_VAL_WIDTH 0x20
+#define PROBE_COUNTERS_VAL_COUNTERS_VAL_MASK 0xffff
+#define PROBE_COUNTERS_VAL_COUNTERS_VAL(_reg) (((_reg) & PROBE_COUNTERS_VAL_COUNTERS_VAL_MASK) >> 0x0)
+
+#define SBM_SENSE_IN0 (0xB0)
+#define SBM_SENSE_IN0_SENSE_IN0_OFFSET 0x0
+#define SBM_SENSE_IN0_SENSE_IN0_WIDTH 0x1
+#define SBM_SENSE_IN0_SENSE_IN0_MASK 0xffff
+#define SBM_SENSE_IN0_SENSE_IN0(_reg) (((_reg) & SBM_SENSE_IN0_SENSE_IN0_MASK) >> 0x0)
+
+#define OBS_L1_ATB_ID_COREID(_base) ((_base) + 0x1c00)
+#define OBS_L1_ATB_ID_COREID_CORETYPEID_OFFSET 0x0
+#define OBS_L1_ATB_ID_COREID_CORETYPEID_WIDTH 0x8
+#define OBS_L1_ATB_ID_COREID_CORETYPEID_MASK 0xff
+#define OBS_L1_ATB_ID_COREID_CORETYPEID(_reg) (((_reg) & 0xff) >> 0x0)
+#define OBS_L1_ATB_ID_COREID_CORECHECKSUM_OFFSET 0x8
+#define OBS_L1_ATB_ID_COREID_CORECHECKSUM_WIDTH 0x18
+#define OBS_L1_ATB_ID_COREID_CORECHECKSUM_MASK 0xffffff00
+#define OBS_L1_ATB_ID_COREID_CORECHECKSUM(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define OBS_L1_ATB_ID_REVISIONID(_base) ((_base) + 0x1c04)
+#define OBS_L1_ATB_ID_REVISIONID_USERID_OFFSET 0x0
+#define OBS_L1_ATB_ID_REVISIONID_USERID_WIDTH 0x8
+#define OBS_L1_ATB_ID_REVISIONID_USERID_MASK 0xff
+#define OBS_L1_ATB_ID_REVISIONID_USERID(_reg) (((_reg) & 0xff) >> 0x0)
+#define OBS_L1_ATB_ID_REVISIONID_FLEXNOCID_OFFSET 0x8
+#define OBS_L1_ATB_ID_REVISIONID_FLEXNOCID_WIDTH 0x18
+#define OBS_L1_ATB_ID_REVISIONID_FLEXNOCID_MASK 0xffffff00
+#define OBS_L1_ATB_ID_REVISIONID_FLEXNOCID(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define OBS_L1_ATB_ATBID(_base) ((_base) + 0x1c08)
+#define OBS_L1_ATB_ATBID_ATBID_OFFSET 0x0
+#define OBS_L1_ATB_ATBID_ATBID_WIDTH 0x7
+#define OBS_L1_ATB_ATBID_ATBID_MASK 0x7f
+#define OBS_L1_ATB_ATBID_ATBID(_reg) (((_reg) & 0x7f) >> 0x0)
+
+#define OBS_L1_ATB_ATBEN(_base) ((_base) + 0x1c0c)
+#define OBS_L1_ATB_ATBEN_ATBEN_OFFSET 0x0
+#define OBS_L1_ATB_ATBEN_ATBEN_WIDTH 0x1
+#define OBS_L1_ATB_ATBEN_ATBEN_MASK 0x1
+#define OBS_L1_ATB_ATBEN_ATBEN(_reg) (((_reg) & 0x1) >> 0x0)
+
+#define OBS_L1_ATB_SYNCPERIOD(_base) ((_base) + 0x1c10)
+#define OBS_L1_ATB_SYNCPERIOD_SYNCPERIOD_OFFSET 0x0
+#define OBS_L1_ATB_SYNCPERIOD_SYNCPERIOD_WIDTH 0x5
+#define OBS_L1_ATB_SYNCPERIOD_SYNCPERIOD_MASK 0x1f
+#define OBS_L1_ATB_SYNCPERIOD_SYNCPERIOD(_reg) (((_reg) & 0x1f) >> 0x0)
+
+#define GPU_QOS_GEN_ID_COREID(_base) ((_base) + 0x1d00)
+#define GPU_QOS_GEN_ID_COREID_CORETYPEID_OFFSET 0x0
+#define GPU_QOS_GEN_ID_COREID_CORETYPEID_WIDTH 0x8
+#define GPU_QOS_GEN_ID_COREID_CORETYPEID_MASK 0xff
+#define GPU_QOS_GEN_ID_COREID_CORETYPEID(_reg) (((_reg) & 0xff) >> 0x0)
+#define GPU_QOS_GEN_ID_COREID_CORECHECKSUM_OFFSET 0x8
+#define GPU_QOS_GEN_ID_COREID_CORECHECKSUM_WIDTH 0x18
+#define GPU_QOS_GEN_ID_COREID_CORECHECKSUM_MASK 0xffffff00
+#define GPU_QOS_GEN_ID_COREID_CORECHECKSUM(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define GPU_QOS_GEN_ID_REVISIONID(_base) ((_base) + 0x1d04)
+#define GPU_QOS_GEN_ID_REVISIONID_USERID_OFFSET 0x0
+#define GPU_QOS_GEN_ID_REVISIONID_USERID_WIDTH 0x8
+#define GPU_QOS_GEN_ID_REVISIONID_USERID_MASK 0xff
+#define GPU_QOS_GEN_ID_REVISIONID_USERID(_reg) (((_reg) & 0xff) >> 0x0)
+#define GPU_QOS_GEN_ID_REVISIONID_FLEXNOCID_OFFSET 0x8
+#define GPU_QOS_GEN_ID_REVISIONID_FLEXNOCID_WIDTH 0x18
+#define GPU_QOS_GEN_ID_REVISIONID_FLEXNOCID_MASK 0xffffff00
+#define GPU_QOS_GEN_ID_REVISIONID_FLEXNOCID(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define GPU_QOS_GEN_PRIORITY 0x8
+#define GPU_QOS_GEN_PRIORITY_P0_OFFSET 0x0
+#define GPU_QOS_GEN_PRIORITY_P0_WIDTH 0x3
+#define GPU_QOS_GEN_PRIORITY_P0_MASK 0x7
+#define GPU_QOS_GEN_PRIORITY_P0(_reg) (((_reg) & 0x7) >> 0x0)
+#define GPU_QOS_GEN_PRIORITY_P1_OFFSET 0x8
+#define GPU_QOS_GEN_PRIORITY_P1_WIDTH 0x3
+#define GPU_QOS_GEN_PRIORITY_P1_MASK 0x700
+#define GPU_QOS_GEN_PRIORITY_P1(_reg) (((_reg) & 0x700) >> 0x8)
+
+#define GPU_QOS_GEN_MODE 0xc
+#define GPU_QOS_GEN_MODE_MODE_OFFSET 0x0
+#define GPU_QOS_GEN_MODE_MODE_WIDTH 0x2
+#define GPU_QOS_GEN_MODE_MODE_MASK 0x3
+#define GPU_QOS_GEN_MODE_MODE(_reg) (((_reg) & 0x3) >> 0x0)
+
+#define GPU_QOS_GEN_BANDWIDTH 0x10
+#define GPU_QOS_GEN_BANDWIDTH_BANDWIDTH_OFFSET 0x0
+#define GPU_QOS_GEN_BANDWIDTH_BANDWIDTH_WIDTH 0xf
+#define GPU_QOS_GEN_BANDWIDTH_BANDWIDTH_MASK 0x7ff
+#define GPU_QOS_GEN_BANDWIDTH_BANDWIDTH(_reg) (((_reg) & 0x7ff) >> 0x0)
+
+#define GPU_QOS_GEN_SATURATION 0x14
+#define GPU_QOS_GEN_SATURATION_SATURATION_OFFSET 0x0
+#define GPU_QOS_GEN_SATURATION_SATURATION_WIDTH 0xa
+#define GPU_QOS_GEN_SATURATION_SATURATION_MASK 0x3ff
+#define GPU_QOS_GEN_SATURATION_SATURATION(_reg) (((_reg) & 0x3ff) >> 0x0)
+
+#define GPU_QOS_GEN_EXTCONTROL 0x18
+#define GPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN_OFFSET 0x0
+#define GPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN_WIDTH 0x1
+#define GPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN_MASK 0x1
+#define GPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN(_reg) (((_reg) & 0x1) >> 0x0)
+#define GPU_QOS_GEN_EXTCONTROL_EXTTHREN_OFFSET 0x1
+#define GPU_QOS_GEN_EXTCONTROL_EXTTHREN_WIDTH 0x1
+#define GPU_QOS_GEN_EXTCONTROL_EXTTHREN_MASK 0x2
+#define GPU_QOS_GEN_EXTCONTROL_EXTTHREN(_reg) (((_reg) & 0x2) >> 0x1)
+#define GPU_QOS_GEN_EXTCONTROL_INTCLKEN_OFFSET 0x2
+#define GPU_QOS_GEN_EXTCONTROL_INTCLKEN_WIDTH 0x1
+#define GPU_QOS_GEN_EXTCONTROL_INTCLKEN_MASK 0x4
+#define GPU_QOS_GEN_EXTCONTROL_INTCLKEN(_reg) (((_reg) & 0x4) >> 0x2)
+#define GPU_QOS_GEN_EXTCONTROL_EXTLIMITEN_OFFSET 0x3
+#define GPU_QOS_GEN_EXTCONTROL_EXTLIMITEN_WIDTH 0x1
+#define GPU_QOS_GEN_EXTCONTROL_EXTLIMITEN_MASK 0x8
+#define GPU_QOS_GEN_EXTCONTROL_EXTLIMITEN(_reg) (((_reg) & 0x8) >> 0x2)
+
+#define VPU_QOS_GEN_ID_COREID(_base) ((_base) + 0x1e00)
+#define VPU_QOS_GEN_ID_COREID_CORETYPEID_OFFSET 0x0
+#define VPU_QOS_GEN_ID_COREID_CORETYPEID_WIDTH 0x8
+#define VPU_QOS_GEN_ID_COREID_CORETYPEID_MASK 0xff
+#define VPU_QOS_GEN_ID_COREID_CORETYPEID(_reg) (((_reg) & 0xff) >> 0x0)
+#define VPU_QOS_GEN_ID_COREID_CORECHECKSUM_OFFSET 0x8
+#define VPU_QOS_GEN_ID_COREID_CORECHECKSUM_WIDTH 0x18
+#define VPU_QOS_GEN_ID_COREID_CORECHECKSUM_MASK 0xffffff00
+#define VPU_QOS_GEN_ID_COREID_CORECHECKSUM(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define VPU_QOS_GEN_ID_REVISIONID(_base) ((_base) + 0x1e04)
+#define VPU_QOS_GEN_ID_REVISIONID_USERID_OFFSET 0x0
+#define VPU_QOS_GEN_ID_REVISIONID_USERID_WIDTH 0x8
+#define VPU_QOS_GEN_ID_REVISIONID_USERID_MASK 0xff
+#define VPU_QOS_GEN_ID_REVISIONID_USERID(_reg) (((_reg) & 0xff) >> 0x0)
+#define VPU_QOS_GEN_ID_REVISIONID_FLEXNOCID_OFFSET 0x8
+#define VPU_QOS_GEN_ID_REVISIONID_FLEXNOCID_WIDTH 0x18
+#define VPU_QOS_GEN_ID_REVISIONID_FLEXNOCID_MASK 0xffffff00
+#define VPU_QOS_GEN_ID_REVISIONID_FLEXNOCID(_reg) (((_reg) & 0xffffff00) >> 0x8)
+
+#define VPU_QOS_GEN_PRIORITY(_base) ((_base) + 0x1e08)
+#define VPU_QOS_GEN_PRIORITY_P0_OFFSET 0x0
+#define VPU_QOS_GEN_PRIORITY_P0_WIDTH 0x2
+#define VPU_QOS_GEN_PRIORITY_P0_MASK 0x3
+#define VPU_QOS_GEN_PRIORITY_P0(_reg) (((_reg) & 0x3) >> 0x0)
+#define VPU_QOS_GEN_PRIORITY_P1_OFFSET 0x2
+#define VPU_QOS_GEN_PRIORITY_P1_WIDTH 0x2
+#define VPU_QOS_GEN_PRIORITY_P1_MASK 0xc
+#define VPU_QOS_GEN_PRIORITY_P1(_reg) (((_reg) & 0xc) >> 0x2)
+
+#define VPU_QOS_GEN_MODE(_base) ((_base) + 0x1e0c)
+#define VPU_QOS_GEN_MODE_MODE_OFFSET 0x0
+#define VPU_QOS_GEN_MODE_MODE_WIDTH 0x2
+#define VPU_QOS_GEN_MODE_MODE_MASK 0x3
+#define VPU_QOS_GEN_MODE_MODE(_reg) (((_reg) & 0x3) >> 0x0)
+
+#define VPU_QOS_GEN_BANDWIDTH(_base) ((_base) + 0x1e10)
+#define VPU_QOS_GEN_BANDWIDTH_BANDWIDTH_OFFSET 0x0
+#define VPU_QOS_GEN_BANDWIDTH_BANDWIDTH_WIDTH 0xb
+#define VPU_QOS_GEN_BANDWIDTH_BANDWIDTH_MASK 0x7ff
+#define VPU_QOS_GEN_BANDWIDTH_BANDWIDTH(_reg) (((_reg) & 0x7ff) >> 0x0)
+
+#define VPU_QOS_GEN_SATURATION(_base) ((_base) + 0x1e14)
+#define VPU_QOS_GEN_SATURATION_SATURATION_OFFSET 0x0
+#define VPU_QOS_GEN_SATURATION_SATURATION_WIDTH 0xa
+#define VPU_QOS_GEN_SATURATION_SATURATION_MASK 0x3ff
+#define VPU_QOS_GEN_SATURATION_SATURATION(_reg) (((_reg) & 0x3ff) >> 0x0)
+
+#define VPU_QOS_GEN_EXTCONTROL(_base) ((_base) + 0x1e18)
+#define VPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN_OFFSET 0x0
+#define VPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN_WIDTH 0x1
+#define VPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN_MASK 0x1
+#define VPU_QOS_GEN_EXTCONTROL_SOCKETQOSEN(_reg) (((_reg) & 0x1) >> 0x0)
+#define VPU_QOS_GEN_EXTCONTROL_EXTTHREN_OFFSET 0x1
+#define VPU_QOS_GEN_EXTCONTROL_EXTTHREN_WIDTH 0x1
+#define VPU_QOS_GEN_EXTCONTROL_EXTTHREN_MASK 0x2
+#define VPU_QOS_GEN_EXTCONTROL_EXTTHREN(_reg) (((_reg) & 0x2) >> 0x1)
+#define VPU_QOS_GEN_EXTCONTROL_INTCLKEN_OFFSET 0x2
+#define VPU_QOS_GEN_EXTCONTROL_INTCLKEN_WIDTH 0x1
+#define VPU_QOS_GEN_EXTCONTROL_INTCLKEN_MASK 0x4
+#define VPU_QOS_GEN_EXTCONTROL_INTCLKEN(_reg) (((_reg) & 0x4) >> 0x2)
+#endif
diff --git a/drivers/interconnect/eswin/noc_stat.c b/drivers/interconnect/eswin/noc_stat.c
new file mode 100644
index 000000000000..a4f6a90f1426
--- /dev/null
+++ b/drivers/interconnect/eswin/noc_stat.c
@@ -0,0 +1,966 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/bitfield.h>
+
+#include "noc.h"
+#include "noc_regs.h"
+
+//#pragma GCC optimize("O0")
+
+/**
+ * Create a new stat_measure from parsed dts entry
+ * @param stat_probe
+ * @return The created win2030_noc_stat_measure object
+ */
+static struct win2030_noc_stat_measure *new_stat_measure(
+ struct win2030_noc_stat_probe *stat_probe)
+{
+ struct win2030_noc_stat_measure *stat_measure;
+ unsigned long flags;
+
+ stat_measure = kzalloc(sizeof(struct win2030_noc_stat_measure),
+ GFP_KERNEL);
+ if (!stat_measure)
+ return ERR_PTR(-ENOMEM);
+ INIT_LIST_HEAD(&stat_measure->link);
+ spin_lock_init(&stat_measure->lock);
+
+ spin_lock_irqsave(&stat_probe->lock, flags);
+ list_add_tail(&stat_measure->link, &stat_probe->measure);
+ spin_unlock_irqrestore(&stat_probe->lock, flags);
+
+ return stat_measure;
+}
+
+/**
+ * Create a new stat_probe. A stat_probe contains all measurements which
+ * are linked to the same probe
+ * @param noc_stat the stat_probe will be attached to noc_stat
+ * @param probe the stat_probe will refer to probe
+ * @return The created xgold_noc_stat_probe object
+ */
+static struct win2030_noc_stat_probe *new_stat_probe(struct win2030_noc_stat
+ *noc_stat,
+ struct win2030_noc_probe
+ *probe)
+{
+ struct win2030_noc_stat_probe *stat_probe;
+ unsigned long flags;
+
+ stat_probe = kzalloc(sizeof(struct win2030_noc_stat_probe), GFP_KERNEL);
+ if (!stat_probe)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&stat_probe->measure);
+ INIT_LIST_HEAD(&stat_probe->link);
+ spin_lock_init(&stat_probe->lock);
+
+ spin_lock_irqsave(&noc_stat->lock, flags);
+ list_add_tail(&stat_probe->link, &noc_stat->probe);
+ spin_unlock_irqrestore(&noc_stat->lock, flags);
+ stat_probe->probe = probe;
+
+ return stat_probe;
+}
+
+enum {
+ FIELD_ID_TRACE_PORT = 0,
+ FIELD_ID_INIT_FLOW,
+ FIELD_ID_TARGET_FLOW,
+ FIELD_ID_ADDR_BASE,
+ FIELD_ID_ADDR_SIZE,
+ FIELD_ID_OPCODE,
+ FIELD_ID_STATUS,
+ FIELD_ID_LENGTH,
+ FIELD_ID_URGENCY,
+ FIELD_ID_MAX,
+};
+
+static int win2030_noc_stat_parse_traceport_field(struct win2030_noc_probe *probe,
+ struct device *dev, const char *str, int field_id,
+ struct win2030_noc_stat_traceport_data *data)
+{
+ int i;
+ u64 vaule;
+ int ret;
+ struct win2030_register *reg;
+ struct win2030_bitfield *bf = NULL;
+ const char *str_tmp;
+ bool found = false;
+
+ switch (field_id) {
+ case FIELD_ID_INIT_FLOW:
+ str_tmp = &str[strlen("InitFlow:")];
+ reg = probe->filters[0].route_id_base;
+ found = false;
+ list_for_each_entry(bf, &reg->bitfields, link) {
+ if (strcmp(bf->name, "InitFlow") == 0) {
+ i = 0;
+ while (bf->lut[i] != NULL) {
+ if (str_tmp && strcmp(str_tmp, bf->lut[i]) == 0) {
+ data->init_flow_name = kstrdup(str_tmp, GFP_KERNEL);
+ data->init_flow_idx = i;
+ data->init_flow_mask = bf->mask;
+ data->init_flow_offset = bf->offset;
+ found = true;
+ break;
+ }
+ i += 1;
+ }
+ }
+ }
+ if (false == found) {
+ dev_err(dev, "get unknown InitFlow data %s when parsing traceport dts!\n", str);
+ return -EINVAL;
+ }
+ break;
+ case FIELD_ID_TARGET_FLOW:
+ str_tmp = &str[strlen("TargetFlow:")];
+ reg = probe->filters[0].route_id_base;
+ found = false;
+ list_for_each_entry(bf, &reg->bitfields, link) {
+ if (strcmp(bf->name, "TargetFlow") == 0) {
+ i = 0;
+ while (bf->lut[i] != NULL) {
+ if (str_tmp && strcmp(str_tmp, bf->lut[i]) == 0) {
+ data->target_flow_name = kstrdup(str_tmp, GFP_KERNEL);
+ data->target_flow_idx = i;
+ data->target_flow_mask = bf->mask;
+ data->target_flow_offset = bf->offset;
+ found = true;
+ break;
+ }
+ i += 1;
+ }
+ }
+ }
+ if (false == found) {
+ dev_err(dev, "get unknown TargetFlow data %s when parsing traceport dts!\n", str);
+ return -EINVAL;
+ }
+ break;
+ case FIELD_ID_ADDR_BASE:
+ ret = kstrtou64(&str[strlen("AddrBase:")], 0, &vaule);
+ if (ret) {
+ dev_err(dev, "get unknown AddrBase data %s when parsing traceport dts!\n", str);
+ return ret;
+ }
+ data->addrBase_low = FIELD_GET(GENMASK(31, 0), vaule);
+ data->addrBase_high = FIELD_GET(GENMASK(40, 32), vaule);
+ break;
+ case FIELD_ID_ADDR_SIZE:
+ ret = kstrtou64(&str[strlen("AddrSize:")], 0, &vaule);
+ if (ret) {
+ dev_err(dev, "get unknown AddrSize data %s when parsing traceport dts!\n", str);
+ return ret;
+ }
+ data->addrWindowSize = vaule;
+ /*should not exceed the max vaule*/
+ data->addrWindowSize &= PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_MASK;
+ break;
+ case FIELD_ID_OPCODE:
+ if (0 == strcmp(str, "Opcode:RdWrLockUrg")) {
+ data->Opcode = 0xf;
+ } else if (0 == strcmp(str, "Opcode:WrLockUrg")) {
+ data->Opcode = 0xe;
+ } else if (0 == strcmp(str, "Opcode:RdLockUrg")) {
+ data->Opcode = 0xd;
+ } else if (0 == strcmp(str, "Opcode:LockUrg")) {
+ data->Opcode = 0xc;
+ } else if (0 == strcmp(str, "Opcode:RdWrUrg")) {
+ data->Opcode = 0xb;
+ } else if (0 == strcmp(str, "Opcode:WrUrg")) {
+ data->Opcode = 0xa;
+ } else if (0 == strcmp(str, "Opcode:RdUrg")) {
+ data->Opcode = 0x9;
+ } else if (0 == strcmp(str, "Opcode:Urg")) {
+ data->Opcode = 0x8;
+ } else if (0 == strcmp(str, "Opcode:RdWrLock")) {
+ data->Opcode = 0x7;
+ } else if (0 == strcmp(str, "Opcode:WrLock")) {
+ data->Opcode = 0x6;
+ } else if (0 == strcmp(str, "Opcode:RdLock")) {
+ data->Opcode = 0x5;
+ } else if (0 == strcmp(str, "Opcode:Lock")) {
+ data->Opcode = 0x4;
+ } else if (0 == strcmp(str, "Opcode:RdWr")) {
+ data->Opcode = 0x3;
+ } else if (0 == strcmp(str, "Opcode:Wr")) {
+ data->Opcode = 0x2;
+ } else if (0 == strcmp(str, "Opcode:Rd")) {
+ data->Opcode = 0x1;
+ } else {
+ dev_err(dev, "get unknown Opcode data %s when parsing traceport dts!\n", str);
+ return -EINVAL;
+ }
+ break;
+
+ case FIELD_ID_STATUS:
+ if (0 == strcmp(str, "Status:ReqRsp")) {
+ data->Status = 3;
+ } else if (0 == strcmp(str, "Status:Req")) {
+ data->Status = 1;
+ } else if (0 == strcmp(str, "Status:Rsp")) {
+ data->Status = 2;
+ } else {
+ dev_err(dev, "get unknown Status data %s when parsing traceport dts!\n", str);
+ return -EINVAL;
+ }
+ break;
+
+ case FIELD_ID_LENGTH:
+ ret = kstrtou64(&str[strlen("Length:")], 0, &vaule);
+ if (ret) {
+ dev_err(dev, "get unknown length data %s when parsing traceport dts!\n", str);
+ return ret;
+ }
+ data->Length = ilog2(vaule);
+ data->Length &= PROBE_FILTERS_LENGTH_FILTERS_LENGTH_MASK;
+ break;
+
+ case FIELD_ID_URGENCY:
+ ret = kstrtou64(&str[strlen("Urgency:")], 0, &vaule);
+ if (ret) {
+ dev_err(dev, "unknown length data %s when parsing traceport dts!\n", str);
+ return ret;
+ }
+ data->Urgency = vaule;
+ data->Urgency &= PROBE_FILTERS_URGENCY_FILTERS_URGENCY_MASK;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static int win2030_noc_stat_parse_traceport_dts(struct win2030_noc_probe **probe_out,
+ struct win2030_noc_device *noc_device, struct device_node *np,
+ const char *name, struct win2030_noc_stat_traceport_data *data)
+{
+ struct property *prop;
+ struct device *dev = noc_device->dev;
+ int lg;
+ int index = 0;
+ const char *str[FIELD_ID_MAX] = {NULL}, *s;
+ int ret;
+ const char *field[] = {"TracePort:", "InitFlow:", "TargetFlow:",
+ "AddrBase:", "AddrSize:", "Opcode:", "Status:", "Length:", "Urgency:"};
+ int i, j;
+ bool found = false;
+ struct win2030_noc_probe *probe = NULL;
+ const char *str_tmp;
+
+ /* Count number of strings per measurement
+ (normally 1: TracePortSel)
+ */
+ lg = of_property_count_strings(np, name);
+ if (lg > FIELD_ID_MAX || lg < 1)
+ return -EINVAL; /* Invalid data, let's skip this measurement */
+
+ of_property_for_each_string(np, name, prop, s)
+ str[index++] = s;
+
+ str_tmp = &str[0][strlen("TracePort:")];
+ list_for_each_entry(probe, &noc_device->probes, link) {
+ for (j = 0; probe->available_portsel[j] != NULL; j++) {
+ if (str_tmp && strcmp(str_tmp, probe->available_portsel[j]) == 0)
+ break;
+ }
+ if (probe->available_portsel[j] != NULL) {
+ found = true;
+ break;
+ }
+ }
+ if (false == found)
+ return -EINVAL; /* Invalid data, let's skip this measurement. */
+
+ *probe_out = probe;
+ data->name = str_tmp;
+ data->idx_trace_port_sel = j;
+
+ /*if user not set, default set as below
+ route_id_mask: -1, which means all flow
+ target_flow_idx: -1, which means all flow
+ addrBase: 0, which means all address
+ addrWindowSize : 0, which means all range
+ Opcode: 0xf, which means enable Rd,Wr,Lock,Urg
+ Status:0x3,which means Req,Rsp
+ Length:0xf, which means the max packets length could be 0x10000
+ Urgency:0x0, which means all urgency level be selected
+ */
+ data->init_flow_name = NULL;
+ data->init_flow_idx = -1;
+ data->init_flow_offset = -1;
+ data->init_flow_mask = -1;
+ data->target_flow_name = NULL;
+ data->target_flow_idx = -1;
+ data->target_flow_offset = -1;
+ data->target_flow_mask = -1;
+ data->addrBase_low = 0;
+ data->addrBase_high = 0;
+ data->addrWindowSize = 0x3f;
+ data->Opcode = 0xf;
+ data->Status = 0x3;
+ data->Length = 0xF;
+ data->Urgency = 0x0;
+
+ for (i = 1; i < lg; i++) {
+ for (j = 0; j < ARRAY_SIZE(field); j++) {
+ if (0 == strncmp(str[i], field[j], strlen(field[j]))) {
+ ret = win2030_noc_stat_parse_traceport_field(probe, dev, str[i], j, data);
+ if (ret) {
+ dev_err(dev, "error when parsing traceport dts, ret %d!\n", ret);
+ return ret;
+ }
+ }
+ }
+ }
+
+ dev_dbg(dev, "get traceport data, name: %s\n"
+ "\t\t\t\t init_flow: %s init_flow_idx: 0x%x, init_flow_offset: 0x%x, init_flow_mask: 0x%x\n"
+ "\t\t\t\t target_flow: %s, target_flow_idx: 0x%x, target_flow_offset: 0x%x, target_flow_mask: 0x%x\n"
+ "\t\t\t\t addrBase_low: 0x%x, addrBase_high: 0x%x, addrWindowSize: 0x%x\n"
+ "\t\t\t\t Opcode: 0x%x, Status: 0x%x, Length: 0x%llx, Urgency: 0x%x!\n",
+ data->name,
+ data->init_flow_name == NULL ? "not set" : data->init_flow_name, data->init_flow_idx, data->init_flow_offset,data->init_flow_mask,
+ data->target_flow_name == NULL ? "not set" : data->target_flow_name, data->target_flow_idx, data->target_flow_offset, data->target_flow_mask,
+ data->addrBase_low, data->addrBase_high, data->addrWindowSize, data->Opcode, data->Status,
+ int_pow(2, data->Length), data->Urgency);
+
+ return 0;
+}
+
+/**
+ * From one line of dts, add a new stat_measure and a stat_probe (if needed)
+ * @param noc_device A ref to the device
+ * @param np A ref to the dts node
+ * @param name the line of the dts
+ * @return
+ */
+static int new_probe_and_measure(struct win2030_noc_device *noc_device,
+ struct device_node *np, const char *name)
+{
+ struct win2030_noc_stat_probe *stat_probe, *entry = NULL;
+ struct win2030_noc_stat *noc_stat = noc_device->stat;
+ struct win2030_noc_probe *probe = NULL;
+ struct win2030_noc_stat_measure *stat_measure;
+ struct win2030_noc_stat_traceport_data data;
+ bool found = false;
+ int ret;
+
+ ret = win2030_noc_stat_parse_traceport_dts(&probe, noc_device, np, name, &data);
+ if (ret) {
+ return ret;
+ }
+
+ /* Look if the probe was already existing in noc_stat */
+ if (!list_empty(&noc_stat->probe)) {
+ found = false;
+ list_for_each_entry(entry, &noc_stat->probe, link) {
+ if (entry->probe == probe) {
+ /* It is found, create a new measure */
+ stat_measure = new_stat_measure(entry);
+ if (IS_ERR(stat_measure))
+ goto error;
+ found = true;
+ break;
+ }
+ }
+ if (found == false) {
+ /* Not found, we must create a stat_probe */
+ stat_probe = new_stat_probe(noc_stat, probe);
+ if (IS_ERR(stat_probe))
+ goto error;
+ /* Then add the measurement */
+ stat_measure = new_stat_measure(stat_probe);
+ if (IS_ERR(stat_measure))
+ goto free_stat_probe;
+ }
+ } else {
+ /* No stat_probe created up to now, let's create one. */
+ stat_probe = new_stat_probe(noc_stat, probe);
+ if (IS_ERR(stat_probe))
+ goto error;
+ /* Then add the measurement */
+ stat_measure = new_stat_measure(stat_probe);
+ if (IS_ERR(stat_measure))
+ goto free_stat_probe;
+ }
+ memcpy(&stat_measure->data, &data, sizeof(struct win2030_noc_stat_traceport_data));
+ return 0;
+
+free_stat_probe:
+ kfree(stat_probe);
+error:
+ return -ENOMEM;
+}
+
+/**
+ * Return the value of a bitfield
+ * @param bf the bitfield to read
+ * @return the value of the bitfield
+ */
+static unsigned win2030_noc_bf_read(struct win2030_bitfield *bf)
+{
+ struct win2030_register *reg;
+ unsigned bf_value;
+
+ reg = bf->parent;
+ bf_value = ioread32(WIN2030_NOC_REG_ADDR(reg));
+ bf_value &= bf->mask;
+ bf_value >>= bf->offset;
+
+ return bf_value;
+}
+
+/**
+ * Write some bits in a register
+ * @param reg The register to write
+ * @param mask The mask for the bits (shifted)
+ * @param offset The offset of the bits
+ * @param value The value to write
+ * @return 0 if ok, -EINVAL if value is not valid
+ */
+int win2030_noc_reg_write(struct win2030_register *reg, unsigned mask,
+ unsigned offset, unsigned value)
+{
+ unsigned reg_value;
+ unsigned long flags;
+
+ if (value > (mask >> offset)) {
+ dev_err(reg->parent, "reg_write, error para, name %s, value 0x%x, mask 0x%x, offset 0x%x!\n",
+ reg->name, value, mask, offset);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&reg->hw_lock, flags);
+ reg_value = ioread32(WIN2030_NOC_REG_ADDR(reg));
+ reg_value &= ~mask;
+ reg_value |= (value << offset);
+ iowrite32(reg_value, WIN2030_NOC_REG_ADDR(reg));
+ spin_unlock_irqrestore(&reg->hw_lock, flags);
+
+ return 0;
+}
+
+/**
+ * Read some bits in a register
+ * @param reg The register to read
+ * @param mask The mask for the bits (shifted)
+ * @param offset The offset of the bits
+ * @return the read value
+ */
+unsigned win2030_noc_reg_read(struct win2030_register *reg, unsigned mask,
+ unsigned offset)
+{
+ unsigned reg_value;
+
+ reg_value = ioread32(WIN2030_NOC_REG_ADDR(reg));
+ reg_value &= ~mask;
+ reg_value >>= offset;
+
+ return reg_value;
+}
+
+int win2030_noc_stat_packet_probe_launch_measure(
+ struct win2030_noc_device *noc_device,
+ struct win2030_noc_stat_probe *stat_probe)
+{
+ struct win2030_noc_stat_measure *stat_measure;
+ struct win2030_noc_probe *probe;
+ int i, ret = 0;
+ struct win2030_noc_stat_traceport_data *data;
+
+ stat_measure = list_first_entry(&stat_probe->measure,
+ struct win2030_noc_stat_measure, link);
+ data = &stat_measure->data;
+
+ probe = stat_probe->probe;
+
+ /* Set Filters */
+ for (i = 0; i < probe->nr_filters; i++) {
+ /*Address*/
+ ret |=
+ win2030_noc_reg_write(probe->filters[i].addr_base_low->parent,
+ PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_MASK,
+ PROBE_FILTERS_ADDRBASE_LOW_FILTERS_ADDRBASE_LOW_OFFSET,
+ data->addrBase_low);
+
+ ret |=
+ win2030_noc_reg_write(probe->filters[i].addr_base_high->parent,
+ PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_MASK,
+ PROBE_FILTERS_ADDRBASE_HIGH_FILTERS_ADDRBASE_HIGH_OFFSET,
+ data->addrBase_high);
+
+ /*Windowsize*/
+ ret |=
+ win2030_noc_reg_write(probe->filters[i].window_size->parent,
+ PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_MASK,
+ PROBE_FILTERS_WINDOWSIZE_FILTERS_WINDOWSIZE_OFFSET,
+ data->addrWindowSize);
+
+ /*opcode*/
+ ret |=
+ win2030_noc_reg_write(probe->filters[i].op_code, 0xf, 0, data->Opcode);
+
+ /*status*/
+ ret |=
+ win2030_noc_reg_write(probe->filters[i].status, 0x3, 0, data->Status);
+
+ /*length*/
+ ret |=
+ win2030_noc_reg_write(probe->filters[i].length->parent,
+ PROBE_FILTERS_LENGTH_FILTERS_LENGTH_MASK,
+ PROBE_FILTERS_LENGTH_FILTERS_LENGTH_OFFSET,
+ data->Length);
+
+ /*urgency*/
+ ret |=
+ win2030_noc_reg_write(probe->filters[i].urgency->parent,
+ PROBE_FILTERS_URGENCY_FILTERS_URGENCY_MASK,
+ PROBE_FILTERS_URGENCY_FILTERS_URGENCY_OFFSET,
+ data->Urgency);
+ }
+
+ if (data->init_flow_idx != 0xff) {
+ ret |= win2030_noc_reg_write(probe->filters[0].route_id_mask->parent,
+ data->init_flow_mask, data->init_flow_offset,
+ (data->init_flow_mask >> data->init_flow_offset));
+
+ ret |= win2030_noc_reg_write(probe->filters[0].route_id_base,
+ data->init_flow_mask, data->init_flow_offset,
+ data->init_flow_idx);
+ }
+ if (data->target_flow_idx != 0xff) {
+ ret |= win2030_noc_reg_write(probe->filters[0].route_id_mask->parent,
+ data->target_flow_mask, data->target_flow_offset,
+ (data->target_flow_mask >> data->target_flow_offset));
+
+ ret |= win2030_noc_reg_write(probe->filters[0].route_id_base,
+ data->target_flow_mask, data->target_flow_offset,
+ data->target_flow_idx);
+ }
+ //if (data->init_flow_idx != 0xff || data->target_flow_idx != 0xff) {
+ /* Enable Filter LUT */
+ /* To select only filter 0 */
+ ret |= win2030_noc_reg_write(probe->filter_lut,
+ PROBE_FILTERLUT_FILTERLUT_MASK,
+ PROBE_FILTERLUT_FILTERLUT_OFFSET,
+ 0x1);
+ //}
+ /* Enable Alarm and statistics */
+ ret |= win2030_noc_reg_write(probe->main_ctl, PROBE_MAINCTL_STATEN_MASK,
+ PROBE_MAINCTL_STATEN_OFFSET, 1);
+
+ ret |= win2030_noc_reg_write(probe->main_ctl, PROBE_MAINCTL_ALARMEN_MASK,
+ PROBE_MAINCTL_ALARMEN_OFFSET, 1);
+
+ /* Set period to max value */
+ ret |= win2030_noc_reg_write(probe->stat_period,
+ PROBE_STATPERIOD_STATPERIOD_MASK,
+ PROBE_STATPERIOD_STATPERIOD_OFFSET,
+ DURATION);
+
+ /* Select sources for counter0 and 1 */
+ //if (data->init_flow_idx != 0xff || data->target_flow_idx != 0xff) {
+ ret |= win2030_noc_reg_write(probe->main_ctl,
+ PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_MASK,
+ PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_OFFSET,
+ 1);
+
+ ret |=
+ win2030_noc_reg_write(probe->counters[0].source_event->parent,
+ PROBE_COUNTERS_SRC_INTEVENT_MASK,
+ /*FILT_BYTE = 0x14 */
+ PROBE_COUNTERS_SRC_INTEVENT_OFFSET,
+ 0x14);
+ ret |=
+ win2030_noc_reg_write(probe->counters[1].source_event->parent,
+ PROBE_COUNTERS_SRC_INTEVENT_MASK,
+ /* chain */
+ PROBE_COUNTERS_SRC_INTEVENT_OFFSET,
+ 0x10);
+ #if 0
+ } else {
+ ret |= win2030_noc_reg_write(probe->main_ctl,
+ PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_MASK,
+ PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_OFFSET,
+ 0);
+ ret |=
+ win2030_noc_reg_write(probe->counters[0].source_event->parent,
+ PROBE_COUNTERS_SRC_INTEVENT_MASK,
+ /* BYTE */
+ PROBE_COUNTERS_SRC_INTEVENT_OFFSET,
+ 0x8);
+ ret |=
+ win2030_noc_reg_write(probe->counters[1].source_event->parent,
+ PROBE_COUNTERS_SRC_INTEVENT_MASK,
+ /* chain */
+ PROBE_COUNTERS_SRC_INTEVENT_OFFSET,
+ 0x10);
+ }
+ #endif
+ /* Set alarm mode */
+ /* min */
+ ret |= win2030_noc_reg_write(probe->counters[0].alarm_mode->parent,
+ PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_MASK,
+ PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_OFFSET,
+ 1);
+ return ret;
+}
+
+int win2030_noc_stat_tranc_probe_launch_measure(
+ struct win2030_noc_device *noc_device,
+ struct win2030_noc_stat_probe *stat_probe)
+{
+ struct win2030_noc_stat_measure *stat_measure;
+ struct win2030_noc_probe *probe;
+ int i, ret = 0;
+
+ stat_measure = list_first_entry(&stat_probe->measure,
+ struct win2030_noc_stat_measure, link);
+
+ probe = stat_probe->probe;
+
+ /* Reset Filters */
+ for (i = 0; i < probe->nr_filters; i++) {
+ /*select all kinds opcode*/
+ ret |=
+ win2030_noc_reg_write(probe->trans_filters[i].op_code, 0xf, 0, 0xf);
+ }
+
+ /* Enable Alarm and statistics */
+ ret |= win2030_noc_reg_write(probe->main_ctl, PROBE_MAINCTL_STATEN_MASK,
+ PROBE_MAINCTL_STATEN_OFFSET, 1);
+
+ ret |= win2030_noc_reg_write(probe->main_ctl, PROBE_MAINCTL_ALARMEN_MASK,
+ PROBE_MAINCTL_ALARMEN_OFFSET, 1);
+
+ /* Set period to max value */
+ ret |= win2030_noc_reg_write(probe->stat_period,
+ PROBE_STATPERIOD_STATPERIOD_MASK,
+ PROBE_STATPERIOD_STATPERIOD_OFFSET,
+ DURATION);
+
+ /* map counters to trace port*/
+ ret |= win2030_noc_reg_write(probe->main_ctl,
+ PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_MASK,
+ PROBE_MAINCTL_FILT_BYTE_ALWAYS_CAHINABLE_EN_OFFSET,
+ 1);
+
+ for (i = 0; i < probe->nr_portsel; i++) {
+ ret |=
+ win2030_noc_reg_write(probe->counters[i * 2].source_event->parent,
+ PROBE_COUNTERS_SRC_INTEVENT_MASK,
+ /* BYTE */
+ PROBE_COUNTERS_SRC_INTEVENT_OFFSET,
+ 0x8);
+ /* Set alarm mode min */
+ ret |=
+ win2030_noc_reg_write(probe->counters[i * 2].alarm_mode->parent,
+ PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_MASK,
+ PROBE_COUNTERS_ALARMMODE_COUNTERS_ALARMMODE_OFFSET,
+ 1);
+ ret |=
+ win2030_noc_reg_write(probe->counters[i * 2 + 1].source_event->parent,
+ PROBE_COUNTERS_SRC_INTEVENT_MASK,
+ /* chain */
+ PROBE_COUNTERS_SRC_INTEVENT_OFFSET,
+ 0x10);
+ }
+ return ret;
+}
+
+/**
+ * Configure NOC registers from a stat_measure
+ * @param noc_device A reference to the device
+ * @param stat_probe The stat_probe to find the measure
+ * @return 0 if ok, -EINVAL otherwise
+ */
+
+static int win2030_noc_stat_launch_measure(struct win2030_noc_device *noc_device,
+ struct win2030_noc_stat_probe *stat_probe)
+{
+ struct win2030_noc_stat_measure *stat_measure;
+ struct win2030_noc_probe *probe;
+ int ret = 0;
+
+ stat_measure = list_first_entry(&stat_probe->measure,
+ struct win2030_noc_stat_measure, link);
+
+ dev_dbg_once(noc_device->dev, "stat measurment %s begin\n", stat_measure->data.name);
+ probe = stat_probe->probe;
+ /* Disable NOC */
+ ret = win2030_noc_reg_write(probe->cfg_ctl, PROBE_CFGCTL_GLOBALEN_MASK,
+ PROBE_CFGCTL_GLOBALEN_OFFSET, 0);
+ while (win2030_noc_reg_read(probe->cfg_ctl, PROBE_CFGCTL_ACTIVE_WIDTH,
+ PROBE_CFGCTL_ACTIVE_OFFSET) != 0)
+ ;
+#if 0
+ /*there is only one probe point, so there is no need to choose probe point*/
+ if (stat_measure->idx_init_flow != 0xff) {
+ /* Select probe TracePortSel if need filter*/
+ ret |= win2030_noc_reg_write(probe->trace_port_sel,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_MASK,
+ PROBE_TRACEPORTSEL_TRACEPORTSEL_OFFSET,
+ stat_measure->idx_trace_port_sel);
+ } else {
+ /* Select counter PortSel if no need filter*/
+ ret |= win2030_noc_reg_write(probe->counters[0].portsel->parent,
+ PROBE_COUNTERS_PORTSEL_COUNTERS_PORTSEL_MASK,
+ PROBE_COUNTERS_PORTSEL_COUNTERS_PORTSEL_OFFSET,
+ stat_measure->idx_trace_port_sel);
+ }
+#endif
+ if (probe_t_pkt == probe->type) {
+ ret |= win2030_noc_stat_packet_probe_launch_measure(noc_device, stat_probe);
+ } else if (probe_t_trans == probe->type) {
+ ret |= win2030_noc_stat_tranc_probe_launch_measure(noc_device, stat_probe);
+ }
+
+ /* Set alarm min value */
+ ret |= win2030_noc_reg_write(probe->stat_alarm_min,
+ PROBE_STATALARMMIN_STATALARMMIN_MASK,
+ PROBE_STATALARMMIN_STATALARMMIN_OFFSET,
+ 0xfffffff);
+
+ ret |= win2030_noc_reg_write(probe->stat_alarm_min_high,
+ PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_MASK,
+ PROBE_STATALARMMIN_HIGH_STATALARMMIN_HIGH_OFFSET,
+ 0xfffffff);
+
+ /* Enable */
+ ret |= win2030_noc_reg_write(probe->stat_alarm_en,
+ PROBE_STATALARMEN_STATALARMEN_MASK,
+ PROBE_STATALARMEN_STATALARMEN_OFFSET,
+ 1);
+
+ ret |= win2030_noc_reg_write(probe->cfg_ctl,
+ PROBE_CFGCTL_GLOBALEN_MASK,
+ PROBE_CFGCTL_GLOBALEN_OFFSET,
+ 1);
+ return ret;
+}
+
+void win2030_noc_stat_reset_statistics(struct device *_dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct win2030_noc_stat_probe *stat_probe = NULL;
+ struct win2030_noc_stat_measure *stat_measure = NULL;
+ unsigned long flags;
+ int j;
+ struct win2030_noc_probe *probe;
+
+ list_for_each_entry(stat_probe, &noc_device->stat->probe, link) {
+ probe = stat_probe->probe;
+ list_for_each_entry(stat_measure, &stat_probe->measure, link) {
+ for (j = 0; j < probe->nr_portsel; j++) {
+ spin_lock_irqsave(&stat_measure->lock, flags);
+ stat_measure->min[j] = -1;
+ stat_measure->max[j] = 0;
+ stat_measure->now[j] = 0;
+ stat_measure->iteration[j] = 0;
+ stat_measure->mean[j] = 0;
+ spin_unlock_irqrestore(&stat_measure->lock,
+ flags);
+ }
+
+ }
+ }
+}
+
+/**
+ * Program all noc registers for all probes
+ * @param _dev A reference to the device
+ * @return 0
+ */
+int win2030_noc_stat_trigger(struct device *_dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct win2030_noc_stat_probe *stat_probe = NULL;
+ struct win2030_noc_stat *noc_stat = noc_device->stat;
+ struct list_head *ptr = NULL;
+ unsigned ret;
+
+ /* Loop over all probes */
+ list_for_each(ptr, &noc_stat->probe) {
+ stat_probe = list_entry(ptr, struct win2030_noc_stat_probe, link);
+ /* Program first measurement */
+ ret = win2030_noc_stat_launch_measure(noc_device, stat_probe);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * Entry point for the NOC sniffer
+ * @param _dev A reference to the device
+ * @return 0 if ok, if error returns 0 but print an error message
+ */
+int win2030_noc_stat_init(struct device *_dev)
+{
+ struct win2030_noc_stat *noc_stat;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct device_node *np = _dev->of_node;
+ int stat_idx = 0;
+ bool ret;
+ char mystr[32];
+ int err;
+
+ /* Create a stat device. */
+ noc_stat = kzalloc(sizeof(struct win2030_noc_stat), GFP_KERNEL);
+ if (!noc_stat)
+ return -ENOMEM;
+ noc_device->stat = noc_stat;
+ noc_stat->run = false;
+ spin_lock_init(&noc_stat->lock);
+ INIT_LIST_HEAD(&noc_stat->probe);
+
+ /* Parse dts and build matrix node */
+ do {
+ scnprintf(mystr, ARRAY_SIZE(mystr), "stat,%d", stat_idx);
+ ret = of_property_read_bool(np, mystr);
+ if (ret == true) {
+ stat_idx++;
+ err = new_probe_and_measure(noc_device, np, mystr);
+ if (err)
+ dev_err(_dev,
+ "Error when adding measure: %s!\n",
+ mystr);
+ }
+ } while (ret == true);
+
+ /* If no stat line in dts then we can release the noc_stat object */
+ if (stat_idx == 0)
+ goto no_sniffer;
+
+ ret = of_property_read_u32(np, "clock,rate", &noc_stat->clock_rate);
+ if (ret) {
+ dev_err(_dev,
+ "\"clock,rate\" property missing. Will use clk_get_rate() "
+ "to discover clock rate.");
+ }
+
+ /* Trigger measurement */
+ if (noc_stat->run) {
+ ret = win2030_noc_stat_trigger(_dev);
+ if (ret)
+ dev_err(_dev,
+ "Error when trigger stat measures!\n");
+ }
+ return 0;
+
+no_sniffer:
+ kfree(noc_stat);
+ return -EINVAL;
+}
+
+/**
+ * Interrupt processing when a measurement is done
+ * @param noc_device A reference to the device
+ * @param probe The probe for which a measurement is done
+ */
+void win2030_noc_stat_do_measure(struct win2030_noc_probe *probe)
+{
+ struct win2030_noc_device *noc_device = probe->parent;
+ struct win2030_noc_stat_probe *stat_probe = NULL;
+ struct win2030_noc_stat_measure *stat_measure;
+ u64 counter = 0;
+ unsigned ret;
+ unsigned long flags;
+ int j;
+
+ /* Find which stat_probe is linked to the probe
+ * which generated the interrupt
+ */
+ list_for_each_entry(stat_probe, &noc_device->stat->probe, link) {
+ if (stat_probe->probe == probe)
+ break;
+ }
+
+ /* Take measure object */
+ stat_measure = list_first_entry(&stat_probe->measure,
+ struct win2030_noc_stat_measure, link);
+
+ for (j = 0; j < probe->nr_portsel; j++) {
+ /* Read HW counters */
+ counter = win2030_noc_bf_read(probe->counters[j * 2 + 1].value);
+ counter = ((counter << 32)
+ | (win2030_noc_bf_read(probe->counters[j * 2].value)));
+
+ /* Update stat_measure statistics */
+ if (counter > stat_measure->max[j * 2]) {
+ spin_lock_irqsave(&stat_measure->lock, flags);
+ stat_measure->max[j * 2] = counter;
+ spin_unlock_irqrestore(&stat_measure->lock, flags);
+ }
+ if (counter < stat_measure->min[j * 2]) {
+ spin_lock_irqsave(&stat_measure->lock, flags);
+ stat_measure->min[j * 2] = counter;
+ spin_unlock_irqrestore(&stat_measure->lock, flags);
+ }
+ stat_measure->now[j * 2] = counter;
+ /*if no traffic, don't update iteration and mean value*/
+ if (0 == counter) {
+ continue;
+ }
+ if (stat_measure->iteration[j * 2] != MAX_ITERATION) {
+ spin_lock_irqsave(&stat_measure->lock, flags);
+ stat_measure->iteration[j * 2] += 1;
+ spin_unlock_irqrestore(&stat_measure->lock, flags);
+
+ spin_lock_irqsave(&stat_measure->lock, flags);
+ stat_measure->mean[j * 2] += counter;
+ spin_unlock_irqrestore(&stat_measure->lock, flags);
+ } else {
+ dev_info(noc_device->dev, "TracePort %s iteration overflow, reset measure statistics!\n",
+ stat_probe->probe->available_portsel[j]);
+ spin_lock_irqsave(&stat_measure->lock, flags);
+ stat_measure->iteration[j * 2] = 1;
+ stat_measure->mean[j * 2] = 0;
+ spin_unlock_irqrestore(&stat_measure->lock, flags);
+ }
+ }
+ /* Program next measure */
+ if (noc_device->stat->run == true) {
+ /* Move to the next measure */
+ spin_lock_irqsave(&stat_probe->lock, flags);
+ list_move(&stat_measure->link, &stat_probe->measure);
+ spin_unlock_irqrestore(&stat_probe->lock, flags);
+
+ /* Program it */
+ ret = win2030_noc_stat_launch_measure(noc_device, stat_probe);
+ if (ret)
+ dev_err(noc_device->dev,
+ "Error when launch stat measures!\n");
+ }
+}
diff --git a/drivers/interconnect/eswin/noc_stat_sysfs.c b/drivers/interconnect/eswin/noc_stat_sysfs.c
new file mode 100644
index 000000000000..891e627dc2bf
--- /dev/null
+++ b/drivers/interconnect/eswin/noc_stat_sysfs.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <asm/div64.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include "noc.h"
+
+/**
+ * Called when writing run file
+ */
+static ssize_t noc_stat_run_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct win2030_noc_device *noc_device = filp->private_data;
+ char buf[SIZE_SMALL_BUF];
+ unsigned ret;
+
+ if (cnt > SIZE_SMALL_BUF)
+ cnt = SIZE_SMALL_BUF - 1;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ if (buf[0] == '0') {
+ noc_device->stat->run = false; /* FIXME spinlock? */
+ /*wait for the latest measure to be finished*/
+ msleep(5);
+ win2030_noc_stat_reset_statistics(noc_device->dev);
+ } else if (buf[0] == '1') {
+ if (true != noc_device->stat->run) {
+ noc_device->stat->run = true;
+ win2030_noc_stat_reset_statistics(noc_device->dev);
+ ret = win2030_noc_stat_trigger(noc_device->dev);
+ if (ret)
+ dev_err(noc_device->dev, "Error when trigger stat measures!\n");
+ }
+ } else
+ return -EFAULT;
+
+ *ppos += cnt;
+
+ return cnt;
+}
+
+/**
+ * Called when reading run file
+ */
+static ssize_t noc_stat_run_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+#define RUN_STR_SIZE 11
+ struct win2030_noc_device *noc_device = filp->private_data;
+ char buf[RUN_STR_SIZE];
+ int r;
+
+ r = snprintf(buf, RUN_STR_SIZE, "%i\n", noc_device->stat->run);
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+/**
+ * Called when writing config file
+ */
+static ssize_t noc_stat_config_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ return 0;
+}
+
+/**
+ * Called when reading config file
+ */
+static ssize_t noc_stat_config_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ return 0;
+}
+
+/**
+ * Called when reading readme file
+ * @param m
+ * @param p
+ * @return
+ */
+static int noc_stat_readme_show(struct seq_file *m, void *p)
+{
+ seq_puts(m,
+ "How to use the NOC sniffer:\nFile run: write '1' to enable "
+ "sniffer (enabling sniffer resets statistics),"
+ " write '0' to disable sniffer.\n"
+ "File config: Not yet implemented.\n"
+ "File results: contain the results of measurement in KB/s "
+ "for all nodes defined in dts.\n");
+ return 0;
+}
+
+/**
+ * Called when reading results file.
+ */
+static ssize_t noc_stat_results_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct win2030_noc_device *noc_device = filp->private_data;
+ struct win2030_noc_probe *probe;
+ struct win2030_noc_stat *stat;
+ struct win2030_noc_stat_probe *stat_probe = NULL;
+ struct win2030_noc_stat_measure *stat_measure = NULL;
+ u64 meas_min, meas_max, meas_mean, meas_now;
+ u64 clock_noc = 0;
+ u64 period_cycle = int_pow(2, DURATION);
+ int j;
+ char *buf1;
+ char buf2[SIZE_SMALL_BUF + 2];
+ int r;
+ ssize_t ret;
+ int count; /* Used to avoid write over buffer size */
+ struct win2030_noc_stat_traceport_data *data;
+
+ //extern int win2030_noc_get_error(struct win2030_noc_device *noc_device);
+ //win2030_noc_get_error(noc_device);
+
+ if (noc_device == NULL)
+ return -ENODEV;
+
+ stat = noc_device->stat;
+ if (!stat)
+ return -ENODEV;
+ clock_noc = (u64) stat->clock_rate;
+
+ buf1 = kzalloc(sizeof(*buf1) * SIZE_BIG_BUF, GFP_KERNEL);
+ if (!buf1)
+ return -ENODEV;
+
+ r = snprintf(buf1, SIZE_BIG_BUF,"\t##### RESULTS #####\n\n");
+
+ count = SIZE_BIG_BUF - strlen(buf1);
+ list_for_each_entry(stat_probe, &stat->probe, link) {
+ probe = stat_probe->probe;
+ if (clock_noc == 0) {
+ clock_noc = (u64) clk_get_rate(probe->clock);
+ }
+ list_for_each_entry(stat_measure, &stat_probe->measure, link) {
+ data = &stat_measure->data;
+ if (count > 0) {
+ if (data->init_flow_name != NULL)
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "Probe %s, clk %llu, (%s): %s-->%s\n",
+ stat_probe->probe->id,
+ clock_noc,
+ stat_probe->probe->available_portsel[data->idx_trace_port_sel],
+ data->init_flow_name,
+ data->target_flow_name);
+ else
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "Probe %s, clk %llu, (%s):\n",
+ stat_probe->probe->id,
+ clock_noc,
+ stat_probe->probe->available_portsel[data->idx_trace_port_sel]);
+
+ strncat(buf1, buf2, count);
+ count -= strlen(buf2);
+ }
+ for (j = 0; j < probe->nr_portsel; j++) {
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "\tTracePort %s, NbOfMeasure=%i\n",
+ stat_probe->probe->available_portsel[j],
+ stat_measure->iteration[j]);
+ strncat(buf1, buf2, count);
+ count -= strlen(buf2);
+
+ if (count > 0) {
+ if (stat_measure->iteration[j] != 0) {
+ meas_min = stat_measure->min[j];
+ meas_max = stat_measure->max[j];
+ meas_mean = stat_measure->mean[j];
+ do_div(meas_mean,
+ stat_measure->iteration[j]);
+ meas_now = stat_measure->now[j];
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "\t\t total=%llu (KB), period time %lld (us), total time %lld (s)\n"
+ "\t\t min=%llu (KB/s), max=%llu (KB/s) mean=%llu (KB/s) current=%llu (KB/s)\n\n",
+ stat_measure->mean[j] / 1000,
+ period_cycle * 1000 * 1000 / clock_noc,
+ (stat_measure->iteration[j] * period_cycle) / clock_noc,
+ ((meas_min * clock_noc) / (period_cycle * 1000)),
+ ((meas_max * clock_noc) / (period_cycle * 1000)),
+ ((meas_mean * clock_noc) / (period_cycle * 1000)),
+ ((meas_now * clock_noc) / (period_cycle * 1000)));
+ } else {
+ r = snprintf(buf2, SIZE_SMALL_BUF,
+ "\t\t No results\n\n");
+ }
+ strncat(buf1, buf2, count);
+ count -= strlen(buf2);
+ }
+ }
+ }
+ }
+
+ r = strlen(buf1) + 1;
+
+ ret = simple_read_from_buffer(ubuf, cnt, ppos, buf1, r);
+
+ kfree(buf1);
+ return ret;
+
+}
+
+/**
+ * Called when open results file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_stat_results_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * Called when opening config file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_stat_config_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * Called when opening run file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_stat_run_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * Called when reading readme file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_stat_readme_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, noc_stat_readme_show, inode->i_private);
+}
+
+static const struct file_operations noc_stat_results_fops = {
+ .open = noc_stat_results_open,
+ .read = noc_stat_results_read,
+ .llseek = generic_file_llseek,
+};
+
+static const struct file_operations noc_stat_config_fops = {
+ .open = noc_stat_config_open,
+ .read = noc_stat_config_read,
+ .write = noc_stat_config_write,
+ .llseek = generic_file_llseek,
+};
+
+static const struct file_operations noc_stat_run_fops = {
+ .open = noc_stat_run_open,
+ .read = noc_stat_run_read,
+ .write = noc_stat_run_write,
+ .llseek = generic_file_llseek,
+};
+
+static const struct file_operations noc_stat_readme_fops = {
+ .open = noc_stat_readme_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/**
+ * Entry point for NOC sniffer sysfs
+ * @param _dev A reference to the device
+ * @return 0
+ */
+int win2030_noc_stat_debugfs_init(struct device *_dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct device_node *np = _dev->of_node;
+ struct dentry *dir, *d;
+ char name[32];
+
+ scnprintf(name, ARRAY_SIZE(name), "%s_stat", np->name);
+
+ dir = debugfs_create_dir(name, win2030_noc_ctrl.win2030_noc_root_debug_dir);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ noc_device->stat->dir = dir;
+
+ d = debugfs_create_file("results", S_IRUGO, dir, noc_device,
+ &noc_stat_results_fops);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+ d = debugfs_create_file("config", S_IRUGO | S_IWUSR, dir, noc_device,
+ &noc_stat_config_fops);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+ d = debugfs_create_file("run", S_IRUGO | S_IWUSR, dir, noc_device,
+ &noc_stat_run_fops);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+ d = debugfs_create_file("readme.txt", S_IRUGO, dir, noc_device,
+ &noc_stat_readme_fops);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+
+ return 0;
+}
diff --git a/drivers/interconnect/eswin/noc_sysfs.c b/drivers/interconnect/eswin/noc_sysfs.c
new file mode 100644
index 000000000000..e3b24a258818
--- /dev/null
+++ b/drivers/interconnect/eswin/noc_sysfs.c
@@ -0,0 +1,783 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+
+#include "noc.h"
+
+#define MAX_ENUM_SIZE 254
+
+static int win2030_noc_user_flag_dump(char *buf,
+ struct win2030_bitfield *bitfield, unsigned bf_value)
+{
+ int i;
+ int count = 1;
+
+ for (i = 0; i < bitfield->length; i++) {
+ if (test_bit(i, (unsigned long *)&bf_value)) {
+ strncat(buf, bitfield->lut[i], strlen(bitfield->lut[i]));
+ strncat(buf, "|", 1);
+ count += strlen(bitfield->lut[i]);
+ }
+ }
+ count--;
+ return count;
+}
+
+static void win2030_bitfield_debug_print(char *buf,
+ struct win2030_bitfield *bitfield,
+ unsigned value,
+ const char *prefix)
+{
+ unsigned bf_value = (value & bitfield->mask)
+ >> bitfield->offset;
+ int count = 0;
+ char buf2[SIZE_SMALL_BUF + 2];
+
+ if (bitfield->lut) {
+ if (!strcmp(bitfield->name, "User_flag")) {
+ count = sprintf(buf2, "%s%s: ", prefix, bitfield->name);
+ count += win2030_noc_user_flag_dump(buf2, bitfield, bf_value);
+ } else {
+ count = sprintf(buf2, "%s%s: %s", prefix,
+ bitfield->name,
+ bitfield->lut[bf_value]);
+ }
+ } else {
+ count = sprintf(buf2, "%s%s: %x", prefix,
+ bitfield->name, bf_value);
+ }
+ strncat(buf, buf2, count);
+ strncat(buf, "\n", 1);
+ return;
+}
+
+static int win2030_register_debug_print(char *buf,
+ struct win2030_register *reg,
+ unsigned value,
+ const char *prefix)
+{
+ struct win2030_bitfield *bf = NULL;
+ char myprefix[64];
+ int count = 0;
+ char buf2[SIZE_SMALL_BUF + 2];
+
+ scnprintf(myprefix, ARRAY_SIZE(myprefix), "\t%s", prefix);
+ count = sprintf(buf2, "%s%s: %#x\n", prefix, reg->name, value);
+ strncat(buf, buf2, count);
+
+ list_for_each_entry(bf, &reg->bitfields, link) {
+ win2030_bitfield_debug_print(buf, bf, value,
+ myprefix);
+ }
+ return 0;
+}
+
+int noc_debug_open(struct inode *inode, struct file *filp)
+{
+ filp->private_data = inode->i_private;
+ return 0;
+}
+static int noc_err_get_bitfield_vaule(struct win2030_register *reg,
+ int reg_vaule, char *bitfield_name)
+{
+ struct win2030_bitfield *bf = NULL;
+ int bitfield_vaule = -1;
+
+ list_for_each_entry(bf, &reg->bitfields, link) {
+ if (!strcmp(bf->name, bitfield_name)) {
+ bitfield_vaule = (reg_vaule & bf->mask) >> bf->offset;
+ break;
+ }
+ }
+ return bitfield_vaule;
+}
+
+static void win2030_noc_err_addr_dump(char *buf, struct win2030_noc_device *noc_device,
+ struct win2030_noc_error *noc_err,
+ int reg_index,
+ const char *prefix)
+{
+ struct win2030_register *reg1, *reg3, *reg4;
+ u32 vaule_init_flow, vaule_target_flow, vaule_target_sub_range;
+ struct win2030_bitfield *bf = NULL;
+ struct win2030_bitfield *bf_AbsoluteAddress;
+ unsigned vaule_addr_msb;
+ u64 vaule_addr;
+ int i;
+ unsigned vaule_err_RouteId;
+ bool found = false;
+ char myprefix[64];
+ char buf2[SIZE_SMALL_BUF + 2];
+ int count = 0;
+
+ scnprintf(myprefix, ARRAY_SIZE(myprefix), "\t%s", prefix);
+
+ reg3 = noc_device->error_registers[reg_index];
+
+ /*Get the InitFlow, TargetFlow, TargetSubRange of the err*/
+ vaule_err_RouteId = noc_err->err[reg3->aperture_link];
+
+ reg1 = noc_device->error_registers[reg3->aperture_link];
+ vaule_init_flow = noc_err_get_bitfield_vaule(reg1, vaule_err_RouteId, "InitFlow");
+ vaule_target_flow = noc_err_get_bitfield_vaule(reg1, vaule_err_RouteId, "TargetFlow");
+ vaule_target_sub_range = noc_err_get_bitfield_vaule(reg1, vaule_err_RouteId, "TargetSubRange");
+
+ vaule_addr = noc_err->err[reg_index];
+
+ if (-1 != reg3->msb_link) {
+ /*if addr msb exist, get it*/
+ vaule_addr_msb = noc_err->err[reg3->msb_link];
+ reg4 = noc_device->error_registers[reg3->msb_link];
+ list_for_each_entry(bf, &reg4->bitfields, link) {
+ if (!strcmp(bf->name, "addr_msb")) {
+ vaule_addr |= ((u64)(vaule_addr_msb & bf->mask)) << 32;
+ break;
+ }
+ }
+ }
+ /*check if any recorded RouteId match the err pkt's RouteId*/
+ list_for_each_entry(bf, &reg3->bitfields, link) {
+ if (!strcmp(bf->name, "AbsoluteAddress")) {
+ bf_AbsoluteAddress = bf;
+ for (i = 0; i < bf_AbsoluteAddress->aperture_size; i++) {
+ if (bf_AbsoluteAddress->target_sub_range[i] == vaule_target_sub_range
+ && bf_AbsoluteAddress->target_flow[i] == vaule_target_flow
+ && bf_AbsoluteAddress->init_flow[i] == vaule_init_flow) {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (true == found) {
+ vaule_addr |= bf_AbsoluteAddress->aperture_base[i];
+ count = sprintf(buf2, "%s%s: 0x%llx\n", prefix, bf_AbsoluteAddress->name, vaule_addr);
+ } else {
+ count = sprintf(buf2, "%s%s: 0x%llx\n", prefix, "OffsetAddr", vaule_addr);
+ }
+ strncat(buf, buf2, count);
+ return;
+}
+
+int noc_error_dump(char *buf,
+ struct win2030_noc_device *noc_device,
+ struct win2030_noc_error *noc_err)
+{
+ unsigned i;
+ unsigned err;
+ struct win2030_register *reg;
+ int count = 0;
+ char buf2[SIZE_SMALL_BUF + 2];
+
+ count = sprintf(buf2, "timestamp: %lld:\n", noc_err->timestamp);
+ strncat(buf, buf2, count);
+ for (i = 0; i < noc_device->error_logger_cnt; i++) {
+ reg = noc_device->error_registers[noc_device->err_log_lut[i]];
+ if (-1 != reg->aperture_link) {
+ /*should ErrLog3 come here
+ get err_init_target_subrange form ErrLog1
+ if ErrLog4 exist, add it to the addr vaule
+ */
+ win2030_noc_err_addr_dump(buf, noc_device, noc_err,
+ noc_device->err_log_lut[i], "\t");
+ }
+ err = noc_err->err[noc_device->err_log_lut[i]];
+ win2030_register_debug_print(buf, reg, err, "\t");
+ }
+ return 0;
+}
+
+static const char *win2030_bitfield_get_enum_from_value(struct win2030_bitfield *bf,
+ unsigned value)
+{
+ const char **lut = bf->lut;
+ const char *mystr;
+ unsigned i;
+
+ if (lut == NULL)
+ return ERR_PTR(-EINVAL);
+
+ for (mystr = lut[0], i = 0; mystr; mystr = lut[++i])
+ ;
+
+ if (value > i)
+ return ERR_PTR(-EINVAL);
+
+ return lut[value];
+}
+
+int win2030_bitfield_read(struct win2030_bitfield *bf, unsigned *value)
+{
+ struct win2030_register *reg = bf->parent;
+
+ if (reg == NULL)
+ return -EINVAL;
+
+ if (reg->base == NULL)
+ return -EINVAL;
+
+ if (value == NULL)
+ return -EINVAL;
+
+ *value = ioread32(WIN2030_NOC_REG_ADDR(reg));
+ *value &= bf->mask;
+ *value >>= bf->offset;
+
+ dev_dbg_once(reg->parent,
+ "Read Register 0x%08x @0x%px (%s), bitfield (%s) "
+ "val=0x%x, %d bit at offset %d, mask %x\n",
+ ioread32(WIN2030_NOC_REG_ADDR(reg)), WIN2030_NOC_REG_ADDR(reg),
+ reg->name, bf->name, *value, bf->length, bf->offset, bf->mask);
+
+ return 0;
+}
+
+static const char *win2030_bitfield_read_enum(struct win2030_bitfield *bf)
+{
+ int ret;
+ unsigned value;
+
+ ret = win2030_bitfield_read(bf, &value);
+ if (ret)
+ return NULL;
+
+ return win2030_bitfield_get_enum_from_value(bf, value);
+}
+
+static int win2030_bitfield_get_value_from_enum(struct win2030_bitfield *bf,
+ const char *str_value,
+ unsigned *value)
+{
+ const char **lut = bf->lut;
+ const char *mystr;
+ unsigned i;
+
+ if (lut == NULL) {
+ if (sscanf(str_value, "%x", value) == 1)
+ return 0;
+
+ return -EINVAL;
+ }
+
+ for (mystr = lut[0], i = 0; mystr; mystr = lut[++i]) {
+ if (!(strcmp(str_value, mystr))) {
+ *value = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int win2030_bitfield_write(struct win2030_bitfield *bf, unsigned value)
+{
+ unsigned long flags;
+ unsigned reg_value;
+ struct win2030_register *reg = bf->parent;
+
+ if (reg == NULL)
+ return -EINVAL;
+
+ if (reg->base == NULL)
+ return -EINVAL;
+
+ /*if (value >= (BIT(bf->length)-1)) */
+ /* return -EINVAL; */
+
+ spin_lock_irqsave(&reg->hw_lock, flags);
+ reg_value = ioread32(WIN2030_NOC_REG_ADDR(reg));
+ reg_value &= ~bf->mask;
+ reg_value |= (value << bf->offset);
+ iowrite32(reg_value, WIN2030_NOC_REG_ADDR(reg));
+ spin_unlock_irqrestore(&reg->hw_lock, flags);
+
+ dev_dbg(reg->parent,
+ "Write Register 0x%08x @%p (%s), bitfield (%s) val=0x%x"
+ ", %d bit at offset %d, mask %x\n",
+ reg_value, WIN2030_NOC_REG_ADDR(reg), reg->name, bf->name,
+ value, bf->length, bf->offset, bf->mask);
+ return 0;
+}
+
+static int win2030_bitfield_write_enum(struct win2030_bitfield *bf, const char *str)
+{
+ int value, ret;
+
+ ret = win2030_bitfield_get_value_from_enum(bf, str, &value);
+ if (ret)
+ return ret;
+
+ ret = win2030_bitfield_write(bf, value);
+
+ return ret;
+}
+
+static int noc_debug_available_show(struct seq_file *m, void *p)
+{
+ const char **to_show = m->private;
+ const char *str;
+ int i = 0;
+ for (str = to_show[0]; str; str = to_show[++i])
+ seq_printf(m, "%s ", str);
+
+ seq_puts(m, "\r\n");
+ return 0;
+}
+
+static int noc_debug_available_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, noc_debug_available_show, inode->i_private);
+}
+
+static ssize_t noc_debug_bf_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct win2030_bitfield *bf = filp->private_data;
+ char buf[MAX_ENUM_SIZE + 1];
+ int i;
+ size_t ret;
+ int err;
+
+ ret = cnt;
+
+ if (cnt > MAX_ENUM_SIZE)
+ cnt = MAX_ENUM_SIZE;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ /* strip ending whitespace. */
+ for (i = cnt - 1; i > 0 && isspace(buf[i]); i--)
+ buf[i] = 0;
+
+ if (bf->lut) {
+ err = win2030_bitfield_write_enum(bf, buf);
+ if (err)
+ return err;
+ } else {
+ unsigned value = 0;
+ err = sscanf(buf, "%x\n", &value);
+ /*if (err) */
+ /* return -ENODEV; */
+ err = win2030_bitfield_write(bf, value);
+ if (err)
+ return -ENODEV;
+ }
+ *ppos += ret;
+
+ return ret;
+}
+
+static ssize_t noc_debug_bf_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct win2030_bitfield *bf = filp->private_data;
+ char buf[MAX_ENUM_SIZE + 2];
+ int r;
+ const char *str;
+ unsigned value;
+
+ if (bf == NULL)
+ return -ENODEV;
+
+ if (bf->lut) {
+ str = win2030_bitfield_read_enum(bf);
+ if (IS_ERR_OR_NULL(str))
+ str = "UNKNOWN BITFIELD VALUE";
+ r = sprintf(buf, "%s\n", str);
+ } else {
+ r = win2030_bitfield_read(bf, &value);
+ if (r)
+ return -ENODEV;
+
+ r = sprintf(buf, "%#x\n", value);
+ }
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static int noc_debug_error_show(struct seq_file *m, void *p)
+{
+ struct win2030_noc_device *noc_device = m->private;
+ struct win2030_noc_error *noc_err = NULL;
+ unsigned long flags;
+ char *buf;
+
+ buf = kzalloc(sizeof(*buf) * SIZE_BIG_BUF, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&noc_device->lock, flags);
+ list_for_each_entry(noc_err, &noc_device->err_queue, link) {
+ spin_unlock_irqrestore(&noc_device->lock, flags);
+ noc_error_dump(buf, noc_device, noc_err);
+ spin_lock_irqsave(&noc_device->lock, flags);
+ }
+ spin_unlock_irqrestore(&noc_device->lock, flags);
+ seq_printf(m, "%s\n", buf);
+ kfree(buf);
+ return 0;
+}
+
+static int noc_debug_error_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, noc_debug_error_show, inode->i_private);
+}
+
+/**
+ * Called when writing run file
+ */
+static ssize_t noc_qos_run_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct win2030_noc_device *noc_device = filp->private_data;
+ char buf[SIZE_SMALL_BUF];
+ unsigned ret;
+
+ if (cnt > SIZE_SMALL_BUF)
+ cnt = SIZE_SMALL_BUF - 1;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ if (buf[0] == '0') {
+ /* FIXME spinlock? */
+ if (false != noc_device->qos_run) {
+ noc_device->qos_run = false;
+ ret = noc_device_qos_set(noc_device, false);
+ if (ret)
+ dev_err(noc_device->dev, "Error when disable qos config!\n");
+ }
+ } else if (buf[0] == '1') {
+ if (true != noc_device->qos_run) {
+ noc_device->qos_run = true;
+ ret = noc_device_qos_set(noc_device, true);
+ if (ret)
+ dev_err(noc_device->dev, "Error when enable qos config!\n");
+ }
+ } else
+ return -EFAULT;
+
+ *ppos += cnt;
+
+ return cnt;
+}
+
+/**
+ * Called when reading run file
+ */
+static ssize_t noc_qos_run_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+#define RUN_STR_SIZE 11
+ struct win2030_noc_device *noc_device = filp->private_data;
+ char buf[RUN_STR_SIZE];
+ int r;
+
+ r = snprintf(buf, RUN_STR_SIZE, "%i\n", noc_device->qos_run);
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+/**
+ * Called when opening run file
+ * @param inode
+ * @param file
+ * @return
+ */
+static int noc_qos_run_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static const struct file_operations noc_debug_error_fops = {
+ .open = noc_debug_error_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations noc_debug_qos_fops = {
+ .open = noc_qos_run_open,
+ .read = noc_qos_run_read,
+ .write = noc_qos_run_write,
+ .llseek = generic_file_llseek,
+};
+
+static const struct file_operations noc_debug_fops = {
+ .open = noc_debug_open,
+ .read = noc_debug_bf_read,
+ .write = noc_debug_bf_write,
+ .llseek = generic_file_llseek,
+};
+
+static const struct file_operations noc_debug_available_fops = {
+ .open = noc_debug_available_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int win2030_noc_debug_reg_init(struct win2030_register *reg, struct dentry *dir)
+{
+ struct dentry *reg_dir;
+ struct win2030_bitfield *bf = NULL;
+
+ reg_dir = debugfs_create_dir(reg->name, dir);
+ if (IS_ERR(reg_dir))
+ return PTR_ERR(reg_dir);
+
+ list_for_each_entry(bf, &reg->bitfields, link) {
+ debugfs_create_file(bf->name,
+ S_IRUGO | S_IWUSR, reg_dir, bf,
+ &noc_debug_fops);
+ }
+ return 0;
+}
+
+int win2030_noc_packet_probe_debug_init(struct win2030_noc_probe *probe,
+ struct dentry *probe_dir)
+
+{
+ int j;
+ char name[64];
+ struct win2030_register *reg;
+ struct win2030_bitfield *bf = NULL;
+ int ret = 0;
+
+ for (j = 0; j < probe->nr_filters; j++) {
+ struct win2030_noc_filter *filter;
+ struct dentry *filter_dir;
+
+ filter = &probe->filters[j];
+
+ scnprintf(name, ARRAY_SIZE(name), "filter%d", j);
+ filter_dir = debugfs_create_dir(name, probe_dir);
+ if (IS_ERR(filter_dir))
+ return PTR_ERR(filter_dir);
+
+ reg = filter->route_id_base;
+ list_for_each_entry(bf, &reg->bitfields, link) {
+ if (j != 0)
+ continue;
+ if ((!(strcmp(bf->name, "InitFlow")))
+ || (!(strcmp(bf->name, "TargetFlow")))) {
+ scnprintf(name, ARRAY_SIZE(name),
+ "available_%s_source_event", bf->name);
+ debugfs_create_file(name, S_IRUGO,
+ filter_dir, bf->lut,
+ &noc_debug_available_fops);
+ }
+ }
+
+ list_for_each_entry(reg, &filter->register_link, parent_link) {
+ ret = win2030_noc_debug_reg_init(reg, filter_dir);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+ return ret;
+}
+
+int win2030_noc_trans_probe_debug_init(struct win2030_noc_probe *probe,
+ struct dentry *probe_dir)
+{
+ int j;
+ char name[64];
+ struct win2030_register *reg = NULL;
+ int ret = 0;
+
+ for (j = 0; j < probe->nr_filters; j++) {
+ struct win2030_noc_trans_filter *filter;
+ struct dentry *filter_dir;
+
+ filter = &probe->trans_filters[j];
+
+ scnprintf(name, ARRAY_SIZE(name), "filter%d", j);
+ filter_dir = debugfs_create_dir(name, probe_dir);
+ if (IS_ERR(filter_dir))
+ return PTR_ERR(filter_dir);
+
+ list_for_each_entry(reg, &filter->register_link, parent_link) {
+ ret = win2030_noc_debug_reg_init(reg, filter_dir);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+
+ for (j = 0; j < probe->nr_profilers; j++) {
+ struct win2030_noc_trans_profiler *profiler;
+ struct dentry *profiler_dir;
+
+ profiler = &probe->trans_profilers[j];
+
+ scnprintf(name, ARRAY_SIZE(name), "profiler%d", j);
+ profiler_dir = debugfs_create_dir(name, probe_dir);
+ if (IS_ERR(profiler_dir))
+ return PTR_ERR(profiler_dir);
+
+ list_for_each_entry(reg, &profiler->register_link, parent_link) {
+ ret = win2030_noc_debug_reg_init(reg, profiler_dir);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+/**
+ * Create sysfs
+ * @param _dev
+ * @return
+ */
+int win2030_noc_debug_init(struct device *_dev)
+{
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct device_node *np = _dev->of_node;
+ struct dentry *dir;
+ char name[64];
+ int j;
+ struct win2030_noc_probe *probe = NULL;
+ int ret;
+ struct dev_qos_cfg *qos = NULL;
+ struct dentry *dir_qos;
+ struct dentry *dir_qos_module;
+ struct win2030_register *reg = NULL;
+
+ scnprintf(name, ARRAY_SIZE(name), "%s_debug", np->name);
+
+ dir = debugfs_create_dir(name, win2030_noc_ctrl.win2030_noc_root_debug_dir);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ noc_device->dir = dir;
+
+ debugfs_create_file("errors", S_IRUGO, dir, noc_device, &noc_debug_error_fops);
+
+ list_for_each_entry(probe, &noc_device->probes, link) {
+ struct dentry *probe_dir;
+
+ scnprintf(name, ARRAY_SIZE(name), "probe_%s", probe->id);
+ probe_dir = debugfs_create_dir(name, noc_device->dir);
+ if (IS_ERR(probe_dir))
+ return PTR_ERR(probe_dir);
+
+ list_for_each_entry(reg, &probe->register_link, parent_link) {
+ ret = win2030_noc_debug_reg_init(reg, probe_dir);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (probe_t_pkt == probe->type) {
+ ret = win2030_noc_packet_probe_debug_init(probe, probe_dir);
+ } else {
+ ret = win2030_noc_trans_probe_debug_init(probe, probe_dir);
+ }
+ if (0 != ret) {
+ return ret;
+ }
+ for (j = 0; j < probe->nr_counters; j++) {
+ struct win2030_noc_counter *cnt;
+ struct dentry *dir_cnt;
+ cnt = &probe->counters[j];
+
+ scnprintf(name, ARRAY_SIZE(name), "counter%d", j);
+ dir_cnt = debugfs_create_dir(name, probe_dir);
+ if (IS_ERR(dir_cnt))
+ return PTR_ERR(dir_cnt);
+
+ /* We assume the counters are the same */
+ if (j == 0) {
+ /*
+ scnprintf(name, ARRAY_SIZE(name),
+ "available_counter_port_selection");
+ debugfs_create_file(name, S_IRUGO, probe_dir,
+ cnt->portsel->lut,
+ &noc_debug_available_fops);
+ */
+ scnprintf(name, ARRAY_SIZE(name),
+ "available_counter_alarm_mode");
+ debugfs_create_file(name, S_IRUGO, probe_dir,
+ cnt->alarm_mode->lut,
+ &noc_debug_available_fops);
+
+ scnprintf(name, ARRAY_SIZE(name),
+ "available_counter_source_event");
+ debugfs_create_file(name, S_IRUGO, probe_dir,
+ cnt->source_event->lut,
+ &noc_debug_available_fops);
+ }
+ list_for_each_entry(reg, &cnt->register_link, parent_link) {
+ ret = win2030_noc_debug_reg_init(reg, dir_cnt);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+ }
+ if (!list_empty (&noc_device->qos_list)) {
+ dir_qos = debugfs_create_dir("qos", noc_device->dir);
+ if (IS_ERR(dir_qos))
+ return PTR_ERR(dir_qos);
+
+ debugfs_create_file("enable", S_IRUGO, dir_qos, noc_device, &noc_debug_qos_fops);
+ list_for_each_entry(qos, &noc_device->qos_list, list) {
+ dir_qos_module = debugfs_create_dir(qos->name, dir_qos);
+ list_for_each_entry(reg, &qos->register_link, parent_link) {
+ ret = win2030_noc_debug_reg_init(reg, dir_qos_module);
+ if (ret < 0) {
+ dev_err(_dev, "Error %d while init qos debug register\n", ret);
+ return ret;
+ }
+ }
+ }
+ }
+
+ ret = win2030_noc_sideband_mgr_debug_init(_dev);
+ if (0 != ret) {
+ dev_err(_dev, "Error %d while init sideband mgr debug\n", ret);
+ return ret;
+ }
+ return 0;
+}
diff --git a/drivers/interconnect/eswin/of_noc.c b/drivers/interconnect/eswin/of_noc.c
new file mode 100644
index 000000000000..bc5d874593da
--- /dev/null
+++ b/drivers/interconnect/eswin/of_noc.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/interrupt.h>
+#include <linux/win2030_noc.h>
+
+#include "noc.h"
+
+
+void of_noc_qos_free(struct noc_qos_list *qos_root)
+{
+ struct noc_qos_list *qos = NULL;
+ list_for_each_entry(qos, &qos_root->list, list) {
+ kfree(qos);
+ }
+}
+EXPORT_SYMBOL(of_noc_qos_free);
+
+int of_noc_qos_populate(struct device *dev,
+ struct device_node *np,
+ struct noc_qos_list **qos_root)
+{
+ int def_len;
+ struct property *prop;
+
+ prop = of_find_property(np, "intel,qos-ports", &def_len);
+ if (prop != NULL) {
+ struct noc_qos_list *qos;
+ int i, ncfg = of_property_count_strings(np, "intel,qos-ports");
+ *qos_root = (struct noc_qos_list *)
+ devm_kzalloc(dev,
+ sizeof(struct noc_qos_list), GFP_KERNEL);
+ if (*qos_root == NULL) {
+ dev_err(dev, "%s: Memory allocation failed!",
+ __func__);
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&(*qos_root)->list);
+ for (i = 0; i < ncfg; i++) {
+ qos = (struct noc_qos_list *)
+ devm_kzalloc(dev,
+ sizeof(struct noc_qos_list),
+ GFP_KERNEL);
+ if (qos == NULL) {
+ dev_err(dev, "%s: Memory allocation failed!",
+ __func__);
+ of_noc_qos_free(*qos_root);
+ return -ENOMEM;
+ }
+ of_property_read_string_index(np,
+ "intel,qos-ports", i,
+ &qos->name);
+
+ dev_info(dev, "QoS port %s added to list\n", qos->name);
+ list_add_tail(&qos->list,
+ &(*qos_root)->list);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(of_noc_qos_populate);
diff --git a/drivers/interconnect/eswin/win2030_noc_wdt.c b/drivers/interconnect/eswin/win2030_noc_wdt.c
new file mode 100644
index 000000000000..ed9ed6827ad2
--- /dev/null
+++ b/drivers/interconnect/eswin/win2030_noc_wdt.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/printk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/bitfield.h>
+
+#define SYSCON_NOC_CFG0_OFFSET 0x324
+#define NOC_CFG0_REG_BIT_WDT_MASK GENMASK(16, 0)
+#define SYSCON_NOC_CFG1_OFFSET 0x328
+#define NOC_CFG1_REG_BIT_WDT_MASK GENMASK(20, 0)
+
+char *noc_wdt_irq_src[] = {
+ "cnoc_vo_timeout",
+ "cnoc_vi_timeout",
+ "cnoc_vc_timeout",
+ "cnoc_tcu_timeout",
+ "cnoc_pciet_x_timeout",
+ "cnoc_pciet_p_timeout",
+ "cnoc_npu_timeout",
+ "cnoc_mcput_d2d_timeout",
+ "cnoc_lsp_apb6_timeout",
+ "cnoc_lsp_apb4_timeout",
+ "cnoc_lsp_apb3_timeout",
+ "cnoc_lsp_apb2_timeout",
+ "cnoc_hsp_timeout",
+ "cnoc_gpu_timeout",
+ "cnoc_dspt_timeout",
+ "cnoc_ddrt1_phy_timeout",
+ "cnoc_ddrt1_ctrl_timeout",
+ "cnoc_ddr0_phy_timeout",
+ "cnoc_ddr0_ctrl_timeout",
+ "cnoc_aon_timeout",
+ "clmm_timeout",
+ "rnoc_ddrt1_p4_timeout",
+ "rnoc_ddrt0_p4_timeout",
+ "mnoc_ddr1_p3_timeout",
+ "mnoc_ddr0_p3_timeout",
+ "snoc_pciet_timeout",
+ "snoc_npu_timeout",
+ "snoc_dspt_timeout",
+ "snoc_ddrt1_p2_timeout",
+ "snoc_ddrt1_p1_timeout",
+ "snoc_ddrt0_p2_timeout",
+ "snoc_ddrt0_p1_timeout",
+ "snoc_aon_timeout",
+ "lnoc_ddrt1_p0_timeout",
+ "lnoc_ddrt0_p0_timeout",
+};
+
+#define NOC_WDT_IRQ_NUMBER (ARRAY_SIZE(noc_wdt_irq_src))
+
+static irqreturn_t noc_wdt_interrupt(int irq, void *dev_id)
+{
+ struct irq_data *data = NULL;
+ char *irq_src = NULL;
+
+ data = irq_get_irq_data(irq);
+ if (NULL == data) {
+ pr_err("noc-wdt: invalid irq data\n");
+ }
+ irq_src = (char *)dev_id;
+ pr_warn_once("noc-wdt: interrupt %s occurred on irq %d, hw irq %ld!\n", irq_src,
+ irq, data->hwirq);
+ return IRQ_HANDLED;
+}
+
+static int eswin_noc_wdt_probe(struct platform_device *pdev)
+{
+ int i;
+ int req_irq;
+ struct device *dev;
+ int ret;
+ struct device *parent;
+ struct regmap *regmap;
+ int noc_ctl_reg;
+ int wd_ref_divsor;
+
+ for (i = 0; i < NOC_WDT_IRQ_NUMBER; i++) {
+ req_irq = platform_get_irq(pdev, i);
+ if (req_irq < 0)
+ return req_irq;
+
+ ret = devm_request_irq(&pdev->dev, req_irq, &noc_wdt_interrupt,
+ IRQF_SHARED |IRQF_ONESHOT ,
+ noc_wdt_irq_src[i], (void *)noc_wdt_irq_src[i]);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot register irq %d, ret %d\n", req_irq, ret);
+ return ret;
+ }
+ dev_dbg(&pdev->dev,"registered irq %s, base %d, num %ld\n", noc_wdt_irq_src[i],
+ platform_get_irq(pdev, 0), NOC_WDT_IRQ_NUMBER);
+ }
+ dev = &pdev->dev;
+ regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "eswin,syscrg_csr");
+ if (!IS_ERR(regmap)) {
+ ret = of_property_read_u32_index(dev->of_node, "eswin,syscrg_csr", 1,
+ &noc_ctl_reg);
+ if (ret) {
+ dev_err(dev, "can't get noc_ctl_reg offset from dts node(errno:%d)\n", ret);
+ return ret;
+ }
+ ret = of_property_read_u32_index(dev->of_node, "eswin,syscrg_csr", 2,
+ &wd_ref_divsor);
+ if (ret) {
+ dev_err(dev, "can't get wd_ref_divsor val from dts node(errno:%d)\n", ret);
+ return ret;
+ }
+ ret = regmap_update_bits(regmap, noc_ctl_reg, GENMASK(19, 4),
+ FIELD_PREP(GENMASK(19, 4), wd_ref_divsor));
+ if (ret) {
+ dev_err(dev, "can't set wd_ref_divsor val in sys_crg(errno:%d)\n", ret);
+ return ret;
+ }
+ }
+ parent = pdev->dev.parent;
+ if (!parent) {
+ dev_err(&pdev->dev, "no parent\n");
+ return -ENODEV;
+ }
+
+ regmap = syscon_node_to_regmap(parent->of_node);
+ if (IS_ERR(regmap)) {
+ dev_err(&pdev->dev, "failed to get regmap\n");
+ return PTR_ERR(regmap);
+ }
+ ret = regmap_write_bits(regmap, SYSCON_NOC_CFG0_OFFSET,
+ NOC_CFG0_REG_BIT_WDT_MASK, 0x1FFFF);
+ if (0 != ret) {
+ dev_err(&pdev->dev,"failed to enable timeout wdt\n");
+ }
+ ret = regmap_write_bits(regmap, SYSCON_NOC_CFG1_OFFSET,
+ NOC_CFG1_REG_BIT_WDT_MASK, 0x1FFFFF);
+ if (0 != ret) {
+ dev_err(&pdev->dev,"failed to enable timeout wdt\n");
+ }
+ return ret;
+}
+
+static int eswin_noc_wdt_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id eswin_noc_wdt_dt_ids[] = {
+ { .compatible = "eswin,win2030-noc-wdt", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver eswin_noc_wdt_driver = {
+ .probe = eswin_noc_wdt_probe,
+ .remove = eswin_noc_wdt_remove,
+ .driver = {
+ .name = "eswin-noc-wdt",
+ .of_match_table = eswin_noc_wdt_dt_ids,
+ },
+};
+
+static int __init eswin_noc_wdt_init(void)
+{
+ return platform_driver_register(&eswin_noc_wdt_driver);
+}
+
+subsys_initcall(eswin_noc_wdt_init);
diff --git a/drivers/interconnect/eswin/win2030_sideband_manager.c b/drivers/interconnect/eswin/win2030_sideband_manager.c
new file mode 100644
index 000000000000..c694e34115ea
--- /dev/null
+++ b/drivers/interconnect/eswin/win2030_sideband_manager.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Noc Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+
+#include "noc.h"
+#include "noc_regs.h"
+
+static const char *win2030_noc_busy_idle_enum[] = {
+ "busy",
+ "idle",
+ NULL
+};
+
+int win2030_noc_sideband_mgr_query(u32 sbm_id)
+{
+ struct win2030_bitfield *bf = NULL;
+ struct win2030_register *reg;
+ struct win2030_noc_sideband_magr *sbm = NULL;
+ int ret;
+ unsigned value;
+
+ if (IS_ERR_OR_NULL(win2030_noc_ctrl.win2030_noc_root_debug_dir) ||
+ list_empty(&win2030_noc_ctrl.sbm_link)) {
+ pr_info("Win2030 NOC SideBand driver not initialisation, will return idle directly!");
+ return 1;
+ }
+ list_for_each_entry(sbm, &win2030_noc_ctrl.sbm_link, link) {
+ reg = sbm->SenseIn0;
+ list_for_each_entry(bf, &reg->bitfields, link) {
+ if (sbm_id == bf->sbm_id) {
+ ret = win2030_bitfield_read(bf, &value);
+ if (ret) {
+ return -EIO;
+ } else {
+ return value;
+ }
+ }
+ }
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(win2030_noc_sideband_mgr_query);
+
+int win2030_noc_sideband_mgr_debug_init(struct device *_dev)
+{
+ int ret;
+ struct dentry *sbm_dir;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ struct win2030_noc_sideband_magr *sbm = noc_device->sbm;
+
+ if (NULL == sbm) {
+ return 0;
+ }
+
+ sbm_dir = debugfs_create_dir("sideband_manager", noc_device->dir);
+ if (IS_ERR(sbm_dir))
+ return PTR_ERR(sbm_dir);
+
+ ret = win2030_noc_debug_reg_init(sbm->SenseIn0, sbm_dir);
+ if (ret < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+static int win2030_noc_register_sideband_mgr(struct device_node *np,
+ struct device *_dev)
+{
+ int ret, i;
+ struct win2030_noc_sideband_magr *sbm;
+ struct win2030_register *reg;
+ struct win2030_bitfield *bf;
+ int size;
+ u32 *values_tab;
+ int offset;
+ struct win2030_noc_device *noc_device = dev_get_drvdata(_dev);
+ const char **bf_name;
+
+ /* Initialise mgr */
+ sbm = devm_kcalloc(_dev, 1, sizeof(struct win2030_noc_sideband_magr), GFP_KERNEL);
+ sbm->hw_base = of_iomap(np, 0);
+ if (IS_ERR_OR_NULL(sbm->hw_base)) {
+ return -EINVAL;
+ }
+ sbm->dev = _dev;
+
+ /**
+ * Register: SenseIn0
+ */
+ reg = sbm->SenseIn0 = win2030_new_register(_dev, "SenseIn0",
+ sbm->hw_base, SBM_SENSE_IN0, 32);
+ if (IS_ERR_OR_NULL(reg))
+ return -EINVAL;
+
+ size = of_property_count_elems_of_size(np, "SenseIn0", sizeof(u32) * 2);
+ if (size < 0) {
+ return -ENOENT;
+ }
+ values_tab = devm_kcalloc(_dev, size * 2, sizeof(u32), GFP_KERNEL);
+ if (!values_tab) {
+ return -ENOMEM;
+ }
+ ret = of_property_read_u32_array(np, "SenseIn0", values_tab, size * 2);
+ if (ret) {
+ dev_err(_dev, "Error while parsing SenseIn0 bitfield, ret %d!\n", ret);
+ return -ENOENT;
+ }
+ bf_name = win2030_get_strings_from_dts(_dev, np, "bf-name");
+ if (IS_ERR(bf_name)) {
+ ret = PTR_ERR(bf_name);
+ dev_err(_dev, "Error %d while get sbm bf name!\n", ret);
+ return ret;
+ }
+ for (i = 0; i < size; i++) {
+ offset = values_tab[2 * i + 1];
+ bf = win2030_new_bitfield(reg, bf_name[i], offset, SBM_SENSE_IN0_SENSE_IN0_WIDTH,
+ win2030_noc_busy_idle_enum);
+ if (IS_ERR_OR_NULL(bf))
+ return -EIO;
+
+ bf->sbm_id = values_tab[2 * i];
+ }
+ noc_device->sbm = sbm;
+ list_add_tail(&sbm->link, &win2030_noc_ctrl.sbm_link);
+ return 0;
+}
+
+
+/**
+ * Create sideband manager
+ * @param _dev
+ * @return
+ */
+int win2030_noc_init_sideband_mgr(struct device *_dev)
+{
+ int ret;
+ struct device_node *child = NULL;
+
+ for_each_child_of_node(_dev->of_node, child) {
+ if (of_device_is_compatible(child, "eswin,win2xxx-noc-sideband-manager")) {
+ ret = win2030_noc_register_sideband_mgr(child, _dev);
+ if (0 != ret) {
+ dev_err(_dev, "Error while register sideband manager\n");
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/include/linux/win2030_noc.h b/include/linux/win2030_noc.h
new file mode 100644
index 000000000000..8705b17960de
--- /dev/null
+++ b/include/linux/win2030_noc.h
@@ -0,0 +1,46 @@
+/*
+ ****************************************************************
+ *
+ * Copyright (C) 2011-2014 Intel Mobile Communications GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the GNU General Public License Version 2
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ ****************************************************************
+ */
+
+#ifndef WIN2030_NOC_H_
+#define WIN2030_NOC_H_
+
+struct noc_qos_list {
+ struct list_head list;
+ const char *name;
+};
+
+extern void win2030_noc_qos_set(char *name, bool is_enalbe);
+extern int of_noc_qos_populate(struct device *dev,
+ struct device_node *np,
+ struct noc_qos_list **qos);
+extern void of_noc_qos_free(struct noc_qos_list *qos_root);
+
+/**
+ * win2030_noc_sideband_mgr_query - query the module transaction status
+ *
+ * @sbm_id: module id in noc sideband manager
+ *
+ * return: module transaction status on sucess , 1 if idle, 0 if busy.
+ -EIO if failed to get the transaction status, -EINVAL
+ if can't match the sbm_id form sidebade manager
+ */
+extern int win2030_noc_sideband_mgr_query(int sbm_id);
+
+#endif
--
2.47.0