[PATCH] hw/mips/loongson3_virt: Implement node counter timer

Jiaxun Yang posted 1 patch 6 months, 2 weeks ago
Failed in applying to current master (apply log)
hw/mips/loongson3_bootp.h |  1 +
hw/mips/loongson3_virt.c  | 38 ++++++++++++++++++++++++++++++++++----
2 files changed, 35 insertions(+), 4 deletions(-)
[PATCH] hw/mips/loongson3_virt: Implement node counter timer
Posted by Jiaxun Yang 6 months, 2 weeks ago
Node counter is a timer presents on Loongson-3 chips, which runs
as fast as CPU clock. It's being mapped into a MMIO location.

Emulate this for loongson3_virt machine, in hope that kernel can
use it as a better clock source.

Hardware's behavior on 32-bit read/write is also emulated in case
legacy kernel is trying to use it with hi/lo splitted read.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 hw/mips/loongson3_bootp.h |  1 +
 hw/mips/loongson3_virt.c  | 38 ++++++++++++++++++++++++++++++++++----
 2 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/hw/mips/loongson3_bootp.h b/hw/mips/loongson3_bootp.h
index 1b0dd3b59171..c6a435397d2c 100644
--- a/hw/mips/loongson3_bootp.h
+++ b/hw/mips/loongson3_bootp.h
@@ -210,6 +210,7 @@ enum {
     VIRT_PCIE_ECAM,
     VIRT_BIOS_ROM,
     VIRT_UART,
+    VIRT_NODECNT,
     VIRT_LIOINTC,
     VIRT_PCIE_MMIO,
     VIRT_HIGHMEM
diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c
index b10a611a98f4..b78ac8032096 100644
--- a/hw/mips/loongson3_virt.c
+++ b/hw/mips/loongson3_virt.c
@@ -74,6 +74,7 @@ const MemMapEntry virt_memmap[] = {
     [VIRT_PCIE_ECAM] =   { 0x1a000000,     0x2000000 },
     [VIRT_BIOS_ROM] =    { 0x1fc00000,      0x200000 },
     [VIRT_UART] =        { 0x1fe001e0,           0x8 },
+    [VIRT_NODECNT] =     { 0x3ff00408,           0x8 },
     [VIRT_LIOINTC] =     { 0x3ff01400,          0x64 },
     [VIRT_PCIE_MMIO] =   { 0x40000000,    0x40000000 },
     [VIRT_HIGHMEM] =     { 0x80000000,           0x0 }, /* Variable */
@@ -92,6 +93,7 @@ static const MemMapEntry loader_rommap[] = {
 
 struct LoongsonMachineState {
     MachineState parent_obj;
+    Clock *cpuclk;
     MemoryRegion *pio_alias;
     MemoryRegion *mmio_alias;
     MemoryRegion *ecam_alias;
@@ -145,6 +147,29 @@ static const MemoryRegionOps loongson3_pm_ops = {
     }
 };
 
+static uint64_t loongson3_nodecnt_read(void *opaque,
+                                        hwaddr addr, unsigned size)
+{
+    LoongsonMachineState *s = opaque;
+    int64_t now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    uint64_t ticks = clock_ns_to_ticks(s->cpuclk, now_ns);
+
+    if (addr == 0x4) {
+        return ticks >> 32;
+    }
+
+    return ticks;
+}
+
+static const MemoryRegionOps loongson3_nodecnt_ops = {
+    .read  = loongson3_nodecnt_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 8,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 8,
+};
+
 #define DEF_LOONGSON3_FREQ (800 * 1000 * 1000)
 
 static uint64_t get_cpu_freq_hz(void)
@@ -463,7 +488,6 @@ static void mips_loongson3_virt_init(MachineState *machine)
     int i;
     long bios_size;
     MIPSCPU *cpu;
-    Clock *cpuclk;
     CPUMIPSState *env;
     DeviceState *liointc;
     char *filename;
@@ -471,10 +495,12 @@ static void mips_loongson3_virt_init(MachineState *machine)
     const char *kernel_filename = machine->kernel_filename;
     const char *initrd_filename = machine->initrd_filename;
     ram_addr_t ram_size = machine->ram_size;
+    LoongsonMachineState *ms = LOONGSON_MACHINE(machine);
     MemoryRegion *address_space_mem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *bios = g_new(MemoryRegion, 1);
     MemoryRegion *iomem = g_new(MemoryRegion, 1);
+    MemoryRegion *nodecnt = g_new(MemoryRegion, 1);
 
     /* TODO: TCG will support all CPU types */
     if (!kvm_enabled()) {
@@ -520,14 +546,14 @@ static void mips_loongson3_virt_init(MachineState *machine)
     sysbus_create_simple("goldfish_rtc", virt_memmap[VIRT_RTC].base,
                          qdev_get_gpio_in(liointc, RTC_IRQ));
 
-    cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
-    clock_set_hz(cpuclk, DEF_LOONGSON3_FREQ);
+    ms->cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+    clock_set_hz(ms->cpuclk, DEF_LOONGSON3_FREQ);
 
     for (i = 0; i < machine->smp.cpus; i++) {
         int ip;
 
         /* init CPUs */
-        cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
+        cpu = mips_cpu_create_with_clock(machine->cpu_type, ms->cpuclk);
 
         /* Init internal devices */
         cpu_mips_irq_init_cpu(cpu);
@@ -553,6 +579,8 @@ static void mips_loongson3_virt_init(MachineState *machine)
                            machine->ram, 0, virt_memmap[VIRT_LOWMEM].size);
     memory_region_init_io(iomem, NULL, &loongson3_pm_ops,
                            NULL, "loongson3_pm", virt_memmap[VIRT_PM].size);
+    memory_region_init_io(nodecnt, NULL, &loongson3_nodecnt_ops, ms,
+                          "loongson3_nodecnt", virt_memmap[VIRT_NODECNT].size);
 
     memory_region_add_subregion(address_space_mem,
                       virt_memmap[VIRT_LOWMEM].base, ram);
@@ -562,6 +590,8 @@ static void mips_loongson3_virt_init(MachineState *machine)
                       virt_memmap[VIRT_HIGHMEM].base, machine->ram);
     memory_region_add_subregion(address_space_mem,
                       virt_memmap[VIRT_PM].base, iomem);
+    memory_region_add_subregion(address_space_mem,
+                      virt_memmap[VIRT_NODECNT].base, nodecnt);
 
     /*
      * We do not support flash operation, just loading bios.bin as raw BIOS.

---
base-commit: 248f6f62df073a3b4158fd0093863ab885feabb5
change-id: 20240511-loongson3_hpt-cb02790b24db

Best regards,
-- 
Jiaxun Yang <jiaxun.yang@flygoat.com>
Re: [PATCH] hw/mips/loongson3_virt: Implement node counter timer
Posted by Philippe Mathieu-Daudé 5 months, 3 weeks ago
Hi Jiaxun,

On 12/5/24 14:20, Jiaxun Yang wrote:
> Node counter is a timer presents on Loongson-3 chips, which runs
> as fast as CPU clock. It's being mapped into a MMIO location.
> 
> Emulate this for loongson3_virt machine, in hope that kernel can
> use it as a better clock source.
> 
> Hardware's behavior on 32-bit read/write is also emulated in case
> legacy kernel is trying to use it with hi/lo splitted read.
> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>   hw/mips/loongson3_bootp.h |  1 +
>   hw/mips/loongson3_virt.c  | 38 ++++++++++++++++++++++++++++++++++----
>   2 files changed, 35 insertions(+), 4 deletions(-)


> +static uint64_t loongson3_nodecnt_read(void *opaque,
> +                                        hwaddr addr, unsigned size)
> +{
> +    LoongsonMachineState *s = opaque;
> +    int64_t now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    uint64_t ticks = clock_ns_to_ticks(s->cpuclk, now_ns);
> +
> +    if (addr == 0x4) {
> +        return ticks >> 32;

Does that imply .endianness = DEVICE_BIG_ENDIAN?

It could be simpler to let the core MMIO code do the hi/lo
bits management using ".impl.min_access_size = 8".

> +    }
> +
> +    return ticks;
> +}
> +
> +static const MemoryRegionOps loongson3_nodecnt_ops = {
> +    .read  = loongson3_nodecnt_read,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 8,
> +    .impl.min_access_size = 4,
> +    .impl.max_access_size = 8,
> +};