diff --git a/platform/generic/andes/Kconfig b/platform/generic/andes/Kconfig index a91fb9c9..3665b337 100644 --- a/platform/generic/andes/Kconfig +++ b/platform/generic/andes/Kconfig @@ -7,3 +7,11 @@ config ANDES45_PMA config ANDES_SBI bool "Andes SBI support" default n + +config ANDES_PMU + bool "Andes PMU extension (XAndesPMU) support" + default n + help + Andes PMU extension supports the event counter overflow + interrupt and mode filtering, similar to the standard + Sscofpmf and Smcntrpmf. diff --git a/platform/generic/andes/andes_pmu.c b/platform/generic/andes/andes_pmu.c new file mode 100644 index 00000000..4b2d45b0 --- /dev/null +++ b/platform/generic/andes/andes_pmu.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * andes_pmu.c - Andes PMU device callbacks and platform overrides + * + * Copyright (C) 2023 Andes Technology Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +static void andes_hw_counter_enable_irq(uint32_t ctr_idx) +{ + unsigned long mip_val; + + if (ctr_idx >= SBI_PMU_HW_CTR_MAX) + return; + + mip_val = csr_read(CSR_MIP); + if (!(mip_val & MIP_PMOVI)) + csr_clear(CSR_MCOUNTEROVF, BIT(ctr_idx)); + + csr_set(CSR_MCOUNTERINTEN, BIT(ctr_idx)); +} + +static void andes_hw_counter_disable_irq(uint32_t ctr_idx) +{ + csr_clear(CSR_MCOUNTERINTEN, BIT(ctr_idx)); +} + +static void andes_hw_counter_filter_mode(unsigned long flags, int ctr_idx) +{ + if (flags & SBI_PMU_CFG_FLAG_SET_UINH) + csr_set(CSR_MCOUNTERMASK_U, BIT(ctr_idx)); + else + csr_clear(CSR_MCOUNTERMASK_U, BIT(ctr_idx)); + + if (flags & SBI_PMU_CFG_FLAG_SET_SINH) + csr_set(CSR_MCOUNTERMASK_S, BIT(ctr_idx)); + else + csr_clear(CSR_MCOUNTERMASK_S, BIT(ctr_idx)); +} + +static struct sbi_pmu_device andes_pmu = { + .name = "andes_pmu", + .hw_counter_enable_irq = andes_hw_counter_enable_irq, + .hw_counter_disable_irq = andes_hw_counter_disable_irq, + /* + * We set delegation of supervisor local interrupts via + * 18th bit on mslideleg instead of mideleg, so leave + * hw_counter_irq_bit() callback unimplemented. + */ + .hw_counter_irq_bit = NULL, + .hw_counter_filter_mode = andes_hw_counter_filter_mode +}; + +int andes_pmu_extensions_init(const struct fdt_match *match, + struct sbi_hart_features *hfeatures) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + if (!has_andes_pmu()) + return 0; + + /* + * Don't expect both Andes PMU and standard Sscofpmf/Smcntrpmf, + * are supported as they serve the same purpose. + */ + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF) || + sbi_hart_has_extension(scratch, SBI_HART_EXT_SMCNTRPMF)) + return SBI_EINVAL; + sbi_hart_update_extension(scratch, SBI_HART_EXT_XANDESPMU, true); + + /* Inhibit all HPM counters in M-mode */ + csr_write(CSR_MCOUNTERMASK_M, 0xfffffffd); + /* Delegate counter overflow interrupt to S-mode */ + csr_write(CSR_MSLIDELEG, MIP_PMOVI); + + return 0; +} + +int andes_pmu_init(const struct fdt_match *match) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + void *fdt = fdt_get_address(); + int pmu_offset; + + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_XANDESPMU)) + sbi_pmu_set_device(&andes_pmu); + + /* + * Populate default mappings if device-tree doesn't + * provide a valid pmu node. + */ + pmu_offset = fdt_node_offset_by_compatible(fdt, -1, "riscv,pmu"); + if (pmu_offset < 0) + return (pmu_offset == -FDT_ERR_NOTFOUND) ? andes_pmu_setup() + : SBI_EFAIL; + + return 0; +} diff --git a/platform/generic/andes/objects.mk b/platform/generic/andes/objects.mk index e8f86ea0..6a8c66c2 100644 --- a/platform/generic/andes/objects.mk +++ b/platform/generic/andes/objects.mk @@ -7,3 +7,4 @@ platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o andes/sleep.o platform-objs-$(CONFIG_ANDES45_PMA) += andes/andes45-pma.o platform-objs-$(CONFIG_ANDES_SBI) += andes/andes_sbi.o +platform-objs-$(CONFIG_ANDES_PMU) += andes/andes_pmu.o diff --git a/platform/generic/include/andes/andes_hpm.h b/platform/generic/include/andes/andes_hpm.h new file mode 100644 index 00000000..b4d71b9d --- /dev/null +++ b/platform/generic/include/andes/andes_hpm.h @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Andes Technology Corporation + */ + +#ifndef _ANDES_HPM_H_ +#define _ANDES_HPM_H_ + +static inline int andes_pmu_setup(void) { return 0; } + +#endif /* _ANDES_HPM_H_ */ diff --git a/platform/generic/include/andes/andes_pmu.h b/platform/generic/include/andes/andes_pmu.h new file mode 100644 index 00000000..f3553246 --- /dev/null +++ b/platform/generic/include/andes/andes_pmu.h @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) Copyright (c) 2023 Andes Technology Corporation + */ + +#ifndef _RISCV_ANDES_PMU_H +#define _RISCV_ANDES_PMU_H + +#include +#include +#include + +#ifdef CONFIG_ANDES_PMU + +int andes_pmu_init(const struct fdt_match *match); +int andes_pmu_extensions_init(const struct fdt_match *match, + struct sbi_hart_features *hfeatures); + +#else + +static inline int andes_pmu_init(const struct fdt_match *match) +{ + return 0; +} +static inline int andes_pmu_extensions_init(const struct fdt_match *match, + struct sbi_hart_features *hfeatures) +{ + return 0; +} + +#endif /* CONFIG_ANDES_PMU */ + +#endif /* _RISCV_ANDES_PMU_H */