]> Git Repo - J-linux.git/commitdiff
RISC-V: Add Sstc extension support
authorPalmer Dabbelt <[email protected]>
Thu, 11 Aug 2022 21:41:52 +0000 (14:41 -0700)
committerPalmer Dabbelt <[email protected]>
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

1  2 
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 17516afc389a9e4869cfd295c9d57cf4d04dbe58,34f4eaf326c45e9078061fd4a6853a23c91716cd..0e571f6483d92842aa8e8a78796813baeb97ca83
                                 (_AC(1, UL) << IRQ_S_TIMER) | \
                                 (_AC(1, UL) << IRQ_S_EXT))
  
 +/* xENVCFG flags */
 +#define ENVCFG_STCE                   (_AC(1, ULL) << 63)
 +#define ENVCFG_PBMTE                  (_AC(1, ULL) << 62)
 +#define ENVCFG_CBZE                   (_AC(1, UL) << 7)
 +#define ENVCFG_CBCFE                  (_AC(1, UL) << 6)
 +#define ENVCFG_CBIE_SHIFT             4
 +#define ENVCFG_CBIE                   (_AC(0x3, UL) << ENVCFG_CBIE_SHIFT)
 +#define ENVCFG_CBIE_ILL                       _AC(0x0, UL)
 +#define ENVCFG_CBIE_FLUSH             _AC(0x1, UL)
 +#define ENVCFG_CBIE_INV                       _AC(0x3, UL)
 +#define ENVCFG_FIOM                   _AC(0x1, UL)
 +
  /* symbolic CSR names: */
  #define CSR_CYCLE             0xc00
  #define CSR_TIME              0xc01
  #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
  #define CSR_HTIMEDELTA                0x605
  #define CSR_HCOUNTEREN                0x606
  #define CSR_HGEIE             0x607
 +#define CSR_HENVCFG           0x60a
  #define CSR_HTIMEDELTAH               0x615
 +#define CSR_HENVCFGH          0x61a
  #define CSR_HTVAL             0x643
  #define CSR_HIP                       0x644
  #define CSR_HVIP              0x645
  #define CSR_MISA              0x301
  #define CSR_MIE                       0x304
  #define CSR_MTVEC             0x305
 +#define CSR_MENVCFG           0x30a
 +#define CSR_MENVCFGH          0x31a
  #define CSR_MSCRATCH          0x340
  #define CSR_MEPC              0x341
  #define CSR_MCAUSE            0x342
index 3c8a5ca95c7253724271831cad78b6a48d41354c,b186fff75198ceaf16de8544c1e793c52ee5e882..6f59ec64175efd2781635c01e34bb26f80d689fb
@@@ -8,12 -8,10 +8,12 @@@
  #ifndef _ASM_RISCV_HWCAP_H
  #define _ASM_RISCV_HWCAP_H
  
 +#include <asm/errno.h>
  #include <linux/bits.h>
  #include <uapi/asm/hwcap.h>
  
  #ifndef __ASSEMBLY__
 +#include <linux/jump_label.h>
  /*
   * This yields a mask that user programs can use to figure out what
   * instruction set this cpu supports.
@@@ -55,22 -53,10 +55,23 @@@ extern unsigned long elf_hwcap
  enum riscv_isa_ext_id {
        RISCV_ISA_EXT_SSCOFPMF = RISCV_ISA_EXT_BASE,
        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,
  };
  
 +/*
 + * This enum represents the logical ID for each RISC-V ISA extension static
 + * keys. We can use static key to optimize code path if some ISA extensions
 + * are available.
 + */
 +enum riscv_isa_ext_key {
 +      RISCV_ISA_EXT_KEY_FPU,          /* For 'F' and 'D' */
 +      RISCV_ISA_EXT_KEY_ZIHINTPAUSE,
 +      RISCV_ISA_EXT_KEY_MAX,
 +};
 +
  struct riscv_isa_ext_data {
        /* Name of the extension displayed to userspace via /proc/cpuinfo */
        char uprop[RISCV_ISA_EXT_NAME_LEN_MAX];
        unsigned int isa_ext_id;
  };
  
 +extern struct static_key_false riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_MAX];
 +
 +static __always_inline int riscv_isa_ext2key(int num)
 +{
 +      switch (num) {
 +      case RISCV_ISA_EXT_f:
 +              return RISCV_ISA_EXT_KEY_FPU;
 +      case RISCV_ISA_EXT_d:
 +              return RISCV_ISA_EXT_KEY_FPU;
 +      case RISCV_ISA_EXT_ZIHINTPAUSE:
 +              return RISCV_ISA_EXT_KEY_ZIHINTPAUSE;
 +      default:
 +              return -EINVAL;
 +      }
 +}
 +
  unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
  
  #define riscv_isa_extension_mask(ext) BIT_MASK(RISCV_ISA_EXT_##ext)
