diff mbox series

[RFC,v4,05/30] target/loongarch: Add constant timer support

Message ID 20220108091419.2027710-6-yangxiaojuan@loongson.cn
State New
Headers show
Series Add LoongArch softmmu support. | expand

Commit Message

Xiaojuan Yang Jan. 8, 2022, 9:13 a.m. UTC
Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
---
 target/loongarch/constant_timer.c | 63 +++++++++++++++++++++++++++++++
 target/loongarch/cpu.c            |  9 +++++
 target/loongarch/cpu.h            | 10 +++++
 target/loongarch/meson.build      |  1 +
 4 files changed, 83 insertions(+)
 create mode 100644 target/loongarch/constant_timer.c

Comments

WANG Xuerui Jan. 9, 2022, 9:25 a.m. UTC | #1
On 1/8/22 17:13, Xiaojuan Yang wrote:
> Signed-off-by: Xiaojuan Yang<yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao<gaosong@loongson.cn>
> ---
>   target/loongarch/constant_timer.c | 63 +++++++++++++++++++++++++++++++
>   target/loongarch/cpu.c            |  9 +++++
>   target/loongarch/cpu.h            | 10 +++++
>   target/loongarch/meson.build      |  1 +
>   4 files changed, 83 insertions(+)
>   create mode 100644 target/loongarch/constant_timer.c
>
> diff --git a/target/loongarch/constant_timer.c b/target/loongarch/constant_timer.c
> new file mode 100644
> index 0000000000..e7d0f5ffe7
> --- /dev/null
> +++ b/target/loongarch/constant_timer.c
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * QEMU LoongArch constant timer support
> + *
> + * Copyright (c) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/loongarch/loongarch.h"
> +#include "qemu/timer.h"
> +#include "cpu.h"
> +
> +#define TIMER_PERIOD                10 /* 10 ns period for 100 Mhz frequency */
"MHz"
> +#define CONSTANT_TIMER_TICK_MASK    0xfffffffffffcUL
> +#define CONSTANT_TIMER_ENABLE       0x1UL
> +
> +/* LoongArch timer */
Looks like this comment is for some type definitions, but the function 
below is just an accessor, so remove it? The whole file is about the 
"LoongArch timer" after all.
> +uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu)
> +{
> +    return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
> +}
> +
> +uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu)
> +{
> +    uint64_t now, expire;
> +
> +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    expire = timer_expire_time_ns(&cpu->timer);
> +
> +    return (expire - now) / TIMER_PERIOD;
> +}
> +
> +void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
> +                                               uint64_t value)
> +{
> +    CPULoongArchState *env = &cpu->env;
> +    uint64_t now, next;
> +
> +    env->CSR_TCFG = value;
> +    if (value & CONSTANT_TIMER_ENABLE) {
> +        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
> +        timer_mod(&cpu->timer, next);
> +    }
> +}
> +
> +void loongarch_constant_timer_cb(void *opaque)
> +{
> +    LoongArchCPU *cpu  = opaque;
> +    CPULoongArchState *env = &cpu->env;
> +    uint64_t now, next;
> +
> +    if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) {
> +        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        next = now + (env->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
> +        timer_mod(&cpu->timer, next);
> +    } else {
> +        env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
> +    }
> +
> +    env->CSR_ESTAT |= 1 << IRQ_TIMER;
> +    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
> +}
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index 690eeea2e6..823951dddd 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -235,12 +235,21 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
>       LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev);
>       Error *local_err = NULL;
>   
> +#ifndef CONFIG_USER_ONLY
> +    LoongArchCPU *cpu = LOONGARCH_CPU(dev);
> +#endif
> +
>       cpu_exec_realizefn(cs, &local_err);
>       if (local_err != NULL) {
>           error_propagate(errp, local_err);
>           return;
>       }
>   
> +#ifndef CONFIG_USER_ONLY
> +    timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL,
> +                  &loongarch_constant_timer_cb, cpu);
> +#endif
> +
>       cpu_reset(cs);
>       qemu_init_vcpu(cs);
>   
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index cf7fc46f72..ef84584678 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -12,6 +12,7 @@
>   #include "fpu/softfloat-types.h"
>   #include "hw/registerfields.h"
>   #include "cpu-csr.h"
> +#include "qemu/timer.h"
>   
>   #define TCG_GUEST_DEFAULT_MO (0)
>   
> @@ -148,6 +149,9 @@ FIELD(CPUCFG20, L3IU_SIZE, 24, 7)
>   extern const char * const regnames[32];
>   extern const char * const fregnames[32];
>   
> +#define N_IRQS      14
> +#define IRQ_TIMER   11
> +
>   typedef struct CPULoongArchState CPULoongArchState;
>   struct CPULoongArchState {
>       uint64_t gpr[32];
> @@ -242,6 +246,7 @@ struct LoongArchCPU {
>   
>       CPUNegativeOffsetState neg;
>       CPULoongArchState env;
> +    QEMUTimer timer; /* Internal timer */
What do you mean by "internal", is there any "external" counterpart? If 
there isn't one, I think you may be referring to the "architectural" 
timer instead (as is defined by LoongArch, instead of any concrete 
implementation), and this would have to be changed accordingly.
>   };
>   
>   #define TYPE_LOONGARCH_CPU "loongarch-cpu"
> @@ -306,4 +311,9 @@ enum {
>   #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
>   #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
>   
> +void loongarch_constant_timer_cb(void *opaque);
> +uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
> +uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu);
> +void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
> +                                               uint64_t value);
>   #endif /* LOONGARCH_CPU_H */
> diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
> index 103f36ee15..6168e910a0 100644
> --- a/target/loongarch/meson.build
> +++ b/target/loongarch/meson.build
> @@ -17,6 +17,7 @@ loongarch_tcg_ss.add(zlib)
>   loongarch_softmmu_ss = ss.source_set()
>   loongarch_softmmu_ss.add(files(
>     'machine.c',
> +  'constant_timer.c',
Why not alphabetical order? Is there any requirement for ordering here? 
I don't think there is one.
>   ))
>   
>   loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])
Mark Cave-Ayland Jan. 15, 2022, 1:02 p.m. UTC | #2
On 08/01/2022 09:13, Xiaojuan Yang wrote:

> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
> Signed-off-by: Song Gao <gaosong@loongson.cn>
> ---
>   target/loongarch/constant_timer.c | 63 +++++++++++++++++++++++++++++++
>   target/loongarch/cpu.c            |  9 +++++
>   target/loongarch/cpu.h            | 10 +++++
>   target/loongarch/meson.build      |  1 +
>   4 files changed, 83 insertions(+)
>   create mode 100644 target/loongarch/constant_timer.c
> 
> diff --git a/target/loongarch/constant_timer.c b/target/loongarch/constant_timer.c
> new file mode 100644
> index 0000000000..e7d0f5ffe7
> --- /dev/null
> +++ b/target/loongarch/constant_timer.c
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * QEMU LoongArch constant timer support
> + *
> + * Copyright (c) 2021 Loongson Technology Corporation Limited
> + */
> +
> +#include "qemu/osdep.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 CONSTANT_TIMER_TICK_MASK    0xfffffffffffcUL
> +#define CONSTANT_TIMER_ENABLE       0x1UL
> +
> +/* LoongArch timer */
> +uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu)
> +{
> +    return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
> +}
> +
> +uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu)
> +{
> +    uint64_t now, expire;
> +
> +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    expire = timer_expire_time_ns(&cpu->timer);
> +
> +    return (expire - now) / TIMER_PERIOD;
> +}
> +
> +void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
> +                                               uint64_t value)
> +{
> +    CPULoongArchState *env = &cpu->env;
> +    uint64_t now, next;
> +
> +    env->CSR_TCFG = value;
> +    if (value & CONSTANT_TIMER_ENABLE) {
> +        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
> +        timer_mod(&cpu->timer, next);
> +    }
> +}
> +
> +void loongarch_constant_timer_cb(void *opaque)
> +{
> +    LoongArchCPU *cpu  = opaque;
> +    CPULoongArchState *env = &cpu->env;
> +    uint64_t now, next;
> +
> +    if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) {
> +        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +        next = now + (env->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
> +        timer_mod(&cpu->timer, next);
> +    } else {
> +        env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
> +    }
> +
> +    env->CSR_ESTAT |= 1 << IRQ_TIMER;
> +    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
> +}
> diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
> index 690eeea2e6..823951dddd 100644
> --- a/target/loongarch/cpu.c
> +++ b/target/loongarch/cpu.c
> @@ -235,12 +235,21 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
>       LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev);
>       Error *local_err = NULL;
>   
> +#ifndef CONFIG_USER_ONLY
> +    LoongArchCPU *cpu = LOONGARCH_CPU(dev);
> +#endif
> +
>       cpu_exec_realizefn(cs, &local_err);
>       if (local_err != NULL) {
>           error_propagate(errp, local_err);
>           return;
>       }
>   
> +#ifndef CONFIG_USER_ONLY
> +    timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL,
> +                  &loongarch_constant_timer_cb, cpu);
> +#endif
> +

