RISC-V: Add Sstc extension support
authorPalmer Dabbelt <palmer@rivosinc.com>
Thu, 11 Aug 2022 21:41:52 +0000 (14:41 -0700)
committerPalmer Dabbelt <palmer@rivosinc.com>
Thu, 11 Aug 2022 21:41:52 +0000 (14:41 -0700)
This series implements Sstc extension support which was ratified
recently.  Before the Sstc extension, an SBI call is necessary to
generate timer interrupts as only M-mode have access to the timecompare
registers. Thus, there is significant latency to generate timer
interrupts at kernel.  For virtualized enviornments, its even worse as
the KVM handles the SBI call and uses a software timer to emulate the
timecomapre register.

Sstc extension solves both these problems by defining a
stimecmp/vstimecmp at supervisor (host/guest) level. It allows kernel to
program a timer and recieve interrupt without supervisor execution
enviornment (M-mode/HS mode) intervention.

* palmer/riscv-sstc:
  RISC-V: Prefer sstc extension if available
  RISC-V: Enable sstc extension parsing from DT
  RISC-V: Add SSTC extension CSR details

arch/riscv/include/asm/csr.h
arch/riscv/include/asm/hwcap.h
arch/riscv/kernel/cpu.c
arch/riscv/kernel/cpufeature.c
drivers/clocksource/timer-riscv.c

index 17516af..0e571f6 100644 (file)
 #define CSR_SIP                        0x144
 #define CSR_SATP               0x180
 
+#define CSR_STIMECMP           0x14D
+#define CSR_STIMECMPH          0x15D
+
 #define CSR_VSSTATUS           0x200
 #define CSR_VSIE               0x204
 #define CSR_VSTVEC             0x205
 #define CSR_VSTVAL             0x243
 #define CSR_VSIP               0x244
 #define CSR_VSATP              0x280
+#define CSR_VSTIMECMP          0x24D
+#define CSR_VSTIMECMPH         0x25D
 
 #define CSR_HSTATUS            0x600
 #define CSR_HEDELEG            0x602
index 3c8a5ca..6f59ec6 100644 (file)
@@ -57,6 +57,7 @@ enum riscv_isa_ext_id {
        RISCV_ISA_EXT_SVPBMT,
        RISCV_ISA_EXT_ZICBOM,
        RISCV_ISA_EXT_ZIHINTPAUSE,
+       RISCV_ISA_EXT_SSTC,
        RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX,
 };
 
index a77c380..0be8a24 100644 (file)
@@ -95,6 +95,7 @@ static struct riscv_isa_ext_data isa_ext_arr[] = {
        __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
        __RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM),
        __RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE),
+       __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
        __RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX),
 };
 
index c233fbc..553d755 100644 (file)
@@ -203,6 +203,7 @@ void __init riscv_fill_hwcap(void)
                                SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT);
                                SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM);
                                SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
+                               SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC);
                        }
 #undef SET_ISA_EXT_MAP
                }
index e460df7..969a552 100644 (file)
@@ -7,6 +7,9 @@
  * either be read from the "time" and "timeh" CSRs, and can use the SBI to
  * setup events, or directly accessed using MMIO registers.
  */
+
+#define pr_fmt(fmt) "riscv-timer: " fmt
+
 #include <linux/clocksource.h>
 #include <linux/clockchips.h>
 #include <linux/cpu.h>
 #include <linux/of_irq.h>
 #include <clocksource/timer-riscv.h>
 #include <asm/smp.h>
+#include <asm/hwcap.h>
 #include <asm/sbi.h>
 #include <asm/timex.h>
 
+static DEFINE_STATIC_KEY_FALSE(riscv_sstc_available);
+
 static int riscv_clock_next_event(unsigned long delta,
                struct clock_event_device *ce)
 {
+       u64 next_tval = get_cycles64() + delta;
+
        csr_set(CSR_IE, IE_TIE);
-       sbi_set_timer(get_cycles64() + delta);
+       if (static_branch_likely(&riscv_sstc_available)) {
+#if defined(CONFIG_32BIT)
+               csr_write(CSR_STIMECMP, next_tval & 0xFFFFFFFF);
+               csr_write(CSR_STIMECMPH, next_tval >> 32);
+#else
+               csr_write(CSR_STIMECMP, next_tval);
+#endif
+       } else
+               sbi_set_timer(next_tval);
+
        return 0;
 }
 
@@ -166,6 +183,12 @@ static int __init riscv_timer_init_dt(struct device_node *n)
        if (error)
                pr_err("cpu hp setup state failed for RISCV timer [%d]\n",
                       error);
+
+       if (riscv_isa_extension_available(NULL, SSTC)) {
+               pr_info("Timer interrupt in S-mode is available via sstc extension\n");
+               static_branch_enable(&riscv_sstc_available);
+       }
+
        return error;
 }