From 3c34827d89b68df0c5dc5f746e7a0212f1182cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chuangyifeng=E2=80=9D?= Date: Tue, 21 May 2024 14:17:38 +0800 Subject: [PATCH 012/222] 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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +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 . + * + * Authors: HuangYiFeng + */ + +#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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * Authors: HuangYiFeng + */ + +#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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include + +#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 . + * + * + **************************************************************** + */ + +#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