As I mentioned previously, I'm not convinced that adding the CONFIG_USER_ONLY guards 
is the right thing to do here.

For SPARC64 there is a separate sparc64_cpu_devinit() function that is only called in 
sysemu mode (via the machine init() function) which sets up the timers. Have a look 
at hw/sparc64/sparc64.c and hw/sparc64/sun4u.c for an existing reference as to how 
this is done.

This suggests that a similar function would need to exist in hw/loongarch/loongson3.c.

>       cpu_reset(cs);
>       qemu_init_vcpu(cs);
>   
> diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
> index cf7fc46f72..ef84584678 100644
> --- a/target/loongarch/cpu.h
> +++ b/target/loongarch/cpu.h
> @@ -12,6 +12,7 @@
>   #include "fpu/softfloat-types.h"
>   #include "hw/registerfields.h"
>   #include "cpu-csr.h"
> +#include "qemu/timer.h"
>   
>   #define TCG_GUEST_DEFAULT_MO (0)
>   
> @@ -148,6 +149,9 @@ FIELD(CPUCFG20, L3IU_SIZE, 24, 7)
>   extern const char * const regnames[32];
>   extern const char * const fregnames[32];
>   
> +#define N_IRQS      14
> +#define IRQ_TIMER   11
> +
>   typedef struct CPULoongArchState CPULoongArchState;
>   struct CPULoongArchState {
>       uint64_t gpr[32];
> @@ -242,6 +246,7 @@ struct LoongArchCPU {
>   
>       CPUNegativeOffsetState neg;
>       CPULoongArchState env;
> +    QEMUTimer timer; /* Internal timer */
>   };
>   
>   #define TYPE_LOONGARCH_CPU "loongarch-cpu"
> @@ -306,4 +311,9 @@ enum {
>   #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
>   #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
>   
> +void loongarch_constant_timer_cb(void *opaque);
> +uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
> +uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu);
> +void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
> +                                               uint64_t value);
>   #endif /* LOONGARCH_CPU_H */
> diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
> index 103f36ee15..6168e910a0 100644
> --- a/target/loongarch/meson.build
> +++ b/target/loongarch/meson.build
> @@ -17,6 +17,7 @@ loongarch_tcg_ss.add(zlib)
>   loongarch_softmmu_ss = ss.source_set()
>   loongarch_softmmu_ss.add(files(
>     'machine.c',
> +  'constant_timer.c',
>   ))
>   
>   loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])


ATB,

