From: LuHui <luhu...@gmail.com> Signed-off-by: LuHui <luhu...@gmail.com> --- MAINTAINERS | 1 + configs/devices/mips-softmmu/common.mak | 2 + hw/mips/Kconfig | 11 ++ hw/mips/meson.build | 2 + hw/mips/mt7628.c | 189 ++++++++++++++++++++++++ hw/mips/vocore2.c | 180 ++++++++++++++++++++++ include/hw/mips/mt7628.h | 77 ++++++++++ 7 files changed, 462 insertions(+) create mode 100644 hw/mips/mt7628.c create mode 100644 hw/mips/vocore2.c create mode 100644 include/hw/mips/mt7628.h
diff --git a/MAINTAINERS b/MAINTAINERS index 41854e939c..1b2f92e078 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1238,6 +1238,7 @@ VoCore2 M: Lu Hui <luhu...@gmail.com> S: Maintained F: hw/*/*mt7628* +F: hw/mips/vocore2.c Jazz M: Hervé Poussineau <hpous...@reactos.org> diff --git a/configs/devices/mips-softmmu/common.mak b/configs/devices/mips-softmmu/common.mak index 7da99327a7..e4a5c54635 100644 --- a/configs/devices/mips-softmmu/common.mak +++ b/configs/devices/mips-softmmu/common.mak @@ -29,3 +29,5 @@ CONFIG_PCNET_PCI=y CONFIG_MIPSSIM=y CONFIG_SMBUS_EEPROM=y CONFIG_TEST_DEVICES=y +CONFIG_MT7628=y +CONFIG_VOCORE2=y diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index da3a37e215..f7faec010c 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -10,6 +10,17 @@ config MIPSSIM select SERIAL_ISA select MIPSNET +config MT7628 + bool + select SERIAL + select USB_EHCI + select USB_EHCI_SYSBUS + select UNIMP + +config VOCORE2 + bool + select MT7628 + config JAZZ bool select ISA_BUS diff --git a/hw/mips/meson.build b/hw/mips/meson.build index 900613fc08..9ad125a996 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -10,6 +10,8 @@ mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c')) mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c')) mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c')) mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt]) +mips_ss.add(when: 'CONFIG_MT7628', if_true: files('mt7628.c')) +mips_ss.add(when: 'CONFIG_VOCORE2', if_true: files('vocore2.c')) endif hw_arch += {'mips': mips_ss} diff --git a/hw/mips/mt7628.c b/hw/mips/mt7628.c new file mode 100644 index 0000000000..d982b1c704 --- /dev/null +++ b/hw/mips/mt7628.c @@ -0,0 +1,189 @@ +/* + * QEMU/mt7628 emulation + * + * Copyright (c) 2023 Lu Hui <luhu...@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/datadir.h" +#include "hw/clock.h" +#include "hw/block/flash.h" +#include "hw/mips/mips.h" +#include "hw/mips/cpudevs.h" +#include "hw/mips/bios.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/char/serial.h" +#include "hw/misc/unimp.h" +#include "hw/mips/mt7628.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "elf.h" +#include "hw/sysbus.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" +#include "sysemu/qtest.h" +#include "sysemu/reset.h" + +/* data src: linux kernel, openwrt, uboot */ + +/* irq table */ +enum { + IRQ_UART0 = 20, + IRQ_UART1 = 21, + IRQ_UART2 = 22, + IRQ_EHCI = 18 +}; + +/* Memory map */ +const hwaddr mt7628_memmap[] = { + [MT7628_DEV_DDR] = 0x00000000, + [MT7628_DEV_SYSCTRL] = 0x10000000, + [MT7628_DEV_INTC] = 0x10000200, + [MT7628_DEV_UART0] = 0x10000C00, + [MT7628_DEV_UART1] = 0x10000D00, + [MT7628_DEV_UART2] = 0x10000E00, + [MT7628_DEV_EHCI] = 0x101C0000, + [MT7628_DEV_FLASH_DIRECT] = 0x1C000000, +}; + +struct mt7628Unimplemented { + const char *device_name; + hwaddr base; + hwaddr size; +} unimplemented[] = { + { "timer", 0x10000100, 0xFF }, + { "memc", 0x10000300, 0xFF }, + { "rbus", 0x10000400, 0xFF }, + { "mips-cnt", 0x10000500, 0xFF }, + { "gpio", 0x10000600, 0xFF }, + { "spi-slave", 0x10000700, 0xFF }, + { "i2c", 0x10000900, 0xFF }, + { "i2s", 0x10000A00, 0xFF }, + { "spi-master", 0x10000B00, 0xFF }, + { "rgctl", 0x10001000, 2 * KiB }, + { "pcm", 0x10002000, 2 * KiB }, + { "dma", 0x10002800, 2 * KiB }, + { "aes", 0x10004000, 4 * KiB }, + { "pwm", 0x10005000, 4 * KiB }, + { "ethernet-phy", 0x10100000, 64 * KiB }, + { "ethernet", 0x10110000, 32 * KiB }, + { "usb-phy", 0x10120000, 32 * KiB }, + { "sdxc", 0x10130000, 32 * KiB }, + { "pcie", 0x10140000, 256 * KiB }, + { "wlan", 0x10300000, 1 * MiB }, + { "pcie-direct", 0x20000000, 256 * MiB }, +}; + +static void mt7628_init(Object *obj) +{ + mt7628State *s = MT7628(obj); + s->memmap = mt7628_memmap; + + object_initialize_child(obj, "sysctrl", &s->sysctrl, TYPE_MT7628_SYSCTRL); + object_initialize_child(obj, "intc", &s->intc, TYPE_MT7628_INTC); + if (machine_usb(current_machine)) { + object_initialize_child(obj, "ehci", &s->ehci, TYPE_PLATFORM_EHCI); + } +} + +static void mt7628_realize(DeviceState *dev, Error **errp) +{ + mt7628State *s = MT7628(dev); + SysBusDevice *sysbusdev; + int i; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->intc), errp)) { + return; + } + CPUMIPSState *env = &s->cpu->env; + + /* interrupt control */ + sysbusdev = SYS_BUS_DEVICE(&s->intc); + sysbus_mmio_map(sysbusdev, 0, s->memmap[MT7628_DEV_INTC]); + sysbus_connect_irq(sysbusdev, 0, env->irq[MT7628_CPU_IRQ_INTC]); + qdev_pass_gpios(DEVICE(&s->intc), dev, NULL); + + /* system control */ + sysbus_realize(SYS_BUS_DEVICE(&s->sysctrl), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctrl), 0, + s->memmap[MT7628_DEV_SYSCTRL]); + + /* serial port */ + for (i = 0; i < 3; i++) { + if (serial_hd(i)) { + serial_mm_init(get_system_memory(), + s->memmap[MT7628_DEV_UART0 + i], 2, + qdev_get_gpio_in(dev, IRQ_UART0 + i), 115200, + serial_hd(i), DEVICE_NATIVE_ENDIAN); + } else { + create_unimplemented_device("uart", + s->memmap[MT7628_DEV_UART0 + i], 256); + } + } + + /* usb 2.0 host */ + if (machine_usb(current_machine)) { + sysbus_realize(SYS_BUS_DEVICE(&s->ehci), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci), 0, + s->memmap[MT7628_DEV_EHCI]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci), 0, + qdev_get_gpio_in(dev, IRQ_EHCI)); + } else { + create_unimplemented_device("ehci", s->memmap[MT7628_DEV_EHCI], + 256 * KiB); + } + + /* flash direct access */ + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); + if (dinfo) { + pflash_cfi01_register(s->memmap[MT7628_DEV_FLASH_DIRECT], + "mt7628.flash0", 4 * MiB, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 4096, 4, 0, 0, 0, 0, 0); + } else { + create_unimplemented_device("flash-direct", + s->memmap[MT7628_DEV_FLASH_DIRECT], + 4 * MiB); + } + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(unimplemented); i++) { + create_unimplemented_device(unimplemented[i].device_name, + unimplemented[i].base, + unimplemented[i].size); + } +} + +static void mt7628_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + dc->realize = mt7628_realize; + dc->user_creatable = false; +} + +static const TypeInfo mt7628_type_info = { + .name = TYPE_MT7628, + .parent = TYPE_DEVICE, + .instance_size = sizeof(mt7628State), + .instance_init = mt7628_init, + .class_init = mt7628_class_init, +}; + +static void mt7628_register_types(void) +{ + type_register_static(&mt7628_type_info); +} + +type_init(mt7628_register_types); diff --git a/hw/mips/vocore2.c b/hw/mips/vocore2.c new file mode 100644 index 0000000000..e9662224fd --- /dev/null +++ b/hw/mips/vocore2.c @@ -0,0 +1,180 @@ +/* + * QEMU/mt7628 emulation + * + * Copyright (c) 2023 Lu Hui <luhu...@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/datadir.h" +#include "hw/clock.h" +#include "hw/mips/mips.h" +#include "hw/mips/cpudevs.h" +#include "hw/mips/mt7628.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "hw/mips/bios.h" +#include "hw/loader.h" +#include "elf.h" +#include "hw/sysbus.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" +#include "qemu/error-report.h" +#include "sysemu/qtest.h" +#include "sysemu/reset.h" + +static struct _loaderparams { + int ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; +} loaderparams; + +typedef struct ResetData { + MIPSCPU *cpu; + uint64_t vector; +} ResetData; + +static uint64_t load_kernel(void) +{ + uint64_t entry, kernel_high, initrd_size; + long kernel_size; + ram_addr_t initrd_offset; + + kernel_size = load_elf(loaderparams.kernel_filename, NULL, + cpu_mips_kseg0_to_phys, NULL, + &entry, NULL, + &kernel_high, NULL, 0, EM_MIPS, 1, 0); + if (kernel_size < 0) { + error_report("could not load kernel '%s': %s", + loaderparams.kernel_filename, + load_elf_strerror(kernel_size)); + exit(1); + } + + /* load initrd */ + initrd_size = 0; + initrd_offset = 0; + if (loaderparams.initrd_filename) { + initrd_size = get_image_size(loaderparams.initrd_filename); + if (initrd_size > 0) { + initrd_offset = ROUND_UP(kernel_high, INITRD_PAGE_SIZE); + if (initrd_offset + initrd_size > loaderparams.ram_size) { + error_report("memory too small for initial ram disk '%s'", + loaderparams.initrd_filename); + exit(1); + } + initrd_size = load_image_targphys(loaderparams.initrd_filename, + initrd_offset, + loaderparams.ram_size - initrd_offset); + } + if (initrd_size == (target_ulong)-1) { + error_report("could not load initial ram disk '%s'", + loaderparams.initrd_filename); + exit(1); + } + } + return entry; +} + +static void main_cpu_reset(void *opaque) +{ + ResetData *s = (ResetData *) opaque; + CPUMIPSState *env = &s->cpu->env; + + cpu_reset(CPU(s->cpu)); + env->active_tc.PC = s->vector & ~(target_ulong) 1; + if (s->vector & 1) { + env->hflags |= MIPS_HFLAG_M16; + } +} + +static void vocore2_init(MachineState *machine) +{ + const char *kernel_filename = machine->kernel_filename; + const char *kernel_cmdline = machine->kernel_cmdline; + const char *initrd_filename = machine->initrd_filename; + mt7628State *mt7628; + Clock *cpuclk; + MIPSCPU *cpu; + ResetData *reset_info; + + /* BIOS is not supported by this board */ + if (machine->firmware) { + error_report("BIOS not supported for this machine"); + exit(1); + } + + /* CPU limit */ + if (strcmp(machine->cpu_type, MIPS_CPU_TYPE_NAME("24KEc")) != 0) { + error_report("This board can only be used with 24KEc CPU"); + exit(1); + } + + /* RAM limit */ + if (machine->ram_size > 256 * MiB) { + error_report("mt7628: memory size must not exceed 256MiB"); + } + + mt7628 = MT7628(object_new(TYPE_MT7628)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(mt7628)); + object_unref(OBJECT(mt7628)); + + /* CPU Clock */ + cpuclk = clock_new(OBJECT(machine), "cpu-refclk"); + /* xtal 40Mhz -> cpu 580Mhz (VoCore2 use this) */ + /* xtal 25Mhz -> cpu 575Mhz */ + clock_set_hz(cpuclk, 580000000); + + /* CPU */ + cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); + cpu_mips_irq_init_cpu(cpu); + cpu_mips_clock_init(cpu); + mt7628->cpu = cpu; + + /* Mark mt7628 object realized */ + qdev_realize(DEVICE(mt7628), NULL, &error_abort); + + /* DDR */ + memory_region_add_subregion(get_system_memory(), + mt7628->memmap[MT7628_DEV_DDR], + machine->ram); + + /* Load kernel to RAM & goto kernel */ + reset_info = g_new0(ResetData, 1); + reset_info->cpu = cpu; + reset_info->vector = reset_info->cpu->env.active_tc.PC; + qemu_register_reset(main_cpu_reset, reset_info); + if (kernel_filename) { + loaderparams.ram_size = machine->ram_size; + loaderparams.kernel_filename = kernel_filename; + loaderparams.kernel_cmdline = kernel_cmdline; + loaderparams.initrd_filename = initrd_filename; + reset_info->vector = load_kernel(); + } + /* TODO: boot from flash */ +} + +static void vocore2_machine_init(MachineClass *mc) +{ + mc->desc = "VoCore2 (24KEc)"; + mc->init = vocore2_init; + mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24KEc"); + mc->default_ram_id = "vocore2.ram"; + mc->default_ram_size = 128 * MiB; + mc->min_cpus = 1; + mc->max_cpus = 1; + mc->default_cpus = 1; +} + +DEFINE_MACHINE("vocore2", vocore2_machine_init) diff --git a/include/hw/mips/mt7628.h b/include/hw/mips/mt7628.h new file mode 100644 index 0000000000..2570f87846 --- /dev/null +++ b/include/hw/mips/mt7628.h @@ -0,0 +1,77 @@ +/* + * MT7628 System on Chip emulation + * + * Copyright (C) 2023 Lu Hui <luhu...@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_MIPS_MT7628_H +#define HW_MIPS_MT7628_H + +#include "qom/object.h" +#include "hw/mips/mips.h" +#include "hw/mips/cpudevs.h" +#include "hw/mips/bios.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/misc/mt7628-sysctrl.h" +#include "hw/intc/mt7628-intc.h" + +/** + * MT7628 device list + * + * This enumeration is can be used refer to a particular device in the + * MT7628 SoC. For example, the physical memory base address for + * each device can be found in the mt7628State object in the memmap member + * using the device enum value as index. + * + * @see mt7628State + */ +enum { + MT7628_DEV_DDR, + MT7628_DEV_SYSCTRL, + MT7628_DEV_INTC, + MT7628_DEV_UART0, + MT7628_DEV_UART1, + MT7628_DEV_UART2, + MT7628_DEV_EHCI, + MT7628_DEV_FLASH_DIRECT, +}; + + +/* mt7628 cpu interrupt table */ + + +enum { + MT7628_CPU_IRQ_INTC = 2, + MT7628_CPU_IRQ_PCIE = 4, +}; + +#define TYPE_MT7628 "mt7628" +OBJECT_DECLARE_SIMPLE_TYPE(mt7628State, MT7628) + +struct mt7628State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + MIPSCPU *cpu; + const hwaddr *memmap; + mt7628SysCtrlState sysctrl; + mt7628intcState intc; + EHCISysBusState ehci; + MemoryRegion flash_direct; +}; + +#endif /* HW_MIPS_MT7628_H */ -- 2.34.5