7182 lines
213 KiB
Diff
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(®->lock, flags);
|
|
+ list_add_tail(&bf->link, ®->bitfields);
|
|
+ spin_unlock_irqrestore(®->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(®->lock);
|
|
+ spin_lock_init(®->hw_lock);
|
|
+ INIT_LIST_HEAD(®->link);
|
|
+ INIT_LIST_HEAD(®->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, ®->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", ®->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, ®->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", ®);
|
|
+ if (0 != ret) {
|
|
+ dev_err(_dev, "%s register not found!\n", "ErrorLogger1");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ list_for_each_entry(bf, ®->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(®->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, ®);
|
|
+ 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, ®_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(®->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, ®_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(®->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(®->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(®->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(®->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,
|
|
+ ®->offset);
|
|
+ of_property_read_u32_index(np, propname, i + 1,
|
|
+ ®->value);
|
|
+
|
|
+ list_add_tail(®->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(®->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(®->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(®->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(®->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(®->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(®->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, ®->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, ®->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(®->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(®->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, ®->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, ®->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, ®4->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, ®3->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(®->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(®->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, ®->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, ®->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, ®->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
|
|
|