Mark.
diff mbox series

Patch

diff --git a/target/loongarch/constant_timer.c b/target/loongarch/constant_timer.c
new file mode 100644
index 0000000000..e7d0f5ffe7
--- /dev/null
+++ b/target/loongarch/constant_timer.c
@@ -0,0 +1,63 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU LoongArch constant timer support
+ *
+ * Copyright (c) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.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 CONSTANT_TIMER_TICK_MASK    0xfffffffffffcUL
+#define CONSTANT_TIMER_ENABLE       0x1UL
+
+/* LoongArch timer */
+uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu)
+{
+    return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
+}
+
+uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu)
+{
+    uint64_t now, expire;
+
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    expire = timer_expire_time_ns(&cpu->timer);
+
+    return (expire - now) / TIMER_PERIOD;
+}
+
+void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
+                                               uint64_t value)
+{
+    CPULoongArchState *env = &cpu->env;
+    uint64_t now, next;
+
+    env->CSR_TCFG = value;
+    if (value & CONSTANT_TIMER_ENABLE) {
+        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
+        timer_mod(&cpu->timer, next);
+    }
+}
+
+void loongarch_constant_timer_cb(void *opaque)
+{
+    LoongArchCPU *cpu  = opaque;
+    CPULoongArchState *env = &cpu->env;
+    uint64_t now, next;
+
+    if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) {
+        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        next = now + (env->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
+        timer_mod(&cpu->timer, next);
+    } else {
+        env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
+    }
+
+    env->CSR_ESTAT |= 1 << IRQ_TIMER;
+    cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+}
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 690eeea2e6..823951dddd 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -235,12 +235,21 @@  static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
     LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev);
     Error *local_err = NULL;
 
+#ifndef CONFIG_USER_ONLY
+    LoongArchCPU *cpu = LOONGARCH_CPU(dev);
+#endif
+
     cpu_exec_realizefn(cs, &local_err);
     if (local_err != NULL) {
         error_propagate(errp, local_err);
         return;
     }
 
+#ifndef CONFIG_USER_ONLY
+    timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL,
+                  &loongarch_constant_timer_cb, cpu);
+#endif
+
     cpu_reset(cs);
     qemu_init_vcpu(cs);
 
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index cf7fc46f72..ef84584678 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -12,6 +12,7 @@ 
 #include "fpu/softfloat-types.h"
 #include "hw/registerfields.h"
 #include "cpu-csr.h"
+#include "qemu/timer.h"
 
 #define TCG_GUEST_DEFAULT_MO (0)
 
@@ -148,6 +149,9 @@  FIELD(CPUCFG20, L3IU_SIZE, 24, 7)
 extern const char * const regnames[32];
 extern const char * const fregnames[32];
 
+#define N_IRQS      14
+#define IRQ_TIMER   11
+
 typedef struct CPULoongArchState CPULoongArchState;
 struct CPULoongArchState {
     uint64_t gpr[32];
@@ -242,6 +246,7 @@  struct LoongArchCPU {
 
     CPUNegativeOffsetState neg;
     CPULoongArchState env;
+    QEMUTimer timer; /* Internal timer */
 };
 
 #define TYPE_LOONGARCH_CPU "loongarch-cpu"
@@ -306,4 +311,9 @@  enum {
 #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
 #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
 
+void loongarch_constant_timer_cb(void *opaque);
+uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu);
+uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu);
+void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
+                                               uint64_t value);
 #endif /* LOONGARCH_CPU_H */
diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build
index 103f36ee15..6168e910a0 100644
--- a/target/loongarch/meson.build
+++ b/target/loongarch/meson.build
@@ -17,6 +17,7 @@  loongarch_tcg_ss.add(zlib)
 loongarch_softmmu_ss = ss.source_set()
 loongarch_softmmu_ss.add(files(
   'machine.c',
+  'constant_timer.c',
 ))
 
 loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss])