diff options
Diffstat (limited to 'drivers/perf/riscv_pmu_sbi.c')
-rw-r--r-- | drivers/perf/riscv_pmu_sbi.c | 64 |
1 files changed, 57 insertions, 7 deletions
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index 7b2288d4b1ec..c9116d9e4b57 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -44,7 +44,7 @@ static const struct attribute_group *riscv_pmu_attr_groups[] = { }; /* - * RISC-V doesn't have hetergenous harts yet. This need to be part of + * RISC-V doesn't have heterogeneous harts yet. This need to be part of * per_cpu in case of harts with different pmu counters */ static union sbi_pmu_ctr_info *pmu_ctr_list; @@ -52,6 +52,9 @@ static bool riscv_pmu_use_irq; static unsigned int riscv_pmu_irq_num; static unsigned int riscv_pmu_irq; +/* Cache the available counters in a bitmask */ +static unsigned long cmask; + struct sbi_pmu_event_data { union { union { @@ -267,6 +270,58 @@ static bool pmu_sbi_ctr_is_fw(int cidx) return (info->type == SBI_PMU_CTR_TYPE_FW) ? true : false; } +/* + * Returns the counter width of a programmable counter and number of hardware + * counters. As we don't support heterogeneous CPUs yet, it is okay to just + * return the counter width of the first programmable counter. + */ +int riscv_pmu_get_hpm_info(u32 *hw_ctr_width, u32 *num_hw_ctr) +{ + int i; + union sbi_pmu_ctr_info *info; + u32 hpm_width = 0, hpm_count = 0; + + if (!cmask) + return -EINVAL; + + for_each_set_bit(i, &cmask, RISCV_MAX_COUNTERS) { + info = &pmu_ctr_list[i]; + if (!info) + continue; + if (!hpm_width && info->csr != CSR_CYCLE && info->csr != CSR_INSTRET) + hpm_width = info->width; + if (info->type == SBI_PMU_CTR_TYPE_HW) + hpm_count++; + } + + *hw_ctr_width = hpm_width; + *num_hw_ctr = hpm_count; + + return 0; +} +EXPORT_SYMBOL_GPL(riscv_pmu_get_hpm_info); + +static unsigned long pmu_sbi_get_filter_flags(struct perf_event *event) +{ + unsigned long cflags = 0; + bool guest_events = false; + + if (event->attr.config1 & RISCV_PMU_CONFIG1_GUEST_EVENTS) + guest_events = true; + if (event->attr.exclude_kernel) + cflags |= guest_events ? SBI_PMU_CFG_FLAG_SET_VSINH : SBI_PMU_CFG_FLAG_SET_SINH; + if (event->attr.exclude_user) + cflags |= guest_events ? SBI_PMU_CFG_FLAG_SET_VUINH : SBI_PMU_CFG_FLAG_SET_UINH; + if (guest_events && event->attr.exclude_hv) + cflags |= SBI_PMU_CFG_FLAG_SET_SINH; + if (event->attr.exclude_host) + cflags |= SBI_PMU_CFG_FLAG_SET_UINH | SBI_PMU_CFG_FLAG_SET_SINH; + if (event->attr.exclude_guest) + cflags |= SBI_PMU_CFG_FLAG_SET_VSINH | SBI_PMU_CFG_FLAG_SET_VUINH; + + return cflags; +} + static int pmu_sbi_ctr_get_idx(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -277,11 +332,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event) uint64_t cbase = 0; unsigned long cflags = 0; - if (event->attr.exclude_kernel) - cflags |= SBI_PMU_CFG_FLAG_SET_SINH; - if (event->attr.exclude_user) - cflags |= SBI_PMU_CFG_FLAG_SET_UINH; - + cflags = pmu_sbi_get_filter_flags(event); /* retrieve the available counter index */ #if defined(CONFIG_32BIT) ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, @@ -806,7 +857,6 @@ static void riscv_pmu_destroy(struct riscv_pmu *pmu) static int pmu_sbi_device_probe(struct platform_device *pdev) { struct riscv_pmu *pmu = NULL; - unsigned long cmask = 0; int ret = -ENODEV; int num_counters; |