Skip to content

Commit

Permalink
lib: sbi: Implement Sstc extension
Browse files Browse the repository at this point in the history
Recently, Sstc extension was ratified. It defines stimecmp which allows
the supervisor mode to directly update the timecmp value without the
need of the SBI call. The hardware also can inject the S-mode timer
interrupt direclty to the supervisor without going through the M-mode.
To maintain backward compatibility with the older software, SBI call
now uses stimecmp directly if the hardware supports.

Implement the Sstc extension.

Signed-off-by: Atish Patra <atishp@rivosinc.com>
  • Loading branch information
atishp04 committed Jan 22, 2022
1 parent 2a467c2 commit e6b1858
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 5 deletions.
4 changes: 4 additions & 0 deletions include/sbi/riscv_encoding.h
Expand Up @@ -307,6 +307,10 @@
#define CSR_STVAL 0x143
#define CSR_SIP 0x144

/* Sstc extension */
#define CSR_STIMECMP 0x14D
#define CSR_STIMECMPH 0x15D

/* Supervisor Protection and Translation */
#define CSR_SATP 0x180

Expand Down
4 changes: 3 additions & 1 deletion include/sbi/sbi_hart.h
Expand Up @@ -26,9 +26,11 @@ enum sbi_hart_features {
SBI_HART_HAS_TIME = (1 << 4),
/** HART has MENVCFG CSR */
SBI_HART_HAS_MENVCFG = (1 << 5),
/** HART has SSTC extension implemented in hardware */
SBI_HART_HAS_SSTC = (1 << 6),

/** Last index of Hart features*/
SBI_HART_HAS_LAST_FEATURE = SBI_HART_HAS_MENVCFG,
SBI_HART_HAS_LAST_FEATURE = SBI_HART_HAS_SSTC,
};

struct sbi_scratch;
Expand Down
30 changes: 29 additions & 1 deletion lib/sbi/sbi_hart.c
Expand Up @@ -40,6 +40,8 @@ static unsigned long hart_features_offset;
static void mstatus_init(struct sbi_scratch *scratch)
{
unsigned long mstatus_val = 0;
unsigned long menvcfg_val = 0;
unsigned long mcountern_val = -1;

/* Enable FPU */
if (misa_extension('D') || misa_extension('F'))
Expand All @@ -51,6 +53,25 @@ static void mstatus_init(struct sbi_scratch *scratch)

csr_write(CSR_MSTATUS, mstatus_val);

/*
* The spec doesn't explicitly describe the reset value of menvcfg.
* Enable access to stimecmp if sstc extension is present in the
* hardware.
*/
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SSTC)) {
/* The TM bit should be set in mcounteren */
mcountern_val = 0x2;
#if __riscv_xlen == 32
menvcfg_val = csr_read(CSR_MENVCFGH);
menvcfg_val |= MENVCFGH_STCE;
csr_write(CSR_MENVCFGH, menvcfg_val);
#else
menvcfg_val = csr_read(CSR_MENVCFG);
menvcfg_val |= MENVCFG_STCE;
csr_write(CSR_MENVCFG, menvcfg_val);
#endif
}

/* Disable user mode usage of all perf counters except default ones (CY, TM, IR) */
if (misa_extension('S') &&
sbi_hart_has_feature(scratch, SBI_HART_HAS_SCOUNTEREN))
Expand All @@ -62,7 +83,7 @@ static void mstatus_init(struct sbi_scratch *scratch)
* But counters will not run until mcountinhibit is set.
*/
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTEREN))
csr_write(CSR_MCOUNTEREN, -1);
csr_write(CSR_MCOUNTEREN, mcountern_val);

/* All programmable counters will start running at runtime after S-mode request */
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
Expand Down Expand Up @@ -283,6 +304,9 @@ static inline char *sbi_hart_feature_id2string(unsigned long feature)
case SBI_HART_HAS_TIME:
fstr = "time";
break;
case SBI_HART_HAS_SSTC:
fstr = "sstc";
break;
default:
break;
}
Expand Down Expand Up @@ -512,6 +536,10 @@ static void hart_detect_features(struct sbi_scratch *scratch)
if (!trap.cause)
hfeatures->features |= SBI_HART_HAS_MENVCFG;

/* Detect if hart supports stimecmp CSR(Sstc extension) and menvcfg is implemented */
csr_read_allowed(CSR_STIMECMP, (unsigned long)&trap);
if (!trap.cause && sbi_hart_has_feature(scratch, SBI_HART_HAS_MENVCFG))
hfeatures->features |= SBI_HART_HAS_SSTC;
}

int sbi_hart_reinit(struct sbi_scratch *scratch)
Expand Down
25 changes: 22 additions & 3 deletions lib/sbi/sbi_timer.c
Expand Up @@ -124,16 +124,35 @@ void sbi_timer_set_delta_upper(ulong delta_upper)
void sbi_timer_event_start(u64 next_event)
{
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SET_TIMER);
if (timer_dev && timer_dev->timer_event_start)

/**
* Update the stimecmp directly if available. This allows
* the older software to leverage sstc extension on newer hardware.
*/
if (sbi_hart_has_feature(sbi_scratch_thishart_ptr(), SBI_HART_HAS_SSTC)) {
#if __riscv_xlen == 32
csr_write(CSR_STIMECMP, next_event & 0xFFFFFFFF);
csr_write(CSR_STIMECMPH, next_event >> 32);
#else
csr_write(CSR_STIMECMP, next_event);
#endif
} else if (timer_dev && timer_dev->timer_event_start) {
timer_dev->timer_event_start(next_event);
csr_clear(CSR_MIP, MIP_STIP);
csr_clear(CSR_MIP, MIP_STIP);
}
csr_set(CSR_MIE, MIP_MTIP);
}

void sbi_timer_process(void)
{
csr_clear(CSR_MIE, MIP_MTIP);
csr_set(CSR_MIP, MIP_STIP);
/*
* If sstc extension is available, supervisor can recieve the timer
* direclty without M-mode come in between. This function should
* only invoked if M-mode programs the timer for its own purpose.
*/
if (!sbi_hart_has_feature(sbi_scratch_thishart_ptr(), SBI_HART_HAS_SSTC))
csr_set(CSR_MIP, MIP_STIP);
}

const struct sbi_timer_device *sbi_timer_get_device(void)
Expand Down

0 comments on commit e6b1858

Please sign in to comment.