[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAK9=C2VWSpREd0Hqg3TuLcVxG1=Ddfqn9ouNcowxHdCfJWgmJg@mail.gmail.com>
Date: Sat, 16 Aug 2025 14:12:23 +0530
From: Anup Patel <apatel@...tanamicro.com>
To: Vivian Wang <wangruikang@...as.ac.cn>
Cc: Sunil V L <sunilvl@...tanamicro.com>, "Rafael J . Wysocki" <rafael@...nel.org>,
Palmer Dabbelt <palmer@...belt.com>, Paul Walmsley <paul.walmsley@...ive.com>,
Alexandre Ghiti <alex@...ti.fr>, Len Brown <lenb@...nel.org>, Atish Patra <atish.patra@...ux.dev>,
Andrew Jones <ajones@...tanamicro.com>, Anup Patel <anup@...infault.org>,
Will Deacon <will@...nel.org>, Mark Rutland <mark.rutland@....com>, linux-acpi@...r.kernel.org,
linux-riscv@...ts.infradead.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH 2/2] RISC-V: Add common csr_read_num() and csr_write_num() functions
On Sat, Aug 16, 2025 at 9:54 AM Vivian Wang <wangruikang@...as.ac.cn> wrote:
>
>
> On 8/16/25 00:14, Anup Patel wrote:
> > In RISC-V, there is no CSR read/write instruction which takes CSR
> > number via register so add common csr_read_num() and csr_write_num()
> > functions which allow accessing certain CSRs by passing CSR number
> > as parameter. These common functions will be first used by the
> > ACPI CPPC driver and RISC-V PMU driver.
> >
> > Signed-off-by: Anup Patel <apatel@...tanamicro.com>
> > ---
> > arch/riscv/include/asm/csr.h | 3 +
> > arch/riscv/kernel/Makefile | 1 +
> > arch/riscv/kernel/csr.c | 177 +++++++++++++++++++++++++++++++++++
> > drivers/acpi/riscv/cppc.c | 17 ++--
> > drivers/perf/riscv_pmu.c | 43 +--------
> > 5 files changed, 189 insertions(+), 52 deletions(-)
> > create mode 100644 arch/riscv/kernel/csr.c
> >
> > diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
> > index 6fed42e37705..1540626b3540 100644
> > --- a/arch/riscv/include/asm/csr.h
> > +++ b/arch/riscv/include/asm/csr.h
> > @@ -575,6 +575,9 @@
> > : "memory"); \
> > })
> >
> > +extern unsigned long csr_read_num(unsigned long csr_num, int *out_err);
> > +extern void csr_write_num(unsigned long csr_num, unsigned long val, int *out_err);
> > +
> > #endif /* __ASSEMBLY__ */
> >
> > #endif /* _ASM_RISCV_CSR_H */
> > diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
> > index c7b542573407..0a75e20bde18 100644
> > --- a/arch/riscv/kernel/Makefile
> > +++ b/arch/riscv/kernel/Makefile
> > @@ -50,6 +50,7 @@ obj-y += soc.o
> > obj-$(CONFIG_RISCV_ALTERNATIVE) += alternative.o
> > obj-y += cpu.o
> > obj-y += cpufeature.o
> > +obj-y += csr.o
> > obj-y += entry.o
> > obj-y += irq.o
> > obj-y += process.o
> > diff --git a/arch/riscv/kernel/csr.c b/arch/riscv/kernel/csr.c
> > new file mode 100644
> > index 000000000000..f7de45bb597c
> > --- /dev/null
> > +++ b/arch/riscv/kernel/csr.c
> > @@ -0,0 +1,177 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2025 Ventana Micro Systems Inc.
> > + */
> > +
> > +#define pr_fmt(fmt) "riscv: " fmt
> > +#include <linux/err.h>
> > +#include <linux/export.h>
> > +#include <linux/printk.h>
> > +#include <linux/types.h>
> > +#include <asm/csr.h>
> > +
> > +#define CSR_CUSTOM0_U_RW_BASE 0x800
> > +#define CSR_CUSTOM0_U_RW_COUNT 0x100
> > +
> > +#define CSR_CUSTOM1_U_RO_BASE 0xCC0
> > +#define CSR_CUSTOM1_U_RO_COUNT 0x040
> > +
> > +#define CSR_CUSTOM2_S_RW_BASE 0x5C0
> > +#define CSR_CUSTOM2_S_RW_COUNT 0x040
> > +
> > +#define CSR_CUSTOM3_S_RW_BASE 0x9C0
> > +#define CSR_CUSTOM3_S_RW_COUNT 0x040
> > +
> > +#define CSR_CUSTOM4_S_RO_BASE 0xDC0
> > +#define CSR_CUSTOM4_S_RO_COUNT 0x040
> > +
> > +#define CSR_CUSTOM5_HS_RW_BASE 0x6C0
> > +#define CSR_CUSTOM5_HS_RW_COUNT 0x040
> > +
> > +#define CSR_CUSTOM6_HS_RW_BASE 0xAC0
> > +#define CSR_CUSTOM6_HS_RW_COUNT 0x040
> > +
> > +#define CSR_CUSTOM7_HS_RO_BASE 0xEC0
> > +#define CSR_CUSTOM7_HS_RO_COUNT 0x040
> > +
> > +#define CSR_CUSTOM8_M_RW_BASE 0x7C0
> > +#define CSR_CUSTOM8_M_RW_COUNT 0x040
> > +
> > +#define CSR_CUSTOM9_M_RW_BASE 0xBC0
> > +#define CSR_CUSTOM9_M_RW_COUNT 0x040
> > +
> > +#define CSR_CUSTOM10_M_RO_BASE 0xFC0
> > +#define CSR_CUSTOM10_M_RO_COUNT 0x040
> > +
> > +unsigned long csr_read_num(unsigned long csr_num, int *out_err)
> > +{
> > +#define switchcase_csr_read(__csr_num) \
> > + case (__csr_num): \
> > + return csr_read(__csr_num)
> > +#define switchcase_csr_read_2(__csr_num) \
> > + switchcase_csr_read(__csr_num + 0); \
> > + switchcase_csr_read(__csr_num + 1)
> > +#define switchcase_csr_read_4(__csr_num) \
> > + switchcase_csr_read_2(__csr_num + 0); \
> > + switchcase_csr_read_2(__csr_num + 2)
> > +#define switchcase_csr_read_8(__csr_num) \
> > + switchcase_csr_read_4(__csr_num + 0); \
> > + switchcase_csr_read_4(__csr_num + 4)
> > +#define switchcase_csr_read_16(__csr_num) \
> > + switchcase_csr_read_8(__csr_num + 0); \
> > + switchcase_csr_read_8(__csr_num + 8)
> > +#define switchcase_csr_read_32(__csr_num) \
> > + switchcase_csr_read_16(__csr_num + 0); \
> > + switchcase_csr_read_16(__csr_num + 16)
> > +#define switchcase_csr_read_64(__csr_num) \
> > + switchcase_csr_read_32(__csr_num + 0); \
> > + switchcase_csr_read_32(__csr_num + 32)
> > +#define switchcase_csr_read_128(__csr_num) \
> > + switchcase_csr_read_64(__csr_num + 0); \
> > + switchcase_csr_read_64(__csr_num + 64)
> > +#define switchcase_csr_read_256(__csr_num) \
> > + switchcase_csr_read_128(__csr_num + 0); \
> > + switchcase_csr_read_128(__csr_num + 128)
> > +
>
> That's... a bit horrendous.
>
> Since we know that each inner case is quite simple, can we just do our
> own jump table for that? Each case can be one csrr/csrw and one jal
> (possibly pad to 8 bytes), and we can just jump to
> label + (csr_num - range_start) * 8. See bottom of this message for an
> untested prototype.
>
> Vivian "dramforever" Wang
>
> UNTESTED prototype:
>
> #define CSR_CYCLE 0xc00
> #define CSR_CYCLEH 0xc80
>
> /* Force expand argument if macro */
> #define str(_x) #_x
>
> unsigned long csr_read_num(long csr_num)
> {
> unsigned long res, tmp;
>
> /*
> * Generate a jump table for each range. Each (inner) case is 8 bytes of
> * code, a csrr instruction and a jump, possibly padded, so we just jump
> * to label_1f + (csr_num - _start) * 8
> */
> #define csr_read_case_range(_start, _size) \
> case (_start) ... ((_start) + (_size) - 1): \
> asm volatile ( \
> " lla %[tmp], 1f\n" \
> " add %[tmp], %[tmp], %[offset]\n" \
> " jr %[tmp]\n" \
> "1:\n" \
> " .rept (" str(_size) ")\n" \
> " csrr %[res], (" str(_start) " + \\+)\n" \
> " j 2f\n" \
> " .balign 4\n" \
> " .endr\n" \
> "2:\n" \
> : [tmp] "=&r"(tmp), [res] "=r"(res) \
> : [offset] "r"((csr_num - _start) * 8) \
> : ); \
> break; \
>
>
> switch (csr_num) {
> csr_read_case_range(CSR_CYCLE, 4)
> csr_read_case_range(CSR_CYCLEH, 4)
>
> /* ... other cases */
>
> default:
> /* Error handling */
> }
>
> #undef csr_read_case_range
>
> return res;
> }
Clearly you have trust issues with compiler switch-case optimizations
and your proposed solution looks horrendous to me.
Your proposed solution is effectively a nested switch case where
there is a higher-level jump-table generated by the compiler which
selects a range case and within each range case you have a hand
assembled jump table. If you analyse the effective number of
instructions involved in accessing an CSR then it will be much
more than the current approach.
The current approach relies on a compiler optimized jump table
where each switch-case is just two instructions (12 bytes) for
both csr_read_num() and csr_write_num().
Regards,
Anup
Powered by blists - more mailing lists