drivers: intc: plic: add shell cmd to get irq stats for debugging
Introduced `CONFIG_PLIC_SHELL` to enable the build of shell debugging command to get the hit count of each interrupt controller's IRQ line. This is especially useful when working with dynamically installed ISRs, which will be the case for `plic_sw`. Example usage: ``` uart:~$ plic stats get interrupt-controller@c000000 IRQ Hits ================== 10 177 uart:~$ plic stats get interrupt-controller@c000000 IRQ Hits ================== 10 236 uart:~$ plic stats clear interrupt-controller@c000000 Cleared stats of interrupt-controller@c000000. uart:~$ plic stats get interrupt-controller@c000000 IRQ Hits ================== 10 90 ``` Signed-off-by: Yong Cong Sin <ycsin@meta.com> Signed-off-by: Maxim Adelman <imax@meta.com>
This commit is contained in:
parent
8342d87478
commit
e9fa6f8b4a
|
@ -42,4 +42,11 @@ if(CONFIG_INTEL_VTD_ICTL)
|
|||
zephyr_library_include_directories(${ZEPHYR_BASE}/arch/x86/include)
|
||||
endif()
|
||||
|
||||
if(CONFIG_PLIC_SHELL)
|
||||
message(WARNING "
|
||||
WARNING: `CONFIG_PLIC_SHELL` is enabled.
|
||||
This can use quite a bit of RAM (PLICs * IRQs * sizeof(uint16_t))"
|
||||
)
|
||||
endif()
|
||||
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/arch/common/include)
|
||||
|
|
|
@ -10,3 +10,14 @@ config PLIC
|
|||
help
|
||||
Platform Level Interrupt Controller provides support
|
||||
for external interrupt lines defined by the RISC-V SoC.
|
||||
|
||||
if PLIC
|
||||
|
||||
config PLIC_SHELL
|
||||
bool "PLIC shell commands"
|
||||
depends on SHELL
|
||||
help
|
||||
Enable additional shell commands useful for debugging.
|
||||
Caution: This can use quite a bit of RAM (PLICs * IRQs * sizeof(uint16_t)).
|
||||
|
||||
endif # PLIC
|
||||
|
|
|
@ -13,11 +13,14 @@
|
|||
* for RISC-V processors
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sw_isr_common.h"
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/arch/cpu.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <soc.h>
|
||||
|
||||
#include <zephyr/sw_isr_table.h>
|
||||
|
@ -62,6 +65,14 @@ struct plic_config {
|
|||
riscv_plic_irq_config_func_t irq_config_func;
|
||||
};
|
||||
|
||||
struct plic_stats {
|
||||
uint16_t *irq_count;
|
||||
};
|
||||
|
||||
struct plic_data {
|
||||
struct plic_stats stats;
|
||||
};
|
||||
|
||||
static uint32_t save_irq;
|
||||
static const struct device *save_dev;
|
||||
|
||||
|
@ -258,6 +269,16 @@ static void plic_irq_handler(const struct device *dev)
|
|||
/* Get the IRQ number generating the interrupt */
|
||||
const uint32_t local_irq = sys_read32(claim_complete_addr);
|
||||
|
||||
#ifdef CONFIG_PLIC_SHELL
|
||||
const struct plic_data *data = dev->data;
|
||||
struct plic_stats stat = data->stats;
|
||||
|
||||
/* Cap the count at __UINT16_MAX__ */
|
||||
if (stat.irq_count[local_irq] != __UINT16_MAX__) {
|
||||
stat.irq_count[local_irq]++;
|
||||
}
|
||||
#endif /* CONFIG_PLIC_SHELL */
|
||||
|
||||
/*
|
||||
* Save IRQ in save_irq. To be used, if need be, by
|
||||
* subsequent handlers registered in the _sw_isr_table table,
|
||||
|
@ -340,6 +361,130 @@ static int plic_init(const struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PLIC_SHELL
|
||||
static inline int parse_device(const struct shell *sh, size_t argc, char *argv[],
|
||||
const struct device **plic)
|
||||
{
|
||||
ARG_UNUSED(argc);
|
||||
|
||||
*plic = device_get_binding(argv[1]);
|
||||
if (*plic == NULL) {
|
||||
shell_error(sh, "PLIC device (%s) not found!\n", argv[1]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_get_stats(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
const struct device *dev;
|
||||
int ret = parse_device(sh, argc, argv, &dev);
|
||||
uint16_t min_hit = 0;
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct plic_config *config = dev->config;
|
||||
const struct plic_data *data = dev->data;
|
||||
struct plic_stats stat = data->stats;
|
||||
|
||||
if (argc > 2) {
|
||||
min_hit = (uint16_t)atoi(argv[2]);
|
||||
shell_print(sh, "IRQ line with > %d hits:", min_hit);
|
||||
}
|
||||
|
||||
shell_print(sh, " IRQ\t Hits");
|
||||
shell_print(sh, "==================");
|
||||
for (size_t i = 0; i < MIN(config->num_irqs, CONFIG_MAX_IRQ_PER_AGGREGATOR); i++) {
|
||||
if (stat.irq_count[i] > min_hit) {
|
||||
shell_print(sh, "%6d\t%10d", i, stat.irq_count[i]);
|
||||
}
|
||||
}
|
||||
shell_print(sh, "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_clear_stats(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
const struct device *dev;
|
||||
int ret = parse_device(sh, argc, argv, &dev);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct plic_config *config = dev->config;
|
||||
const struct plic_data *data = dev->data;
|
||||
struct plic_stats stat = data->stats;
|
||||
|
||||
memset(stat.irq_count, 0,
|
||||
MIN(config->num_irqs, CONFIG_MAX_IRQ_PER_AGGREGATOR) * sizeof(uint16_t));
|
||||
|
||||
shell_print(sh, "Cleared stats of %s.\n", dev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Device name autocompletion support */
|
||||
static void device_name_get(size_t idx, struct shell_static_entry *entry)
|
||||
{
|
||||
const struct device *dev = shell_device_lookup(idx, NULL);
|
||||
|
||||
entry->syntax = (dev != NULL) ? dev->name : NULL;
|
||||
entry->handler = NULL;
|
||||
entry->help = NULL;
|
||||
entry->subcmd = NULL;
|
||||
}
|
||||
|
||||
SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(plic_stats_cmds,
|
||||
SHELL_CMD_ARG(get, &dsub_device_name,
|
||||
"Read PLIC's stats.\n"
|
||||
"Usage: plic stats get <device> [minimum hits]",
|
||||
cmd_get_stats, 2, 1),
|
||||
SHELL_CMD_ARG(clear, &dsub_device_name,
|
||||
"Reset PLIC's stats.\n"
|
||||
"Usage: plic stats clear <device>",
|
||||
cmd_clear_stats, 2, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(plic_cmds,
|
||||
SHELL_CMD_ARG(stats, &plic_stats_cmds, "PLIC stats", NULL, 3, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
static int cmd_plic(const struct shell *sh, size_t argc, char **argv)
|
||||
{
|
||||
shell_error(sh, "%s:unknown parameter: %s", argv[0], argv[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
SHELL_CMD_ARG_REGISTER(plic, &plic_cmds, "PLIC shell commands",
|
||||
cmd_plic, 2, 0);
|
||||
|
||||
#define PLIC_INTC_IRQ_COUNT_BUF_DEFINE(n) \
|
||||
static uint16_t local_irq_count_##n[MIN(DT_INST_PROP(n, riscv_ndev), \
|
||||
CONFIG_MAX_IRQ_PER_AGGREGATOR)];
|
||||
|
||||
#define PLIC_INTC_DATA_INIT(n) \
|
||||
PLIC_INTC_IRQ_COUNT_BUF_DEFINE(n); \
|
||||
static struct plic_data plic_data_##n = { \
|
||||
.stats = { \
|
||||
.irq_count = local_irq_count_##n, \
|
||||
}, \
|
||||
};
|
||||
|
||||
#define PLIC_INTC_DATA(n) &plic_data_##n
|
||||
#else
|
||||
#define PLIC_INTC_DATA_INIT(...)
|
||||
#define PLIC_INTC_DATA(n) (NULL)
|
||||
#endif
|
||||
|
||||
#define PLIC_INTC_IRQ_FUNC_DECLARE(n) static void plic_irq_config_func_##n(void)
|
||||
|
||||
#define PLIC_INTC_IRQ_FUNC_DEFINE(n) \
|
||||
|
@ -364,8 +509,9 @@ static int plic_init(const struct device *dev)
|
|||
|
||||
#define PLIC_INTC_DEVICE_INIT(n) \
|
||||
PLIC_INTC_CONFIG_INIT(n) \
|
||||
PLIC_INTC_DATA_INIT(n) \
|
||||
DEVICE_DT_INST_DEFINE(n, &plic_init, NULL, \
|
||||
NULL, &plic_config_##n, \
|
||||
PLIC_INTC_DATA(n), &plic_config_##n, \
|
||||
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, \
|
||||
NULL);
|
||||
|
||||
|
|
Loading…
Reference in a new issue