@@ -435,6 +435,7 @@ static void set_loongarch_csr(CPULoongArchState *env)
env->CSR_CPUID = (cs->cpu_index & 0x1ff);
env->CSR_EENTRY |= (uint64_t)0x80000000;
env->CSR_TLBRENTRY |= (uint64_t)0x80000000;
+ env->CSR_TMID = cs->cpu_index;
}
#endif
@@ -46,6 +46,9 @@ FIELD(FCSR0, CAUSE, 24, 5)
extern const char * const regnames[];
extern const char * const fregnames[];
+#define N_IRQS 14
+#define IRQ_TIMER 11
+
#define LOONGARCH_HFLAG_KU 0x00003 /* kernel/user mode mask */
#define LOONGARCH_HFLAG_UM 0x00003 /* user mode flag */
#define LOONGARCH_HFLAG_KM 0x00000 /* kernel mode flag */
@@ -84,6 +87,8 @@ struct CPULoongArchState {
int error_code;
target_ulong exception_base;
#endif
+ void *irq[N_IRQS];
+ QEMUTimer *timer; /* Internal timer */
};
/**
@@ -181,4 +186,9 @@ enum {
#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
+void cpu_loongarch_clock_init(LoongArchCPU *cpu);
+uint64_t cpu_loongarch_get_stable_counter(CPULoongArchState *env);
+uint64_t cpu_loongarch_get_stable_timer_ticks(CPULoongArchState *env);
+void cpu_loongarch_store_stable_timer_config(CPULoongArchState *env,
+ uint64_t value);
#endif /* LOONGARCH_CPU_H */
@@ -14,6 +14,7 @@
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
+#include "hw/irq.h"
#include "cpu-csr.h"
#include "tcg/tcg-ldst.h"
@@ -78,7 +79,12 @@ target_ulong helper_csr_rdq(CPULoongArchState *env, uint64_t csr)
CASE_CSR_RDQ(KS7)
CASE_CSR_RDQ(KS8)
CASE_CSR_RDQ(TMID)
+ CASE_CSR_RDQ(TCFG)
+ case LOONGARCH_CSR_TVAL:
+ v = cpu_loongarch_get_stable_timer_ticks(env);
+ break;
CASE_CSR_RDQ(CNTC)
+ CASE_CSR_RDQ(TINTCLR)
CASE_CSR_RDQ(LLBCTL)
CASE_CSR_RDQ(IMPCTL1)
CASE_CSR_RDQ(IMPCTL2)
@@ -223,8 +229,16 @@ target_ulong helper_csr_wrq(CPULoongArchState *env, target_ulong val,
CASE_CSR_WRQ(KS7)
CASE_CSR_WRQ(KS8)
CASE_CSR_WRQ(TMID)
+ case LOONGARCH_CSR_TCFG:
+ old_v = env->CSR_TCFG;
+ cpu_loongarch_store_stable_timer_config(env, val);
+ break;
CASE_CSR_WRQ(TVAL)
CASE_CSR_WRQ(CNTC)
+ case LOONGARCH_CSR_TINTCLR:
+ old_v = 0;
+ qemu_irq_lower(env->irq[IRQ_TIMER]);
+ break;
CASE_CSR_WRQ(LLBCTL)
CASE_CSR_WRQ(IMPCTL1)
CASE_CSR_WRQ(IMPCTL2)
@@ -373,8 +387,12 @@ void helper_csr_xchgq_r0(CPULoongArchState *env, target_ulong mask, uint64_t csr
CASE_CSR_XCHGQ(KS7)
CASE_CSR_XCHGQ(KS8)
CASE_CSR_XCHGQ(TMID)
+ case LOONGARCH_CSR_TCFG:
+ cpu_loongarch_store_stable_timer_config(env, env->CSR_TCFG & (~mask));
+ break;
CASE_CSR_XCHGQ(TVAL)
CASE_CSR_XCHGQ(CNTC)
+ CASE_CSR_XCHGQ(TINTCLR)
CASE_CSR_XCHGQ(LLBCTL)
CASE_CSR_XCHGQ(IMPCTL1)
CASE_CSR_XCHGQ(IMPCTL2)
@@ -471,6 +489,7 @@ void helper_csr_xchgq_r0(CPULoongArchState *env, target_ulong mask, uint64_t csr
target_ulong helper_csr_xchgq(CPULoongArchState *env, target_ulong val,
target_ulong mask, uint64_t csr)
{
+ target_ulong tmp;
target_ulong v = val & mask;
#define CASE_CSR_XCHGQ(csr) \
@@ -523,8 +542,15 @@ target_ulong helper_csr_xchgq(CPULoongArchState *env, target_ulong val,
CASE_CSR_XCHGQ(KS7)
CASE_CSR_XCHGQ(KS8)
CASE_CSR_XCHGQ(TMID)
+ case LOONGARCH_CSR_TCFG:
+ val = env->CSR_TCFG;
+ tmp = val & ~mask;
+ tmp |= v;
+ cpu_loongarch_store_stable_timer_config(env, tmp);
+ break;
CASE_CSR_XCHGQ(TVAL)
CASE_CSR_XCHGQ(CNTC)
+ CASE_CSR_XCHGQ(TINTCLR)
CASE_CSR_XCHGQ(LLBCTL)
CASE_CSR_XCHGQ(IMPCTL1)
CASE_CSR_XCHGQ(IMPCTL2)
@@ -18,6 +18,7 @@ loongarch_softmmu_ss.add(files(
'machine.c',
'tlb_helper.c',
'csr_helper.c',
+ 'stabletimer.c',
))
loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])
new file mode 100644
@@ -0,0 +1,71 @@
+/*
+ * QEMU LoongArch timer support
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/loongarch/loongarch.h"
+#include "qemu/timer.h"
+#include "cpu.h"
+
+#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
+#define STABLETIMER_TICK_MASK 0xfffffffffffcUL
+#define STABLETIMER_ENABLE 0x1UL
+
+/* LoongArch timer */
+uint64_t cpu_loongarch_get_stable_counter(CPULoongArchState *env)
+{
+ return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
+}
+
+uint64_t cpu_loongarch_get_stable_timer_ticks(CPULoongArchState *env)
+{
+ uint64_t now, expire;
+
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ expire = timer_expire_time_ns(env->timer);
+
+ return (expire - now) / TIMER_PERIOD;
+}
+
+void cpu_loongarch_store_stable_timer_config(CPULoongArchState *env,
+ uint64_t value)
+{
+ uint64_t now, next;
+
+ env->CSR_TCFG = value;
+ if (value & STABLETIMER_ENABLE) {
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ next = now + (value & STABLETIMER_TICK_MASK) * TIMER_PERIOD;
+ timer_mod(env->timer, next);
+ }
+}
+
+static void loongarch_stable_timer_cb(void *opaque)
+{
+ CPULoongArchState *env;
+ uint64_t now, next;
+
+ env = opaque;
+ if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) {
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ next = now + (env->CSR_TCFG & STABLETIMER_TICK_MASK) * TIMER_PERIOD;
+ timer_mod(env->timer, next);
+ } else {
+ env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
+ }
+
+ qemu_irq_raise(env->irq[IRQ_TIMER]);
+}
+
+void cpu_loongarch_clock_init(LoongArchCPU *cpu)
+{
+ CPULoongArchState *env = &cpu->env;
+
+ env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ &loongarch_stable_timer_cb, env);
+}