[PATCH v2 1/4] riscv: Extend cpufeature.c to detect vendor extensions
Yu-Chien Peter Lin
peterlin at andestech.com
Tue Jul 2 02:54:30 PDT 2024
Hi Charlie,
On Sun, Jun 09, 2024 at 09:34:14PM -0700, Charlie Jenkins wrote:
> Instead of grouping all vendor extensions into the same riscv_isa_ext
> that standard instructions use, create a struct
> "riscv_isa_vendor_ext_data_list" that allows each vendor to maintain
> their vendor extensions independently of the standard extensions.
> xandespmu is currently the only vendor extension so that is the only
> extension that is affected by this change.
>
> An additional benefit of this is that the extensions of each vendor can
> be conditionally enabled. A config RISCV_ISA_VENDOR_EXT_ANDES has been
> added to allow for that.
>
> Signed-off-by: Charlie Jenkins <charlie at rivosinc.com>
> Reviewed-by: Conor Dooley <conor.dooley at microchip.com>
> Reviewed-by: Andy Chiu <andy.chiu at sifive.com>
Andes PMU works as expected. Thanks for taking care of this.
Tested-by: Yu Chien Peter Lin <peterlin at andestech.com>
Reviewed-by: Yu Chien Peter Lin <peterlin at andestech.com>
> ---
> arch/riscv/Kconfig | 2 +
> arch/riscv/Kconfig.vendor | 19 ++++
> arch/riscv/errata/andes/errata.c | 3 +
> arch/riscv/errata/sifive/errata.c | 3 +
> arch/riscv/errata/thead/errata.c | 3 +
> arch/riscv/include/asm/cpufeature.h | 18 +++
> arch/riscv/include/asm/hwcap.h | 1 -
> arch/riscv/include/asm/vendor_extensions.h | 49 ++++++++
> arch/riscv/include/asm/vendor_extensions/andes.h | 19 ++++
> arch/riscv/kernel/Makefile | 2 +
> arch/riscv/kernel/cpufeature.c | 137 +++++++++++++++++------
> arch/riscv/kernel/vendor_extensions.c | 56 +++++++++
> arch/riscv/kernel/vendor_extensions/Makefile | 3 +
> arch/riscv/kernel/vendor_extensions/andes.c | 18 +++
> drivers/perf/riscv_pmu_sbi.c | 10 +-
> 15 files changed, 306 insertions(+), 37 deletions(-)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 0525ee2d63c7..4ae2bf4dd497 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -774,6 +774,8 @@ config RISCV_EFFICIENT_UNALIGNED_ACCESS
>
> endchoice
>
> +source "arch/riscv/Kconfig.vendor"
> +
> endmenu # "Platform type"
>
> menu "Kernel features"
> diff --git a/arch/riscv/Kconfig.vendor b/arch/riscv/Kconfig.vendor
> new file mode 100644
> index 000000000000..6f1cdd32ed29
> --- /dev/null
> +++ b/arch/riscv/Kconfig.vendor
> @@ -0,0 +1,19 @@
> +menu "Vendor extensions"
> +
> +config RISCV_ISA_VENDOR_EXT
> + bool
> +
> +menu "Andes"
> +config RISCV_ISA_VENDOR_EXT_ANDES
> + bool "Andes vendor extension support"
> + select RISCV_ISA_VENDOR_EXT
> + default y
> + help
> + Say N here if you want to disable all Andes vendor extension
> + support. This will cause any Andes vendor extensions that are
> + requested by hardware probing to be ignored.
> +
> + If you don't know what to do here, say Y.
> +endmenu
> +
> +endmenu
> diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c
> index f2708a9494a1..fc1a34faa5f3 100644
> --- a/arch/riscv/errata/andes/errata.c
> +++ b/arch/riscv/errata/andes/errata.c
> @@ -17,6 +17,7 @@
> #include <asm/processor.h>
> #include <asm/sbi.h>
> #include <asm/vendorid_list.h>
> +#include <asm/vendor_extensions.h>
>
> #define ANDES_AX45MP_MARCHID 0x8000000000008a45UL
> #define ANDES_AX45MP_MIMPID 0x500UL
> @@ -65,6 +66,8 @@ void __init_or_module andes_errata_patch_func(struct alt_entry *begin, struct al
> unsigned long archid, unsigned long impid,
> unsigned int stage)
> {
> + BUILD_BUG_ON(ERRATA_ANDES_NUMBER >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
> +
> if (stage == RISCV_ALTERNATIVES_BOOT)
> errata_probe_iocp(stage, archid, impid);
>
> diff --git a/arch/riscv/errata/sifive/errata.c b/arch/riscv/errata/sifive/errata.c
> index 716cfedad3a2..cea3b96ade11 100644
> --- a/arch/riscv/errata/sifive/errata.c
> +++ b/arch/riscv/errata/sifive/errata.c
> @@ -12,6 +12,7 @@
> #include <asm/alternative.h>
> #include <asm/vendorid_list.h>
> #include <asm/errata_list.h>
> +#include <asm/vendor_extensions.h>
>
> struct errata_info_t {
> char name[32];
> @@ -96,6 +97,8 @@ void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
> u32 cpu_apply_errata = 0;
> u32 tmp;
>
> + BUILD_BUG_ON(ERRATA_SIFIVE_NUMBER >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
> +
> if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
> return;
>
> diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c
> index bf6a0a6318ee..f5120e07c318 100644
> --- a/arch/riscv/errata/thead/errata.c
> +++ b/arch/riscv/errata/thead/errata.c
> @@ -18,6 +18,7 @@
> #include <asm/io.h>
> #include <asm/patch.h>
> #include <asm/vendorid_list.h>
> +#include <asm/vendor_extensions.h>
>
> #define CSR_TH_SXSTATUS 0x5c0
> #define SXSTATUS_MAEE _AC(0x200000, UL)
> @@ -166,6 +167,8 @@ void thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
> u32 tmp;
> void *oldptr, *altptr;
>
> + BUILD_BUG_ON(ERRATA_THEAD_NUMBER >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE);
> +
> for (alt = begin; alt < end; alt++) {
> if (alt->vendor_id != THEAD_VENDOR_ID)
> continue;
> diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
> index 347805446151..550d661dc78d 100644
> --- a/arch/riscv/include/asm/cpufeature.h
> +++ b/arch/riscv/include/asm/cpufeature.h
> @@ -33,6 +33,24 @@ extern struct riscv_isainfo hart_isa[NR_CPUS];
>
> void riscv_user_isa_enable(void);
>
> +#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \
> + .name = #_name, \
> + .property = #_name, \
> + .id = _id, \
> + .subset_ext_ids = _subset_exts, \
> + .subset_ext_size = _subset_exts_size \
> +}
> +
> +#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0)
> +
> +/* Used to declare pure "lasso" extension (Zk for instance) */
> +#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \
> + _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts))
> +
> +/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */
> +#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \
> + _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts))
> +
> #if defined(CONFIG_RISCV_MISALIGNED)
> bool check_unaligned_access_emulated_all_cpus(void);
> void unaligned_emulation_finish(void);
> diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
> index e17d0078a651..1f2d2599c655 100644
> --- a/arch/riscv/include/asm/hwcap.h
> +++ b/arch/riscv/include/asm/hwcap.h
> @@ -80,7 +80,6 @@
> #define RISCV_ISA_EXT_ZFA 71
> #define RISCV_ISA_EXT_ZTSO 72
> #define RISCV_ISA_EXT_ZACAS 73
> -#define RISCV_ISA_EXT_XANDESPMU 74
>
> #define RISCV_ISA_EXT_XLINUXENVCFG 127
>
> diff --git a/arch/riscv/include/asm/vendor_extensions.h b/arch/riscv/include/asm/vendor_extensions.h
> new file mode 100644
> index 000000000000..5fca550fc1f6
> --- /dev/null
> +++ b/arch/riscv/include/asm/vendor_extensions.h
> @@ -0,0 +1,49 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright 2024 Rivos, Inc
> + */
> +
> +#ifndef _ASM_VENDOR_EXTENSIONS_H
> +#define _ASM_VENDOR_EXTENSIONS_H
> +
> +#include <asm/cpufeature.h>
> +
> +#include <linux/array_size.h>
> +#include <linux/types.h>
> +
> +/*
> + * The extension keys of each vendor must be strictly less than this value.
> + */
> +#define RISCV_ISA_VENDOR_EXT_MAX 32
> +
> +struct riscv_isavendorinfo {
> + DECLARE_BITMAP(isa, RISCV_ISA_VENDOR_EXT_MAX);
> +};
> +
> +struct riscv_isa_vendor_ext_data_list {
> + bool is_initialized;
> + const size_t ext_data_count;
> + const struct riscv_isa_ext_data *ext_data;
> + struct riscv_isavendorinfo per_hart_isa_bitmap[NR_CPUS];
> + struct riscv_isavendorinfo all_harts_isa_bitmap;
> +};
> +
> +extern struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[];
> +
> +extern const size_t riscv_isa_vendor_ext_list_size;
> +
> +/*
> + * The alternatives need some way of distinguishing between vendor extensions
> + * and errata. Incrementing all of the vendor extension keys so they are at
> + * least 0x8000 accomplishes that.
> + */
> +#define RISCV_VENDOR_EXT_ALTERNATIVES_BASE 0x8000
> +
> +#define VENDOR_EXT_ALL_CPUS -1
> +
> +bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsigned int bit);
> +#define riscv_isa_vendor_extension_available(vendor, ext) \
> + __riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor, \
> + RISCV_ISA_VENDOR_EXT_##ext)
> +
> +#endif /* _ASM_VENDOR_EXTENSIONS_H */
> diff --git a/arch/riscv/include/asm/vendor_extensions/andes.h b/arch/riscv/include/asm/vendor_extensions/andes.h
> new file mode 100644
> index 000000000000..7bb2fc43438f
> --- /dev/null
> +++ b/arch/riscv/include/asm/vendor_extensions/andes.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_ANDES_H
> +#define _ASM_RISCV_VENDOR_EXTENSIONS_ANDES_H
> +
> +#include <asm/vendor_extensions.h>
> +
> +#include <linux/types.h>
> +
> +#define RISCV_ISA_VENDOR_EXT_XANDESPMU 0
> +
> +/*
> + * Extension keys should be strictly less than max.
> + * It is safe to increment this when necessary.
> + */
> +#define RISCV_ISA_VENDOR_EXT_MAX_ANDES 32
> +
> +extern struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_andes;
> +
> +#endif
> diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
> index 5b243d46f4b1..b0aea202273d 100644
> --- a/arch/riscv/kernel/Makefile
> +++ b/arch/riscv/kernel/Makefile
> @@ -58,6 +58,8 @@ obj-y += riscv_ksyms.o
> obj-y += stacktrace.o
> obj-y += cacheinfo.o
> obj-y += patch.o
> +obj-y += vendor_extensions.o
> +obj-y += vendor_extensions/
> obj-y += probes/
> obj-y += tests/
> obj-$(CONFIG_MMU) += vdso.o vdso/
> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> index 5ef48cb20ee1..f2c24820700b 100644
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c
> @@ -24,6 +24,7 @@
> #include <asm/processor.h>
> #include <asm/sbi.h>
> #include <asm/vector.h>
> +#include <asm/vendor_extensions.h>
>
> #define NUM_ALPHA_EXTS ('z' - 'a' + 1)
>
> @@ -100,24 +101,6 @@ static bool riscv_isa_extension_check(int id)
> return true;
> }
>
> -#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size) { \
> - .name = #_name, \
> - .property = #_name, \
> - .id = _id, \
> - .subset_ext_ids = _subset_exts, \
> - .subset_ext_size = _subset_exts_size \
> -}
> -
> -#define __RISCV_ISA_EXT_DATA(_name, _id) _RISCV_ISA_EXT_DATA(_name, _id, NULL, 0)
> -
> -/* Used to declare pure "lasso" extension (Zk for instance) */
> -#define __RISCV_ISA_EXT_BUNDLE(_name, _bundled_exts) \
> - _RISCV_ISA_EXT_DATA(_name, RISCV_ISA_EXT_INVALID, _bundled_exts, ARRAY_SIZE(_bundled_exts))
> -
> -/* Used to declare extensions that are a superset of other extensions (Zvbb for instance) */
> -#define __RISCV_ISA_EXT_SUPERSET(_name, _id, _sub_exts) \
> - _RISCV_ISA_EXT_DATA(_name, _id, _sub_exts, ARRAY_SIZE(_sub_exts))
> -
> static const unsigned int riscv_zk_bundled_exts[] = {
> RISCV_ISA_EXT_ZBKB,
> RISCV_ISA_EXT_ZBKC,
> @@ -304,7 +287,6 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
> __RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
> __RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
> __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
> - __RISCV_ISA_EXT_DATA(xandespmu, RISCV_ISA_EXT_XANDESPMU),
> };
>
> const size_t riscv_isa_ext_count = ARRAY_SIZE(riscv_isa_ext);
> @@ -351,6 +333,21 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc
> bool ext_long = false, ext_err = false;
>
> switch (*ext) {
> + case 'x':
> + case 'X':
> + if (acpi_disabled)
> + pr_warn_once("Vendor extensions are ignored in riscv,isa. Use riscv,isa-extensions instead.");
> + /*
> + * To skip an extension, we find its end.
> + * As multi-letter extensions must be split from other multi-letter
> + * extensions with an "_", the end of a multi-letter extension will
> + * either be the null character or the "_" at the start of the next
> + * multi-letter extension.
> + */
> + for (; *isa && *isa != '_'; ++isa)
> + ;
> + ext_err = true;
> + break;
> case 's':
> /*
> * Workaround for invalid single-letter 's' & 'u' (QEMU).
> @@ -366,8 +363,6 @@ static void __init riscv_parse_isa_string(unsigned long *this_hwcap, struct risc
> }
> fallthrough;
> case 'S':
> - case 'x':
> - case 'X':
> case 'z':
> case 'Z':
> /*
> @@ -574,6 +569,61 @@ static void __init riscv_fill_hwcap_from_isa_string(unsigned long *isa2hwcap)
> acpi_put_table((struct acpi_table_header *)rhct);
> }
>
> +static void __init riscv_fill_cpu_vendor_ext(struct device_node *cpu_node, int cpu)
> +{
> + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT))
> + return;
> +
> + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) {
> + struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i];
> +
> + for (int j = 0; j < ext_list->ext_data_count; j++) {
> + const struct riscv_isa_ext_data ext = ext_list->ext_data[j];
> + struct riscv_isavendorinfo *isavendorinfo = &ext_list->per_hart_isa_bitmap[cpu];
> +
> + if (of_property_match_string(cpu_node, "riscv,isa-extensions",
> + ext.property) < 0)
> + continue;
> +
> + /*
> + * Assume that subset extensions are all members of the
> + * same vendor.
> + */
> + if (ext.subset_ext_size)
> + for (int k = 0; k < ext.subset_ext_size; k++)
> + set_bit(ext.subset_ext_ids[k], isavendorinfo->isa);
> +
> + set_bit(ext.id, isavendorinfo->isa);
> + }
> + }
> +}
> +
> +/*
> + * Populate all_harts_isa_bitmap for each vendor with all of the extensions that
> + * are shared across CPUs for that vendor.
> + */
> +static void __init riscv_fill_vendor_ext_list(int cpu)
> +{
> + if (!IS_ENABLED(CONFIG_RISCV_ISA_VENDOR_EXT))
> + return;
> +
> + for (int i = 0; i < riscv_isa_vendor_ext_list_size; i++) {
> + struct riscv_isa_vendor_ext_data_list *ext_list = riscv_isa_vendor_ext_list[i];
> +
> + if (!ext_list->is_initialized) {
> + bitmap_copy(ext_list->all_harts_isa_bitmap.isa,
> + ext_list->per_hart_isa_bitmap[cpu].isa,
> + RISCV_ISA_VENDOR_EXT_MAX);
> + ext_list->is_initialized = true;
> + } else {
> + bitmap_and(ext_list->all_harts_isa_bitmap.isa,
> + ext_list->all_harts_isa_bitmap.isa,
> + ext_list->per_hart_isa_bitmap[cpu].isa,
> + RISCV_ISA_VENDOR_EXT_MAX);
> + }
> + }
> +}
> +
> static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap)
> {
> unsigned int cpu;
> @@ -617,6 +667,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap)
> }
> }
>
> + riscv_fill_cpu_vendor_ext(cpu_node, cpu);
> +
> of_node_put(cpu_node);
>
> /*
> @@ -632,6 +684,8 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap)
> bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX);
> else
> bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX);
> +
> + riscv_fill_vendor_ext_list(cpu);
> }
>
> if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX))
> @@ -768,28 +822,45 @@ void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
> {
> struct alt_entry *alt;
> void *oldptr, *altptr;
> - u16 id, value;
> + u16 id, value, vendor;
>
> if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
> return;
>
> for (alt = begin; alt < end; alt++) {
> - if (alt->vendor_id != 0)
> - continue;
> -
> id = PATCH_ID_CPUFEATURE_ID(alt->patch_id);
> + vendor = PATCH_ID_CPUFEATURE_ID(alt->vendor_id);
>
> - if (id >= RISCV_ISA_EXT_MAX) {
> - WARN(1, "This extension id:%d is not in ISA extension list", id);
> - continue;
> - }
> + /*
> + * Any alternative with a patch_id that is less than
> + * RISCV_ISA_EXT_MAX is interpreted as a standard extension.
> + *
> + * Any alternative with patch_id that is greater than or equal
> + * to RISCV_VENDOR_EXT_ALTERNATIVES_BASE is interpreted as a
> + * vendor extension.
> + */
> + if (id < RISCV_ISA_EXT_MAX) {
> + /*
> + * This patch should be treated as errata so skip
> + * processing here.
> + */
> + if (alt->vendor_id != 0)
> + continue;
>
> - if (!__riscv_isa_extension_available(NULL, id))
> - continue;
> + if (!__riscv_isa_extension_available(NULL, id))
> + continue;
>
> - value = PATCH_ID_CPUFEATURE_VALUE(alt->patch_id);
> - if (!riscv_cpufeature_patch_check(id, value))
> + value = PATCH_ID_CPUFEATURE_VALUE(alt->patch_id);
> + if (!riscv_cpufeature_patch_check(id, value))
> + continue;
> + } else if (id >= RISCV_VENDOR_EXT_ALTERNATIVES_BASE) {
> + if (!__riscv_isa_vendor_extension_available(VENDOR_EXT_ALL_CPUS, vendor,
> + id - RISCV_VENDOR_EXT_ALTERNATIVES_BASE))
> + continue;
> + } else {
> + WARN(1, "This extension id:%d is not in ISA extension list", id);
> continue;
> + }
>
> oldptr = ALT_OLD_PTR(alt);
> altptr = ALT_ALT_PTR(alt);
> diff --git a/arch/riscv/kernel/vendor_extensions.c b/arch/riscv/kernel/vendor_extensions.c
> new file mode 100644
> index 000000000000..b6c1e7b5d34b
> --- /dev/null
> +++ b/arch/riscv/kernel/vendor_extensions.c
> @@ -0,0 +1,56 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2024 Rivos, Inc
> + */
> +
> +#include <asm/vendorid_list.h>
> +#include <asm/vendor_extensions.h>
> +#include <asm/vendor_extensions/andes.h>
> +
> +#include <linux/array_size.h>
> +#include <linux/types.h>
> +
> +struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = {
> +#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_ANDES
> + &riscv_isa_vendor_ext_list_andes,
> +#endif
> +};
> +
> +const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list);
> +
> +/**
> + * __riscv_isa_vendor_extension_available() - Check whether given vendor
> + * extension is available or not.
> + *
> + * @cpu: check if extension is available on this cpu
> + * @vendor: vendor that the extension is a member of
> + * @bit: bit position of the desired extension
> + * Return: true or false
> + *
> + * NOTE: When cpu is -1, will check if extension is available on all cpus
> + */
> +bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsigned int bit)
> +{
> + struct riscv_isavendorinfo *bmap;
> + struct riscv_isavendorinfo *cpu_bmap;
> +
> + switch (vendor) {
> + #ifdef CONFIG_RISCV_ISA_VENDOR_EXT_ANDES
> + case ANDES_VENDOR_ID:
> + bmap = &riscv_isa_vendor_ext_list_andes.all_harts_isa_bitmap;
> + cpu_bmap = &riscv_isa_vendor_ext_list_andes.per_hart_isa_bitmap[cpu];
> + break;
> + #endif
> + default:
> + return false;
> + }
> +
> + if (cpu != -1)
> + bmap = &cpu_bmap[cpu];
> +
> + if (bit >= RISCV_ISA_VENDOR_EXT_MAX)
> + return false;
> +
> + return test_bit(bit, bmap->isa) ? true : false;
> +}
> +EXPORT_SYMBOL_GPL(__riscv_isa_vendor_extension_available);
> diff --git a/arch/riscv/kernel/vendor_extensions/Makefile b/arch/riscv/kernel/vendor_extensions/Makefile
> new file mode 100644
> index 000000000000..6a61aed944f1
> --- /dev/null
> +++ b/arch/riscv/kernel/vendor_extensions/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_ANDES) += andes.o
> diff --git a/arch/riscv/kernel/vendor_extensions/andes.c b/arch/riscv/kernel/vendor_extensions/andes.c
> new file mode 100644
> index 000000000000..ec688c88456a
> --- /dev/null
> +++ b/arch/riscv/kernel/vendor_extensions/andes.c
> @@ -0,0 +1,18 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <asm/cpufeature.h>
> +#include <asm/vendor_extensions.h>
> +#include <asm/vendor_extensions/andes.h>
> +
> +#include <linux/array_size.h>
> +#include <linux/types.h>
> +
> +/* All Andes vendor extensions supported in Linux */
> +const struct riscv_isa_ext_data riscv_isa_vendor_ext_andes[] = {
> + __RISCV_ISA_EXT_DATA(xandespmu, RISCV_ISA_VENDOR_EXT_XANDESPMU),
> +};
> +
> +struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_andes = {
> + .ext_data_count = ARRAY_SIZE(riscv_isa_vendor_ext_andes),
> + .ext_data = riscv_isa_vendor_ext_andes,
> +};
> diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
> index a2e4005e1fd0..02719e0c6368 100644
> --- a/drivers/perf/riscv_pmu_sbi.c
> +++ b/drivers/perf/riscv_pmu_sbi.c
> @@ -24,6 +24,8 @@
> #include <asm/errata_list.h>
> #include <asm/sbi.h>
> #include <asm/cpufeature.h>
> +#include <asm/vendor_extensions.h>
> +#include <asm/vendor_extensions/andes.h>
>
> #define ALT_SBI_PMU_OVERFLOW(__ovl) \
> asm volatile(ALTERNATIVE_2( \
> @@ -32,7 +34,8 @@ asm volatile(ALTERNATIVE_2( \
> THEAD_VENDOR_ID, ERRATA_THEAD_PMU, \
> CONFIG_ERRATA_THEAD_PMU, \
> "csrr %0, " __stringify(ANDES_CSR_SCOUNTEROF), \
> - 0, RISCV_ISA_EXT_XANDESPMU, \
> + ANDES_VENDOR_ID, \
> + RISCV_ISA_VENDOR_EXT_XANDESPMU + RISCV_VENDOR_EXT_ALTERNATIVES_BASE, \
> CONFIG_ANDES_CUSTOM_PMU) \
> : "=r" (__ovl) : \
> : "memory")
> @@ -41,7 +44,8 @@ asm volatile(ALTERNATIVE_2( \
> asm volatile(ALTERNATIVE( \
> "csrc " __stringify(CSR_IP) ", %0\n\t", \
> "csrc " __stringify(ANDES_CSR_SLIP) ", %0\n\t", \
> - 0, RISCV_ISA_EXT_XANDESPMU, \
> + ANDES_VENDOR_ID, \
> + RISCV_ISA_VENDOR_EXT_XANDESPMU + RISCV_VENDOR_EXT_ALTERNATIVES_BASE, \
> CONFIG_ANDES_CUSTOM_PMU) \
> : : "r"(__irq_mask) \
> : "memory")
> @@ -1060,7 +1064,7 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde
> riscv_cached_mimpid(0) == 0) {
> riscv_pmu_irq_num = THEAD_C9XX_RV_IRQ_PMU;
> riscv_pmu_use_irq = true;
> - } else if (riscv_isa_extension_available(NULL, XANDESPMU) &&
> + } else if (riscv_isa_vendor_extension_available(ANDES_VENDOR_ID, XANDESPMU) &&
> IS_ENABLED(CONFIG_ANDES_CUSTOM_PMU)) {
> riscv_pmu_irq_num = ANDES_SLI_CAUSE_BASE + ANDES_RV_IRQ_PMOVI;
> riscv_pmu_use_irq = true;
More information about the linux-riscv
mailing list