hw/mips/loongson3_bootp.h | 1 + hw/mips/loongson3_virt.c | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-)
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>
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, > +};
© 2016 - 2024 Red Hat, Inc.