diff --combined arch/riscv/kernel/cpu.c
index a77c380703c5c0c4012ba45b6e610fbc392e9e60,0016d9337fe09b7ff173da4906c494b5283173af..0be8a2403212d4a2b9a8a30af78437bc5b597b5f
   * Returns the hart ID of the given device tree node, or -ENODEV if the node
   * isn't an enabled and valid RISC-V hart node.
   */
 -int riscv_of_processor_hartid(struct device_node *node)
 +int riscv_of_processor_hartid(struct device_node *node, unsigned long *hart)
  {
        const char *isa;
 -      u32 hart;
  
        if (!of_device_is_compatible(node, "riscv")) {
                pr_warn("Found incompatible CPU\n");
                return -ENODEV;
        }
  
 -      hart = of_get_cpu_hwid(node, 0);
 -      if (hart == ~0U) {
 +      *hart = (unsigned long) of_get_cpu_hwid(node, 0);
 +      if (*hart == ~0UL) {
                pr_warn("Found CPU without hart ID\n");
                return -ENODEV;
        }
  
        if (!of_device_is_available(node)) {
 -              pr_info("CPU with hartid=%d is not available\n", hart);
 +              pr_info("CPU with hartid=%lu is not available\n", *hart);
                return -ENODEV;
        }
  
        if (of_property_read_string(node, "riscv,isa", &isa)) {
 -              pr_warn("CPU with hartid=%d has no \"riscv,isa\" property\n", hart);
 +              pr_warn("CPU with hartid=%lu has no \"riscv,isa\" property\n", *hart);
                return -ENODEV;
        }
        if (isa[0] != 'r' || isa[1] != 'v') {
 -              pr_warn("CPU with hartid=%d has an invalid ISA of \"%s\"\n", hart, isa);
 +              pr_warn("CPU with hartid=%lu has an invalid ISA of \"%s\"\n", *hart, isa);
                return -ENODEV;
        }
  
 -      return hart;
 +      return 0;
  }
  
  /*
   * To achieve this, we walk up the DT tree until we find an active
   * RISC-V core (HART) node and extract the cpuid from it.
   */
 -int riscv_of_parent_hartid(struct device_node *node)
 +int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid)
  {
 +      int rc;
 +
        for (; node; node = node->parent) {
 -              if (of_device_is_compatible(node, "riscv"))
 -                      return riscv_of_processor_hartid(node);
 +              if (of_device_is_compatible(node, "riscv")) {
 +                      rc = riscv_of_processor_hartid(node, hartid);
 +                      if (!rc)
 +                              return 0;
 +              }
        }
  
        return -1;
@@@ -93,8 -89,7 +93,9 @@@
  static struct riscv_isa_ext_data isa_ext_arr[] = {
        __RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
        __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 c233fbc5b873bd81aee81063472715b1a99ab1ce,d1d83cd9fd4bb790393ac331835909b86bd704d9..553d755483ed6d2c86051e5e4decb4a21d030874
@@@ -12,7 -12,6 +12,7 @@@
  #include <linux/module.h>
  #include <linux/of.h>
  #include <asm/alternative.h>
 +#include <asm/cacheflush.h>
  #include <asm/errata_list.h>
  #include <asm/hwcap.h>
  #include <asm/patch.h>
@@@ -28,8 -27,9 +28,8 @@@ unsigned long elf_hwcap __read_mostly
  /* Host ISA bitmap */
  static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly;
  
 -#ifdef CONFIG_FPU
 -__ro_after_init DEFINE_STATIC_KEY_FALSE(cpu_hwcap_fpu);
 -#endif
 +__ro_after_init DEFINE_STATIC_KEY_ARRAY_FALSE(riscv_isa_ext_keys, RISCV_ISA_EXT_KEY_MAX);
 +EXPORT_SYMBOL(riscv_isa_ext_keys);
  
  /**
   * riscv_isa_extension_base() - Get base extension word
@@@ -73,9 -73,8 +73,9 @@@ void __init riscv_fill_hwcap(void
        struct device_node *node;
        const char *isa;
        char print_str[NUM_ALPHA_EXTS + 1];
 -      int i, j;
 +      int i, j, rc;
        static unsigned long isa2hwcap[256] = {0};
 +      unsigned long hartid;
  
        isa2hwcap['i'] = isa2hwcap['I'] = COMPAT_HWCAP_ISA_I;
        isa2hwcap['m'] = isa2hwcap['M'] = COMPAT_HWCAP_ISA_M;
@@@ -93,8 -92,7 +93,8 @@@
                DECLARE_BITMAP(this_isa, RISCV_ISA_EXT_MAX);
                const char *temp;
  
 -              if (riscv_of_processor_hartid(node) < 0)
 +              rc = riscv_of_processor_hartid(node, &hartid);
 +              if (rc < 0)
                        continue;
  
                if (of_property_read_string(node, "riscv,isa", &isa)) {
                        } else {
                                SET_ISA_EXT_MAP("sscofpmf", RISCV_ISA_EXT_SSCOFPMF);
                                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
                }
                        print_str[j++] = (char)('a' + i);
        pr_info("riscv: ELF capabilities %s\n", print_str);
  
 -#ifdef CONFIG_FPU
 -      if (elf_hwcap & (COMPAT_HWCAP_ISA_F | COMPAT_HWCAP_ISA_D))
 -              static_branch_enable(&cpu_hwcap_fpu);
 -#endif
 +      for_each_set_bit(i, riscv_isa, RISCV_ISA_EXT_MAX) {
 +              j = riscv_isa_ext2key(i);
 +              if (j >= 0)
 +                      static_branch_enable(&riscv_isa_ext_keys[j]);
 +      }
  }
  
  #ifdef CONFIG_RISCV_ALTERNATIVE
 -struct cpufeature_info {
 -      char name[ERRATA_STRING_LENGTH_MAX];
 -      bool (*check_func)(unsigned int stage);
 -};
 -
 -static bool __init_or_module cpufeature_svpbmt_check_func(unsigned int stage)
 +static bool __init_or_module cpufeature_probe_svpbmt(unsigned int stage)
  {
  #ifdef CONFIG_RISCV_ISA_SVPBMT
        switch (stage) {
        return false;
  }
  
 -static const struct cpufeature_info __initdata_or_module
 -cpufeature_list[CPUFEATURE_NUMBER] = {
 -      {
 -              .name = "svpbmt",
 -              .check_func = cpufeature_svpbmt_check_func
 -      },
 -};
 +static bool __init_or_module cpufeature_probe_zicbom(unsigned int stage)
 +{
 +#ifdef CONFIG_RISCV_ISA_ZICBOM
 +      switch (stage) {
 +      case RISCV_ALTERNATIVES_EARLY_BOOT:
 +              return false;
 +      default:
 +              if (riscv_isa_extension_available(NULL, ZICBOM)) {
 +                      riscv_noncoherent_supported();
 +                      return true;
 +              } else {
 +                      return false;
 +              }
 +      }
 +#endif
 +
 +      return false;
 +}
  
 +/*
 + * Probe presence of individual extensions.
 + *
 + * This code may also be executed before kernel relocation, so we cannot use
 + * addresses generated by the address-of operator as they won't be valid in
 + * this context.
 + */
  static u32 __init_or_module cpufeature_probe(unsigned int stage)
  {
 -      const struct cpufeature_info *info;
        u32 cpu_req_feature = 0;
 -      int idx;
  
 -      for (idx = 0; idx < CPUFEATURE_NUMBER; idx++) {
 -              info = &cpufeature_list[idx];
 +      if (cpufeature_probe_svpbmt(stage))
 +              cpu_req_feature |= (1U << CPUFEATURE_SVPBMT);
  
 -              if (info->check_func(stage))
 -                      cpu_req_feature |= (1U << idx);
 -      }
 +      if (cpufeature_probe_zicbom(stage))
 +              cpu_req_feature |= (1U << CPUFEATURE_ZICBOM);
  
        return cpu_req_feature;
  }
@@@ -308,6 -294,7 +309,6 @@@ void __init_or_module riscv_cpufeature_
                                                  unsigned int stage)
  {
        u32 cpu_req_feature = cpufeature_probe(stage);
 -      u32 cpu_apply_feature = 0;
        struct alt_entry *alt;
        u32 tmp;
  
                }
  
                tmp = (1U << alt->errata_id);
 -              if (cpu_req_feature & tmp) {
 +              if (cpu_req_feature & tmp)
                        patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len);
 -                      cpu_apply_feature |= tmp;
 -              }
        }
  }
  #endif
index e460df7ed799de6b00f3d86e2d0557b8f836e317,05f6cf06728970f68b899dcd5423b747e03f600e..969a552da8d2971c7df54cfd6af29871a443aff7
@@@ -7,6 -7,9 +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;
  }
  
@@@ -101,21 -118,20 +118,21 @@@ static irqreturn_t riscv_timer_interrup
  
  static int __init riscv_timer_init_dt(struct device_node *n)
  {
 -      int cpuid, hartid, error;
 +      int cpuid, error;
 +      unsigned long hartid;
        struct device_node *child;
        struct irq_domain *domain;
  
 -      hartid = riscv_of_processor_hartid(n);
 -      if (hartid < 0) {
 -              pr_warn("Not valid hartid for node [%pOF] error = [%d]\n",
 +      error = riscv_of_processor_hartid(n, &hartid);
 +      if (error < 0) {
 +              pr_warn("Not valid hartid for node [%pOF] error = [%lu]\n",
                        n, hartid);
 -              return hartid;
 +              return error;
        }
  
        cpuid = riscv_hartid_to_cpuid(hartid);
        if (cpuid < 0) {
 -              pr_warn("Invalid cpuid for hartid [%d]\n", hartid);
 +              pr_warn("Invalid cpuid for hartid [%lu]\n", hartid);
                return cpuid;
        }
  
                return -ENODEV;
        }
  
 -      pr_info("%s: Registering clocksource cpuid [%d] hartid [%d]\n",
 +      pr_info("%s: Registering clocksource cpuid [%d] hartid [%lu]\n",
               __func__, cpuid, hartid);
        error = clocksource_register_hz(&riscv_clocksource, riscv_timebase);
        if (error) {
        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;
  }
  
This page took 0.075236 seconds and 4 git commands to generate.