From d7b0b2b7132690230ee5fe66e6760c2c640dc481 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 3 Apr 2019 12:48:23 -0700 Subject: [PATCH 01/27] target/arm: Change bswap_code()'s parameter to CPUARMState * Change bswap_code()'s parameter to CPUARMState * in order to accomodate upcoming changes. No functional change intended. Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- target/arm/arm_ldst.h | 12 +++++------- target/arm/cpu.c | 6 ++---- target/arm/cpu.h | 4 ++-- target/arm/translate.c | 9 +++++---- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/target/arm/arm_ldst.h b/target/arm/arm_ldst.h index 45edb108f6aaa..d34eca4987645 100644 --- a/target/arm/arm_ldst.h +++ b/target/arm/arm_ldst.h @@ -24,24 +24,22 @@ #include "qemu/bswap.h" /* Load an instruction and return it in the standard little-endian order */ -static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr, - bool sctlr_b) +static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr) { - return translator_ldl_swap(env, addr, bswap_code(sctlr_b)); + return translator_ldl_swap(env, addr, bswap_code(env)); } /* Ditto, for a halfword (Thumb) instruction */ -static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr, - bool sctlr_b) +static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr) { #ifndef CONFIG_USER_ONLY /* In big-endian (BE32) mode, adjacent Thumb instructions have been swapped within each word. Undo that now. */ - if (sctlr_b) { + if (arm_sctlr_b(env)) { addr ^= 2; } #endif - return translator_lduw_swap(env, addr, bswap_code(sctlr_b)); + return translator_lduw_swap(env, addr, bswap_code(env)); } #endif diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 32bec156f2d4f..06e43e9063f68 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -709,7 +709,6 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) { ARMCPU *ac = ARM_CPU(cpu); CPUARMState *env = &ac->env; - bool sctlr_b; if (is_a64(env)) { /* We might not be compiled with the A64 disassembler @@ -745,8 +744,7 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) info->cap_mode = cap_mode; } - sctlr_b = arm_sctlr_b(env); - if (bswap_code(sctlr_b)) { + if (bswap_code(env)) { #ifdef TARGET_WORDS_BIGENDIAN info->endian = BFD_ENDIAN_LITTLE; #else @@ -755,7 +753,7 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) } info->flags &= ~INSN_ARM_BE32; #ifndef CONFIG_USER_ONLY - if (sctlr_b) { + if (arm_sctlr_b(env)) { info->flags |= INSN_ARM_BE32; } #endif diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 677584e5da046..c67c88bbc0082 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3267,7 +3267,7 @@ static inline int cpu_mmu_index(CPUARMState *env, bool ifetch) return FIELD_EX32(env->hflags, TBFLAG_ANY, MMUIDX); } -static inline bool bswap_code(bool sctlr_b) +static inline bool bswap_code(CPUARMState *env) { #ifdef CONFIG_USER_ONLY /* BE8 (SCTLR.B = 0, TARGET_WORDS_BIGENDIAN = 1) is mixed endian. @@ -3278,7 +3278,7 @@ static inline bool bswap_code(bool sctlr_b) #ifdef TARGET_WORDS_BIGENDIAN 1 ^ #endif - sctlr_b; + arm_sctlr_b(env); #else /* All code access in ARM is little endian, and there are no loaders * doing swaps that need to be reversed diff --git a/target/arm/translate.c b/target/arm/translate.c index c8296116d4b3e..cc470d319f565 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -10676,7 +10676,7 @@ static bool insn_crosses_page(CPUARMState *env, DisasContext *s) * boundary, so we cross the page if the first 16 bits indicate * that this is a 32 bit insn. */ - uint16_t insn = arm_lduw_code(env, s->base.pc_next, s->sctlr_b); + uint16_t insn = arm_lduw_code(env, s->base.pc_next); return !thumb_insn_is_16bit(s, s->base.pc_next, insn); } @@ -10915,7 +10915,8 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } dc->pc_curr = dc->base.pc_next; - insn = arm_ldl_code(env, dc->base.pc_next, dc->sctlr_b); + insn = arm_ldl_code(env, dc->base.pc_next); + dc->insn = insn; dc->base.pc_next += 4; disas_arm_insn(dc, insn); @@ -10984,11 +10985,11 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } dc->pc_curr = dc->base.pc_next; - insn = arm_lduw_code(env, dc->base.pc_next, dc->sctlr_b); + insn = arm_lduw_code(env, dc->base.pc_next); is_16bit = thumb_insn_is_16bit(dc, dc->base.pc_next, insn); dc->base.pc_next += 2; if (!is_16bit) { - uint32_t insn2 = arm_lduw_code(env, dc->base.pc_next, dc->sctlr_b); + uint32_t insn2 = arm_lduw_code(env, dc->base.pc_next); insn = insn << 16 | insn2; dc->base.pc_next += 2; From 2dac37cbe63dd5b35bb70ae60bb06a93362c4968 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 3 Apr 2019 13:17:45 -0700 Subject: [PATCH 02/27] target/arm: Add support for SCTLR.IE used in ARMv7-R ARMv7-R can support legacy BE32 code, which can be configured by SoC vendor via SCTLR.IE bit. Add various bit and pieces needed to emulate that feature. Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- target/arm/cpu.c | 13 +++++++++++++ target/arm/cpu.h | 14 +++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 06e43e9063f68..81544ed0b98ce 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -247,6 +247,10 @@ static void arm_cpu_reset(DeviceState *dev) } env->daif = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F; + if (cpu->cfgend_instr) { + env->uncached_cpsr |= CPSR_E; + } + if (arm_feature(env, ARM_FEATURE_M)) { uint32_t initial_msp; /* Loaded from 0x0 */ uint32_t initial_pc; /* Loaded from 0x4 */ @@ -1081,6 +1085,9 @@ static Property arm_cpu_has_neon_property = static Property arm_cpu_has_dsp_property = DEFINE_PROP_BOOL("dsp", ARMCPU, has_dsp, true); +static Property arm_cpu_cfgend_instr_property = + DEFINE_PROP_BOOL("cfgend-instr", ARMCPU, cfgend_instr, false); + static Property arm_cpu_has_mpu_property = DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true); @@ -1240,6 +1247,8 @@ void arm_cpu_post_init(Object *obj) qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property); + qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_instr_property); + if (arm_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER)) { qdev_property_add_static(DEVICE(cpu), &arm_cpu_gt_cntfrq_property); } @@ -1621,6 +1630,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } } + if (cpu->cfgend_instr) { + cpu->reset_sctlr |= SCTLR_IE; + } + if (!cpu->has_el3) { /* If the has_el3 CPU property is disabled then we need to disable the * feature. diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c67c88bbc0082..d1a1ce6cd1b85 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -944,6 +944,7 @@ struct ARMCPU { * architecture version. */ bool cfgend; + bool cfgend_instr; QLIST_HEAD(, ARMELChangeHook) pre_el_change_hooks; QLIST_HEAD(, ARMELChangeHook) el_change_hooks; @@ -1181,6 +1182,8 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_ATA0 (1ULL << 42) /* v8.5-MemTag */ #define SCTLR_ATA (1ULL << 43) /* v8.5-MemTag */ #define SCTLR_DSSBS (1ULL << 44) /* v8.5 */ +#define SCTLR_IE (1U << 31) + #define CPTR_TCPAC (1U << 31) #define CPTR_TTA (1U << 20) @@ -3155,6 +3158,11 @@ static inline bool arm_cpu_data_is_big_endian_a64(int el, uint64_t sctlr) return sctlr & (el ? SCTLR_EE : SCTLR_E0E); } +static inline bool arm_sctlr_ie(CPUARMState *env) +{ + return env->cp15.sctlr_el[1] & SCTLR_IE; +} + /* Return true if the processor is in big-endian mode. */ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) { @@ -3280,10 +3288,10 @@ static inline bool bswap_code(CPUARMState *env) #endif arm_sctlr_b(env); #else - /* All code access in ARM is little endian, and there are no loaders - * doing swaps that need to be reversed + /* + * ARMv7-R profile speicifes IE bit to enable instruction swapping */ - return 0; + return arm_sctlr_ie(env); #endif } From 09895eb18c86755357e5995a8534e486a256319c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 3 Apr 2019 13:24:05 -0700 Subject: [PATCH 03/27] target/arm: gdbstub: Swap register endiannes if SCTLR.IE is set We need to swap endiannes of register value data sent to GDB when SCTLR.IE is set, since GDB will be running in BE32 and all of the instruction memory is going to be big-endian, so it'll expect register data to be as well. Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- target/arm/gdbstub.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index ecfa88f8e605e..b319b20e7a164 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -27,6 +27,11 @@ typedef struct RegisterSysregXmlParam { int n; } RegisterSysregXmlParam; +static uint32_t arm_gdb_get_reg32(CPUARMState *env, uint32_t reg) +{ + return arm_sctlr_ie(env) ? bswap32(reg) : reg; +} + /* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect whatever the target description contains. Due to a historical mishap the FPA registers appear in between core integer regs and the CPSR. @@ -40,7 +45,8 @@ int arm_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) if (n < 16) { /* Core integer register. */ - return gdb_get_reg32(mem_buf, env->regs[n]); + return gdb_get_reg32(mem_buf, + arm_gdb_get_reg32(env, env->regs[n])); } if (n < 24) { /* FPA registers. */ @@ -59,9 +65,10 @@ int arm_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) case 25: /* CPSR, or XPSR for M-profile */ if (arm_feature(env, ARM_FEATURE_M)) { - return gdb_get_reg32(mem_buf, xpsr_read(env)); - } else { return gdb_get_reg32(mem_buf, cpsr_read(env)); + } else { + return gdb_get_reg32(mem_buf, + arm_gdb_get_reg32(env, cpsr_read(env))); } } /* Unknown register. */ @@ -74,7 +81,7 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) CPUARMState *env = &cpu->env; uint32_t tmp; - tmp = ldl_p(mem_buf); + tmp = arm_gdb_get_reg32(env, ldl_p(mem_buf)); /* Mask out low bit of PC to workaround gdb bugs. This will probably cause problems if we ever implement the Jazelle DBX extensions. */ From a3529f1a61f9fe71f71598bd6196b4ccbc8dad74 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 3 Jul 2019 11:21:25 -0700 Subject: [PATCH 04/27] bitmap: Add for_each_set_bit macro Add for_each_set_bit macro as found in Linux kernel Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- include/qemu/bitops.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index f55ce8b320b24..4d1914c2993a4 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -580,4 +580,10 @@ static inline uint64_t half_unshuffle64(uint64_t x) return x; } +#define for_each_set_bit(bit, addr, size) \ + for ((bit) = find_first_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) + + #endif From 043d8cfee9ee6c26dd84cd44ea15fdcc79da8afb Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 3 Apr 2019 10:55:21 -0700 Subject: [PATCH 05/27] hw/char: Add support for Hercules RTP module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/char/Makefile.objs | 1 + hw/char/hercules_rtp.c | 84 ++++++++++++++++++++++++++++++++++ include/hw/char/hercules_rtp.h | 29 ++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 hw/char/hercules_rtp.c create mode 100644 include/hw/char/hercules_rtp.h diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 9e9a6c1affb6b..67751dd3ffe26 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -30,6 +30,7 @@ common-obj-$(CONFIG_LM32) += lm32_juart.o common-obj-$(CONFIG_LM32) += lm32_uart.o common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o sclpconsole-lm.o +common-obj-$(CONFIG_HERCULES) += hercules_rtp.o obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o obj-$(CONFIG_PSERIES) += spapr_vty.o diff --git a/hw/char/hercules_rtp.c b/hw/char/hercules_rtp.c new file mode 100644 index 0000000000000..cf2d1ccc6cab6 --- /dev/null +++ b/hw/char/hercules_rtp.c @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "hw/char/hercules_rtp.h" +#include "chardev/char-fe.h" +#include "qemu/log.h" + +#define RTPDDMW 0x2c + +static uint64_t hercules_rtp_read(void *opaque, hwaddr offset, unsigned size) +{ + return 0; +} + +static void hercules_rtp_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + HerculesRTPState *s = HERCULES_RTP(opaque); + uint8_t ch = value; + + switch (offset) { + case RTPDDMW: + qemu_chr_fe_write_all(&s->chr, &ch, sizeof(ch)); + break; + } +} + +static const MemoryRegionOps hercules_rtp_ops = { + .read = hercules_rtp_read, + .write = hercules_rtp_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void hercules_rtp_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + HerculesRTPState *s = HERCULES_RTP(obj); + + memory_region_init_io(&s->iomem, obj, &hercules_rtp_ops, s, + TYPE_HERCULES_RTP ".io", HERCULES_RTP_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void hercules_rtp_realize(DeviceState *dev, Error **errp) +{ + HerculesRTPState *s = HERCULES_RTP(dev); + + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, s, NULL, true); +} + +static Property hercules_rtp_properties[] = { + DEFINE_PROP_CHR("chardev", HerculesRTPState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void hercules_rtp_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = hercules_rtp_realize; + device_class_set_props(dc, hercules_rtp_properties); +} + +static const TypeInfo hercules_rtp_info = { + .name = TYPE_HERCULES_RTP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesRTPState), + .instance_init = hercules_rtp_init, + .class_init = hercules_rtp_class_init, +}; + +static void hercules_rtp_register_types(void) +{ + type_register_static(&hercules_rtp_info); +} + +type_init(hercules_rtp_register_types) diff --git a/include/hw/char/hercules_rtp.h b/include/hw/char/hercules_rtp.h new file mode 100644 index 0000000000000..8dd6b8a9407e0 --- /dev/null +++ b/include/hw/char/hercules_rtp.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HW_HERCULES_RTP_H +#define HW_HERCULES_RTP_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" + +#define TYPE_HERCULES_RTP "hercules-rtp" +#define HERCULES_RTP(obj) OBJECT_CHECK(HerculesRTPState, (obj), \ + TYPE_HERCULES_RTP) + +/* This shares the same struct (and cast macro) as the base pl011 device */ + +typedef struct HerculesRTPState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + CharBackend chr; +} HerculesRTPState; + +#define HERCULES_RTP_SIZE 256 + +#endif From 9b6f4df4b3d403d59d129830d04a7cc939795214 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 10 Apr 2019 14:19:04 -0700 Subject: [PATCH 06/27] hw/intc: Add support for Hercules VIM module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/intc/Makefile.objs | 1 + hw/intc/hercules_vim.c | 318 +++++++++++++++++++++++++++++++++ include/hw/intc/hercules_vim.h | 78 ++++++++ 3 files changed, 397 insertions(+) create mode 100644 hw/intc/hercules_vim.c create mode 100644 include/hw/intc/hercules_vim.h diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index f726d87532763..365b78b1e541e 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -49,3 +49,4 @@ obj-$(CONFIG_ARM_GIC) += arm_gicv3_cpuif.o obj-$(CONFIG_MIPS_CPS) += mips_gic.o obj-$(CONFIG_NIOS2) += nios2_iic.o obj-$(CONFIG_OMPIC) += ompic.o +obj-$(CONFIG_HERCULES) += hercules_vim.o diff --git a/hw/intc/hercules_vim.c b/hw/intc/hercules_vim.c new file mode 100644 index 0000000000000..79a356336c353 --- /dev/null +++ b/hw/intc/hercules_vim.c @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "cpu.h" + +#include "hw/intc/hercules_vim.h" + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesVimRegisters { + IRQINDEX = 0x00, + FIQINDEX = 0x04, + FIRQPR0 = 0x10, + FIRQPR1 = 0x14, + FIRQPR2 = 0x18, + FIRQPR3 = 0x1C, + REQENASET0 = 0x30, + REQENASET1 = 0x34, + REQENASET2 = 0x38, + REQENASET3 = 0x3C, + REQENACLR0 = 0x40, + REQENACLR1 = 0x44, + REQENACLR2 = 0x48, + REQENACLR3 = 0x4C, + IRQVECREG = 0x70, + FIQVECREG = 0x74, + CHANCTRL0 = 0x80, + CHANCTRL31 = 0xFC, +}; + +static void hercules_vim_update_line(HerculesVimState *s, + unsigned long *mask, + qemu_irq irq) +{ + int group; + + for (group = 0; group < HERCULES_NUM_IRQ_GROUP; group++) { + if (s->intreq[group] & s->reqena[group] & mask[group]) { + qemu_irq_raise(irq); + return; + } + } + + qemu_irq_lower(irq); +} + +/* Update interrupts. */ +static void hercules_vim_update(HerculesVimState *s) +{ + hercules_vim_update_line(s, s->rpqrif, s->irq); + hercules_vim_update_line(s, s->firqpr, s->fiq); +} + +static void hercules_vim_set_irq(void *opaque, int irq, int level) +{ + HerculesVimState *s = opaque; + int group, index; + unsigned long bit; + /* + * Map physical IRQ line to a channel + */ + irq = s->chanctrl[irq]; + + group = irq / HERCULES_IRQ_GROUP_WIDTH; + index = irq % HERCULES_IRQ_GROUP_WIDTH; + bit = BIT(index); + + if (level) { + s->intreq[group] |= bit; + } else { + s->intreq[group] &= ~bit; + } + + if (unlikely(s->firqpr[group] & bit)) { + hercules_vim_update_line(s, s->firqpr, s->fiq); + } else { + hercules_vim_update_line(s, s->rpqrif, s->irq); + } +} + +static uint32_t hercules_vim_line_index(HerculesVimState *s, + unsigned long *mask) +{ + int group; + + for (group = 0; group < HERCULES_NUM_IRQ_GROUP; group++) { + const unsigned long active = s->intreq[group] & mask[group]; + if (active) { + return HERCULES_IRQ_GROUP_WIDTH * group + ctzl(active) + 1; + } + } + + return 0; +} + +static uint32_t hercules_vim_irq_index(HerculesVimState *s) +{ + return hercules_vim_line_index(s, s->rpqrif); +} + +static uint32_t hercules_vim_fiq_index(HerculesVimState *s) +{ + return hercules_vim_line_index(s, s->firqpr); +} + +static uint32_t hercules_vim_read_vector(HerculesVimState *s, int idx) +{ + /* + * FIXME: Is this the best way to deal with endianness RM57 vs + * TMS570 + */ + const bool big_endian = s->iomem.ops->endianness == DEVICE_BIG_ENDIAN; + const uint32_t *vector = &s->vectors[idx]; + + return big_endian ? ldl_be_p(vector) : ldl_le_p(vector); +} + +static uint64_t hercules_vim_read(void *opaque, hwaddr offset, unsigned size) +{ + HerculesVimState *s = opaque; + + switch (offset) { + case IRQINDEX: + return hercules_vim_irq_index(s); + case FIQINDEX: + return hercules_vim_fiq_index(s); + case FIRQPR0: + return s->firqpr0; + case FIRQPR1: + return s->firqpr1; + case FIRQPR2: + return s->firqpr2; + case FIRQPR3: + return s->firqpr3; + case IRQVECREG: + return hercules_vim_read_vector(s, hercules_vim_irq_index(s)); + case FIQVECREG: + return hercules_vim_read_vector(s, hercules_vim_fiq_index(s)); + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_vim_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesVimState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case IRQINDEX: + case IRQVECREG: + case FIQVECREG: + case FIQINDEX: + /* no-op */ + break; + case FIRQPR0: + s->firqpr0 = val; + s->rpqrif0 = ~val; + break; + case FIRQPR1: + s->firqpr1 = val; + s->rpqrif1 = ~val; + break; + case FIRQPR2: + s->firqpr2 = val; + s->rpqrif2 = ~val; + break; + case FIRQPR3: + s->firqpr3 = val; + s->rpqrif3 = ~val; + break; + case REQENASET0: + s->reqena0 |= val; + hercules_vim_update(s); + break; + case REQENACLR0: + s->reqena0 &= ~val; + hercules_vim_update(s); + break; + case REQENASET1: + s->reqena1 |= val; + hercules_vim_update(s); + break; + case REQENACLR1: + s->reqena1 &= ~val; + hercules_vim_update(s); + break; + case REQENASET2: + s->reqena2 |= val; + hercules_vim_update(s); + break; + case REQENACLR2: + s->reqena2 &= ~val; + hercules_vim_update(s); + break; + case REQENASET3: + s->reqena3 |= val; + hercules_vim_update(s); + break; + case REQENACLR3: + s->reqena3 &= ~val; + hercules_vim_update(s); + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_vim_ops = { + .read = hercules_vim_read, + .write = hercules_vim_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_vim_reset(DeviceState *d) +{ + HerculesVimState *s = HERCULES_VIM(d); + int i; + + memset(s->vectors, 0, sizeof(s->vectors)); + + for (i = 0; i < HERCULES_NUM_IRQ_GROUP; i++) { + s->intreq[i] = 0; + s->reqena[i] = 0; + s->firqpr[i] = 0; + s->rpqrif[i] = ~s->firqpr[i]; + } + + s->firqpr[0] = 0b11; + s->rpqrif[0] = ~s->firqpr[0]; + + for (i = 0; i < HERCULES_NUM_IRQ; i++) { + s->chanctrl[i] = i; + } + + hercules_vim_update(s); +} + +static void hercules_vim_initfn(Object *obj) +{ + HerculesVimState *s = HERCULES_VIM(obj); + + sysbus_init_child_obj(obj, "ecc-regs", &s->ecc, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); +} + +static void hercules_vim_realize(DeviceState *dev, Error **errp) +{ + HerculesVimState *s = HERCULES_VIM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + qdev_prop_set_string(DEVICE(&s->ecc), "name", "ecc-regs"); + qdev_prop_set_uint64(DEVICE(&s->ecc), "size", 256); + object_property_set_bool(OBJECT(&s->ecc), true, "realized", &error_fatal); + sysbus_init_mmio(sbd, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ecc), 0)); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_vim_ops, + s, "hercules.vim", 256); + sysbus_init_mmio(sbd, &s->iomem); + + memory_region_init_ram_ptr(&s->ram, OBJECT(dev), TYPE_HERCULES_VIM ".ram", + sizeof(s->vectors), s->vectors); + sysbus_init_mmio(sbd, &s->ram); + + qdev_init_gpio_in(dev, hercules_vim_set_irq, HERCULES_NUM_IRQ); + + sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->fiq); +} + +static void hercules_vim_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_vim_reset; + dc->realize = hercules_vim_realize; +} + +static const TypeInfo hercules_vim_info = { + .name = TYPE_HERCULES_VIM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesVimState), + .instance_init = hercules_vim_initfn, + .class_init = hercules_vim_class_init, +}; + +static void hercules_vim_register_types(void) +{ + type_register_static(&hercules_vim_info); +} + +type_init(hercules_vim_register_types) diff --git a/include/hw/intc/hercules_vim.h b/include/hw/intc/hercules_vim.h new file mode 100644 index 0000000000000..66ec8dae9a58c --- /dev/null +++ b/include/hw/intc/hercules_vim.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_VIM_H +#define HERCULES_VIM_H + +#include "hw/misc/unimp.h" + +enum HerculesVimInterruprs { + HERCULES_ESM_HIGH_LEVEL_IRQ = 0, + HERCULES_RTI_COMPARE0_IRQ = 2, + HERCULES_RTI_COMPARE1_IRQ = 3, + HERCULES_RTI_COMPARE2_IRQ = 4, + HERCULES_RTI_COMPARE3_IRQ = 5, + HERCULES_RTI_OVERFLOW0_IRQ = 6, + HERCULES_RTI_OVERFLOW1_IRQ = 7, + HERCULES_RTI_TIME_BASE_IRQ = 8, + + HERCULES_ESM_LOW_LEVEL_IRQ = 20, + HERCULES_SSI_IRQ = 21, + HERCULES_MIBSPI1_L0_IRQ = 12, + HERCULES_MIBSPI1_L1_IRQ = 26, + HERCULES_MIBSPI2_L0_IRQ = 17, + HERCULES_MIBSPI2_L1_IRQ = 30, + HERCULES_MIBSPI3_L0_IRQ = 37, + HERCULES_MIBSPI3_L1_IRQ = 38, + HERCULES_MIBSPI4_L0_IRQ = 49, + HERCULES_MIBSPI4_L1_IRQ = 54, + HERCULES_MIBSPI5_L0_IRQ = 53, + HERCULES_MIBSPI5_L1_IRQ = 56, + + HERCULES_NUM_IRQ = 128, + + HERCULES_IRQ_GROUP_WIDTH = BITS_PER_LONG, + HERCULES_NUM_IRQ_GROUP = HERCULES_NUM_IRQ / BITS_PER_LONG, +}; + +#define HERCULES_VIM_BITFIELD(_name) \ + union { \ + unsigned long _name[HERCULES_NUM_IRQ_GROUP]; \ + struct QEMU_PACKED { \ + uint32_t _name##0; \ + uint32_t _name##1; \ + uint32_t _name##2; \ + uint32_t _name##3; \ + }; \ + } + +typedef struct HerculesVimState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion ram; + UnimplementedDeviceState ecc; + uint32_t vectors[HERCULES_NUM_IRQ]; + + HERCULES_VIM_BITFIELD(intreq); + HERCULES_VIM_BITFIELD(reqena); + HERCULES_VIM_BITFIELD(firqpr); + /* + * firqpr inverted + */ + HERCULES_VIM_BITFIELD(rpqrif); + + uint8_t chanctrl[HERCULES_NUM_IRQ]; + + qemu_irq irq; + qemu_irq fiq; +} HerculesVimState; + +#define TYPE_HERCULES_VIM "ti-hercules-vim" +#define HERCULES_VIM(obj) OBJECT_CHECK(HerculesVimState, (obj), \ + TYPE_HERCULES_VIM) +#endif From 3dfd92cb03ac0197d498f63c8acbc7001334d600 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 8 Apr 2019 21:15:15 -0700 Subject: [PATCH 07/27] hw/misc: Add support for Hercules System module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 1 + hw/misc/hercules_system.c | 367 ++++++++++++++++++++++++++++++ include/hw/misc/hercules_system.h | 55 +++++ 3 files changed, 423 insertions(+) create mode 100644 hw/misc/hercules_system.c create mode 100644 include/hw/misc/hercules_system.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index b25181b7113bc..35cc1464c301b 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -86,6 +86,7 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_xdma.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o common-obj-$(CONFIG_MSF2) += msf2-sysreg.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o +common-obj-$(CONFIG_HERCULES) += hercules_system.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_system.c b/hw/misc/hercules_system.c new file mode 100644 index 0000000000000..7335adccea802 --- /dev/null +++ b/hw/misc/hercules_system.c @@ -0,0 +1,367 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_system.h" + +#define NAME_SIZE 20 + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesSysRegisters { + CSDIS = 0x30, + CSDISSET = 0x34, + CSDISCLR = 0x38, + GHVSRC = 0x48, + CSVSTAT = 0x54, + MSTGCR = 0x58, + MINITGCR = 0x5C, + MSINENA = 0x60, + MSTCGSTAT = 0x68, + MSTDONE = BIT(0), + MINIDONE = BIT(8), + MINISTAT = 0x6C, + PLLCTL1 = 0x70, + ROS = BIT(31), + SSIR1 = 0xB0, + SSIR2 = 0xB4, + SSIR3 = 0xB8, + SSIR4 = 0xBC, + SYSECR = 0xE0, + SYSESR = 0xE4, + PORST = BIT(15), + DBGRST = BIT(11), + ICRST = BIT(7), + CPURST = BIT(5), + SWRST = BIT(4), + EXTRST = BIT(3), + GLBSTAT = 0xEC, + RFSLIP = BIT(8), + SSIVEC = 0xF4, +}; + +#define SYSECR_RESET(v) extract32(v, 14, 2) + +enum HerculesSys2Registers { + PLLCTL3 = 0x00, +}; + +static uint64_t hercules_sys_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesSystemState *s = opaque; + + switch (offset) { + case CSDIS: + case CSDISSET: + return s->csdis; + case GHVSRC: + return s->ghvsrc; + case CSVSTAT: + return (~s->csdis) & 0xff; + case MSTGCR: + return s->mstgcr; + case MINITGCR: + return s->minitgcr; + case MSINENA: + return s->msinena; + case MINISTAT: + return s->ministat; + case PLLCTL1: + return s->pllctl1; + case MSTCGSTAT: + return s->mstcgstat; + case SSIR1: + case SSIR2: + case SSIR3: + case SSIR4: + return 0; + case SYSESR: + return s->sysesr; + case GLBSTAT: + return s->glbstat; + case SSIVEC: + qemu_irq_lower(s->irq); + return 0; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_sys_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesSystemState *s = opaque; + const uint32_t val = val64; + unsigned int reset; + + switch (offset) { + case CSDIS: + s->csdis = val; + break; + case CSDISSET: + s->csdis |= val; + break; + case CSDISCLR: + s->csdis &= ~val; + break; + case GHVSRC: + s->ghvsrc = val; + break; + case MSTGCR: + s->mstgcr = val; + break; + case MINITGCR: + s->minitgcr = val; + break; + case MSINENA: + s->msinena = val; + if (val & 0x1 && s->minitgcr & 0xA) { + s->ministat = 0x100; + } + break; + case MINISTAT: + s->ministat &= ~val; + break; + case PLLCTL1: + s->pllctl1 = val; + + if (!s->ghvsrc && !(s->pllctl1 & ROS)) { + s->glbstat |= RFSLIP; + qemu_irq_raise(s->pll1_slip_error); + } + break; + case MSTCGSTAT: + s->mstcgstat &= ~val; + break; + case SSIR1: + case SSIR2: + case SSIR3: + case SSIR4: + /* + * TODO: We might want to emulate the fact that writes to this + * register are actually keyed + */ + qemu_irq_raise(s->irq); + break; + case SYSECR: + reset = SYSECR_RESET(val); + if (reset != 0x1) { + s->sysesr |= SWRST; + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + break; + case SYSESR: + s->sysesr &= ~val; + break; + case GLBSTAT: + s->glbstat &= ~val; + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_system_sys_ops = { + .read = hercules_sys_read, + .write = hercules_sys_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static uint64_t hercules_sys2_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesSystemState *s = opaque; + + switch (offset) { + case PLLCTL3: + return s->pllctl3; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_sys2_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesSystemState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case PLLCTL3: + s->pllctl3 = val; + + if (!s->ghvsrc) { + s->glbstat |= RFSLIP; + qemu_irq_raise(s->pll2_slip_error); + } + break; + default: + qemu_log_bad_offset(offset); + } +} + + +static const MemoryRegionOps hercules_system_sys2_ops = { + .read = hercules_sys2_read, + .write = hercules_sys2_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_system_set_signal(void *opaque, int sig, int level) +{ + HerculesSystemState *s = opaque; + + CPUState *cpu = qemu_get_cpu(0); + + switch (sig) { + case HERCULES_SYSTEM_ICRST: + s->sysesr |= ICRST; + cpu_reset(cpu); + break; + case HERCULES_SYSTEM_CPURST: + s->sysesr |= CPURST; + cpu_reset(cpu); + break; + case HERCULES_SYSTEM_MSTDONE: + s->mstcgstat |= MSTDONE; + break; + } +} + +static void hercules_system_initfn(Object *obj) +{ + HerculesSystemState *s = HERCULES_SYSTEM(obj); + int i; + + for (i = 0; i < HERCULES_SYSTEM_NUM_PCRS; i++) { + sysbus_init_child_obj(obj, "pcr[*]", &s->pcr[i], + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); + } + + s->sysesr |= PORST; +} + +static void hercules_system_realize(DeviceState *dev, Error **errp) +{ + HerculesSystemState *s = HERCULES_SYSTEM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + char name[NAME_SIZE]; + DeviceState *d; + int i; + + memory_region_init_io(&s->sys, OBJECT(dev), &hercules_system_sys_ops, + s, TYPE_HERCULES_SYSTEM ".io.sys", + HERCULES_SYSTEM_SYS_SIZE); + sysbus_init_mmio(sbd, &s->sys); + + memory_region_init_io(&s->sys2, OBJECT(dev), &hercules_system_sys2_ops, + s, TYPE_HERCULES_SYSTEM ".io.sys2", + HERCULES_SYSTEM_SYS2_SIZE); + sysbus_init_mmio(sbd, &s->sys2); + + for (i = 0; i < HERCULES_SYSTEM_NUM_PCRS; i++) { + d = DEVICE(&s->pcr[i]); + snprintf(name, NAME_SIZE, "pcr%d", i); + qdev_prop_set_string(d, "name", name); + qdev_prop_set_uint64(d, "size", HERCULES_SYSTEM_PCR_SIZE); + object_property_set_bool(OBJECT(d), true, "realized", &error_fatal); + + sysbus_init_mmio(sbd, sysbus_mmio_get_region(SYS_BUS_DEVICE(d), 0)); + } + + sysbus_init_irq(sbd, &s->irq); + + qdev_init_gpio_in(dev, hercules_system_set_signal, + HERCULES_SYSTEM_NUM_SIGNALS); + + sysbus_init_irq(sbd, &s->pll1_slip_error); + sysbus_init_irq(sbd, &s->pll2_slip_error); +} + +static void hercules_system_reset(DeviceState *d) +{ + HerculesSystemState *s = HERCULES_SYSTEM(d); + + /* + * If this wasn't a SW reset or POR reset, set DBGRST for now + */ + if (s->sysesr == 0) { + s->sysesr |= DBGRST; + } + + s->minitgcr = 0x5; + s->msinena = 0; + s->ministat = 0; + s->csdis = 0b11001110; + s->mstcgstat = MINIDONE; + s->mstgcr = 0x5; + + qemu_irq_lower(s->irq); +} + +static void hercules_system_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_system_reset; + dc->realize = hercules_system_realize; +} + +static const TypeInfo hercules_system_info = { + .name = TYPE_HERCULES_SYSTEM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesSystemState), + .instance_init = hercules_system_initfn, + .class_init = hercules_system_class_init, +}; + +static void hercules_system_register_types(void) +{ + type_register_static(&hercules_system_info); +} + +type_init(hercules_system_register_types) diff --git a/include/hw/misc/hercules_system.h b/include/hw/misc/hercules_system.h new file mode 100644 index 0000000000000..3fc046f42dad9 --- /dev/null +++ b/include/hw/misc/hercules_system.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_SYSTEM_H +#define HERCULES_SYSTEM_H + +#include "hw/misc/unimp.h" + +enum { + HERCULES_SYSTEM_SYS_SIZE = 256, + HERCULES_SYSTEM_SYS2_SIZE = 256, + HERCULES_SYSTEM_PCR_SIZE = 2 * 1024, + HERCULES_SYSTEM_NUM_PCRS = 3, +}; + +enum HerculesSystemSignals { + HERCULES_SYSTEM_ICRST, + HERCULES_SYSTEM_CPURST, + HERCULES_SYSTEM_MSTDONE, + HERCULES_SYSTEM_NUM_SIGNALS, +}; + +typedef struct HerculesSystemState { + SysBusDevice parent_obj; + + uint32_t csdis; + uint32_t minitgcr; + uint32_t msinena; + uint32_t ministat; + uint32_t sysesr; + uint32_t mstcgstat; + uint32_t mstgcr; + + uint32_t ghvsrc; + uint32_t glbstat; + uint32_t pllctl1; + uint32_t pllctl3; + + MemoryRegion sys; + MemoryRegion sys2; + UnimplementedDeviceState pcr[HERCULES_SYSTEM_NUM_PCRS]; + + qemu_irq irq; + qemu_irq pll1_slip_error; + qemu_irq pll2_slip_error; +} HerculesSystemState; + +#define TYPE_HERCULES_SYSTEM "ti-hercules-system" +#define HERCULES_SYSTEM(obj) OBJECT_CHECK(HerculesSystemState, (obj), \ + TYPE_HERCULES_SYSTEM) +#endif From fef7320cbfb7f5cf81dfcd40397ce9f919835838 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 10 Apr 2019 16:31:45 -0700 Subject: [PATCH 08/27] hw/gpio: Add support for Hercules GIO/N2HET blocks Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/gpio/Makefile.objs | 1 + hw/gpio/hercules_gpio.c | 475 ++++++++++++++++++++++++++++++++ hw/gpio/trace-events | 4 + include/hw/gpio/hercules_gpio.h | 69 +++++ 4 files changed, 549 insertions(+) create mode 100644 hw/gpio/hercules_gpio.c create mode 100644 include/hw/gpio/hercules_gpio.h diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index 3cfc261f9b746..1094bae042e46 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -10,3 +10,4 @@ common-obj-$(CONFIG_IMX) += imx_gpio.o common-obj-$(CONFIG_RASPI) += bcm2835_gpio.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_gpio.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_gpio.o +common-obj-$(CONFIG_HERCULES) += hercules_gpio.o diff --git a/hw/gpio/hercules_gpio.c b/hw/gpio/hercules_gpio.c new file mode 100644 index 0000000000000..1cf8687f762ee --- /dev/null +++ b/hw/gpio/hercules_gpio.c @@ -0,0 +1,475 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" + +#include "hw/gpio/hercules_gpio.h" + +#include "trace.h" + +enum { + HERCULES_GIO_REGS_SIZE = 0x34, + HERCULES_GIO_GIO_SIZE = 0x20, + HERCULES_GIO_REGS_OFFSET = 0x00, + HERCULES_GIO_GIOA_OFFSET = 0x34, + HERCULES_GIO_GIOB_OFFSET = 0x54, +}; + +enum HerculesGioRegRegisters { + GIOGCR0 = 0x00, + GIOINTDET = 0x08, + GIOPOL = 0x0C, + GIOENASET = 0x10, + GIOENACLR = 0x14, + GIOLVLSET = 0x18, + GIOLVLCLR = 0x1C, + GIOFLG = 0x20, + GIOOFF1 = 0x24, + GIOOFF2 = 0x28, + GIOEMU1 = 0x2C, + GIOEMU2 = 0x30, +}; + +enum HerculesGioGioRegisters { + GIODIR = 0x00, + GIODIN = 0x04, + GIODOUT = 0x08, + GIODSET = 0x0C, + GIODCLR = 0x10, + GIOPDR = 0x14, + GIOPULDIS = 0x18, + GIOPSL = 0x1C, +}; + +enum HerculesHetRegisters { + HETDIR = 0x4C, + HETDIN = 0x50, + HETDOUT = 0x54, + HETDSET = 0x58, + HETDCLR = 0x5c, + HETLBPSEL = 0x8C, + HETLBPDIR = 0x90, + HETPINDIS = 0x94, +}; + +#define HETLBPDIR_LBPTSTENA(v) extract32(v, 16, 4) + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +static uint64_t hercules_gio_gio_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesGpio *gpio = opaque; + + switch (offset) { + case GIODIR: + return gpio->dir; + case GIODIN: + return gpio->din; + case GIODSET: + case GIODCLR: + case GIODOUT: + return gpio->dout; + case GIOPDR: + return gpio->pdr; + case GIOPULDIS: + return gpio->puldis; + case GIOPSL: + return gpio->psl; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_gio_update_din(HerculesGpio *gpio) +{ + trace_hercules_gio_update(gpio->bank, + gpio->din, gpio->dir, gpio->dout); + + gpio->din &= ~gpio->dir; + gpio->din |= gpio->dout & gpio->dir; + + trace_hercules_gio_update(gpio->bank, + gpio->din, gpio->dir, gpio->dout); +} + +static void hercules_gio_gio_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesGpio *gpio = opaque; + const uint32_t val = val64; + + switch (offset) { + case GIODIR: + gpio->dir = val; + hercules_gio_update_din(gpio); + break; + case GIODIN: + break; + case GIODOUT: + gpio->dout = val; + hercules_gio_update_din(gpio); + break; + case GIODSET: + gpio->dout |= val; + hercules_gio_update_din(gpio); + break; + case GIODCLR: + gpio->dout &= ~val; + hercules_gio_update_din(gpio); + break; + case GIOPDR: + gpio->pdr = val; + break; + case GIOPULDIS: + gpio->puldis = val; + break; + case GIOPSL: + gpio->psl = val; + break; + default: + qemu_log_bad_offset(offset); + } +} + +static uint64_t hercules_gio_reg_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesGioState *s = opaque; + + switch (offset) { + case GIOENACLR: + return s->gioena; + case GIOLVLSET: + case GIOLVLCLR: + return s->giolvl; + case GIOFLG: + return s->gioflg; + case GIOGCR0: + case GIOINTDET: + case GIOPOL: + case GIOENASET: + case GIOOFF1: + case GIOOFF2: + case GIOEMU1: + case GIOEMU2: + break; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_gio_reg_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesGioState *s = opaque; + uint32_t val = val64; + + switch (offset) { + case GIOENASET: + s->gioena |= val; + break; + case GIOENACLR: + s->gioena &= ~val; + break; + case GIOLVLSET: + s->giolvl |= val; + break; + case GIOLVLCLR: + s->giolvl &= ~val; + break; + case GIOFLG: + s->gioflg = val; + break; + case GIOGCR0: + case GIOINTDET: + case GIOPOL: + case GIOOFF1: + case GIOOFF2: + case GIOEMU1: + case GIOEMU2: + break; + default: + qemu_log_bad_offset(offset); + } +} + +static uint64_t hercules_n2het_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesN2HetState *s = opaque; + + switch (offset) { + case HETDIN: + return s->gpio.din; + case HETDIR: + return s->gpio.dir; + case HETDSET: + case HETDCLR: + case HETDOUT: + return s->gpio.dout; + case HETLBPDIR: + return s->hetlbpdir; + case HETLBPSEL: + case HETPINDIS: + /* always zero */ + break; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_n2het_update_gpios(HerculesN2HetState *s) +{ + const bool loopback = HETLBPDIR_LBPTSTENA(s->hetlbpdir) == 0xA; + + if (unlikely(loopback)) { + int i, n; + + s->gpio.din = s->gpio.dout; + + for (n = 0, i = 0; i < 16; i++, n += 2) { + const uint32_t bits = extract32(s->gpio.dout, n, 2); + const uint8_t pattern = 0b00111100; + unsigned int shift; + + if (bits == 0b00 || bits == 0b11) { + /* Nothing to do both bits are equal */ + continue; + } + + shift = bits & ~BIT(0); /* 0b01 -> 0, 0b10 -> 2 */ + + /* + * false: [n + 1] -> [n] + * true: [n] -> [n + 1] + */ + if (BIT(i) & s->hetlbpdir) { + shift += 4; + } + + s->gpio.din = deposit32(s->gpio.din, n, 2, pattern >> shift); + } + } else { + hercules_gio_update_din(&s->gpio); + } +} + +static void hercules_n2het_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesN2HetState *s = opaque; + uint32_t val = val64; + + switch (offset) { + case HETLBPSEL: + case HETPINDIS: + case HETDIN: + break; + case HETDIR: + s->gpio.dir = val; + hercules_n2het_update_gpios(s); + break; + case HETDOUT: + s->gpio.dout = val; + hercules_n2het_update_gpios(s); + break; + case HETDSET: + s->gpio.dout |= val; + hercules_n2het_update_gpios(s); + break; + case HETDCLR: + s->gpio.dout &= ~val; + hercules_n2het_update_gpios(s); + break; + case HETLBPDIR: + s->hetlbpdir = val; + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_gio_gio_ops = { + .read = hercules_gio_gio_read, + .write = hercules_gio_gio_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps hercules_gio_regs_ops = { + .read = hercules_gio_reg_read, + .write = hercules_gio_reg_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_gio_realize(DeviceState *dev, Error **errp) +{ + HerculesGioState *s = HERCULES_GIO(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->io.regs, OBJECT(dev), &hercules_gio_regs_ops, + s, TYPE_HERCULES_GIO ".io.regs", + HERCULES_GIO_REGS_SIZE); + memory_region_init_io(&s->io.gioa, OBJECT(dev), &hercules_gio_gio_ops, + &s->gpio[0], TYPE_HERCULES_GIO ".io.gioa", + HERCULES_GIO_GIO_SIZE); + memory_region_init_io(&s->io.giob, OBJECT(dev), &hercules_gio_gio_ops, + &s->gpio[1], TYPE_HERCULES_GIO ".io.giob", + HERCULES_GIO_GIO_SIZE); + + memory_region_init(&s->io.container, OBJECT(dev), + TYPE_HERCULES_GIO ".io", + HERCULES_GIO_REGS_SIZE + + HERCULES_GIO_GIO_SIZE + + HERCULES_GIO_GIO_SIZE); + + memory_region_add_subregion(&s->io.container, HERCULES_GIO_REGS_OFFSET, + &s->io.regs); + memory_region_add_subregion(&s->io.container, HERCULES_GIO_GIOA_OFFSET, + &s->io.gioa); + memory_region_add_subregion(&s->io.container, HERCULES_GIO_GIOB_OFFSET, + &s->io.giob); + + sysbus_init_mmio(sbd, &s->io.container); +} + +static void hercules_gio_reset(DeviceState *d) +{ + HerculesGioState *s = HERCULES_GIO(d); + + memset(s->gpio, 0, sizeof(s->gpio)); + + s->gpio[0].bank = 0; + s->gpio[1].bank = 1; +} + +static void hercules_gio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_gio_reset; + dc->realize = hercules_gio_realize; +} + +static const MemoryRegionOps hercules_n2het_ops = { + .read = hercules_n2het_read, + .write = hercules_n2het_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_n2het_initfn(Object *obj) +{ + HerculesN2HetState *s = HERCULES_N2HET(obj); + + sysbus_init_child_obj(obj, "n2het-ram", &s->ram, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); +} + +static void hercules_n2het_realize(DeviceState *dev, Error **errp) +{ + HerculesN2HetState *s = HERCULES_N2HET(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_n2het_ops, s, + TYPE_HERCULES_N2HET ".io", HERCULES_N2HET_REG_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + qdev_prop_set_string(DEVICE(&s->ram), "name", "n2het-ram"); + qdev_prop_set_uint64(DEVICE(&s->ram), "size", HERCULES_N2HET_RAM_SIZE); + object_property_set_bool(OBJECT(&s->ram), true, "realized", + &error_fatal); + + sysbus_init_mmio(sbd, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ram), 0)); +} + +static void hercules_n2het_reset(DeviceState *d) +{ + HerculesN2HetState *s = HERCULES_N2HET(d); + + s->hetlbpdir = 0x00050000; + + uint32_t bank = s->gpio.bank; + memset(&s->gpio, 0, sizeof(s->gpio)); + s->gpio.bank = bank; +} + +static void hercules_n2het_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_n2het_reset; + dc->realize = hercules_n2het_realize; +} + +static const TypeInfo hercules_gio_info = { + .name = TYPE_HERCULES_GIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesGioState), + .class_init = hercules_gio_class_init, +}; + +static const TypeInfo hercules_n2het_info = { + .name = TYPE_HERCULES_N2HET, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesN2HetState), + .instance_init = hercules_n2het_initfn, + .class_init = hercules_n2het_class_init, +}; + +static void hercules_gpio_register_types(void) +{ + type_register_static(&hercules_gio_info); + type_register_static(&hercules_n2het_info); +} + +type_init(hercules_gpio_register_types) diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index c1271fdfb27b7..3a237b053eaf4 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -5,3 +5,7 @@ nrf51_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PR nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 + +# hercules_gpio.c + +hercules_gio_update(uint32_t bank, uint32_t din, uint32_t dir, uint32_t dout) "bank %" PRId32 " din 0x%" PRIx32 " dir 0x%" PRIx32 " dout 0x%" PRIx32 diff --git a/include/hw/gpio/hercules_gpio.h b/include/hw/gpio/hercules_gpio.h new file mode 100644 index 0000000000000..9af4d64aaa1ce --- /dev/null +++ b/include/hw/gpio/hercules_gpio.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_GPIO_H +#define HERCULES_GPIO_H + +#include "hw/misc/unimp.h" + +typedef struct HerculesGpio { + uint32_t dir; + uint32_t din; + uint32_t dout; + uint32_t dset; + uint32_t dclr; + uint32_t pdr; + uint32_t puldis; + uint32_t psl; + + unsigned int bank; +} HerculesGpio; + +typedef struct HerculesGioState { + SysBusDevice parent_obj; + + /* CTRL registers */ + struct { + MemoryRegion gioa; + MemoryRegion giob; + MemoryRegion regs; + MemoryRegion container; + } io; + + HerculesGpio gpio[2]; + + uint32_t gioena; + uint32_t giolvl; + uint32_t gioflg; + +} HerculesGioState; + +enum { + HERCULES_N2HET_REG_SIZE = 256, + HERCULES_N2HET_RAM_SIZE = 128 * 1024, +}; + +typedef struct HerculesN2HetState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t hetlbpdir; + HerculesGpio gpio; + + UnimplementedDeviceState ram; +} HerculesN2HetState; + +#define TYPE_HERCULES_GIO "ti-hercules-gio" +#define HERCULES_GIO(obj) OBJECT_CHECK(HerculesGioState, (obj), \ + TYPE_HERCULES_GIO) + +#define TYPE_HERCULES_N2HET "ti-hercules-n2het" +#define HERCULES_N2HET(obj) OBJECT_CHECK(HerculesN2HetState, (obj), \ + TYPE_HERCULES_N2HET) + +#endif From 47df0ec3b5fac891e745e756954de917a3841356 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Apr 2019 15:34:01 -0700 Subject: [PATCH 09/27] hw/timer: Add support for Hercules RTI module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/timer/Makefile.objs | 1 + hw/timer/hercules_rti.c | 488 ++++++++++++++++++++++++++++++++ include/hw/timer/hercules_rti.h | 108 +++++++ 3 files changed, 597 insertions(+) create mode 100644 hw/timer/hercules_rti.c create mode 100644 include/hw/timer/hercules_rti.h diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index dece235fd72e6..f1e38b9bc6a3f 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -35,3 +35,4 @@ common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o common-obj-$(CONFIG_MSF2) += mss-timer.o common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o +common-obj-$(CONFIG_HERCULES) += hercules_rti.o diff --git a/hw/timer/hercules_rti.c b/hw/timer/hercules_rti.c new file mode 100644 index 0000000000000..46ec8f196e50a --- /dev/null +++ b/hw/timer/hercules_rti.c @@ -0,0 +1,488 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/ptimer.h" +#include "qemu/main-loop.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" + +#include "hw/timer/hercules_rti.h" + +enum HerculesRtiRegisters { + RTIGCTRL = 0x00, + RTICOMPCTRL = 0x0C, + RTIFRC0 = 0x10, + RTICPUC0 = 0x18, + RTICPUC1 = 0x38, + COMPSEL0 = BIT(0), + COMPSEL1 = BIT(4), + COMPSEL2 = BIT(8), + COMPSEL3 = BIT(12), + COMPSEL_ALL = COMPSEL0 | COMPSEL1 | COMPSEL2 | COMPSEL3, + RTICOMP0 = 0x50, + RTIUDCP0 = 0x54, + RTISETINTENA = 0x80, + RTICLEARINTENA = 0x84, + RTIINTFLAG = 0x88, +}; + +#define CNTnEN(n) BIT(n) + +static void hercules_rti_update_irq(HerculesRtiState *s, + unsigned long changed) +{ + const uint32_t masked = s->intflag & s->intena; + int i; + + for_each_set_bit(i, &changed, 32) { + const int group = i / HERCULES_RTI_INT_PER_GROUP; + const int line = i % HERCULES_RTI_INT_PER_GROUP; + + qemu_set_irq(s->irq[group][line], masked & BIT(i)); + } +} + +static bool hercules_rti_frc_is_enabled(const HerculesRtiFrc *frc) +{ + return frc->enabled && frc->gctrl_en & frc->rti->gctrl; +} + +static void hercules_rti_set_frc(HerculesRtiFrc *frc, uint32_t value) +{ + frc->timestamp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + frc->counter = value; +} + +static void __hercules_rti_compare_event(HerculesRtiCompareModule *c, + bool update_timer) +{ + c->rti->intflag |= c->mask; + + if (c->udcp) { + const int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + hercules_rti_set_frc(c->frc, c->comp); + c->comp += c->udcp; + + if (update_timer) { + timer_mod(c->timer, now + c->udcp_ns); + } + } + + hercules_rti_update_irq(c->rti, c->mask); +} + +static void hercules_rti_compare_event(void *opaque) +{ + __hercules_rti_compare_event(opaque, true); +} + +static uint32_t hercules_rti_get_frc(HerculesRtiFrc *frc) +{ + if (hercules_rti_frc_is_enabled(frc)) { + const int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + const int64_t delta_ns = now - frc->timestamp; + const uint32_t result = frc->counter + delta_ns / frc->period; + + hercules_rti_set_frc(frc, result); + + return result; + } + + return frc->counter; +} + +static uint64_t hercules_rti_read(void *opaque, hwaddr offset, unsigned size) +{ + HerculesRtiState *s = opaque; + HerculesRtiCompareModule *c; + uint32_t frc; + + switch (offset) { + case RTIGCTRL: + return s->gctrl; + case RTIFRC0: + frc = hercules_rti_get_frc(&s->frc[0]); + return frc; + case RTICPUC0: + return s->frc[0].cpuc; + case RTICOMP0: + c = &s->compare[0]; + return c->comp; + case RTIUDCP0: + c = &s->compare[0]; + return c->udcp; + case RTISETINTENA: + case RTICLEARINTENA: + return s->intena; + case RTIINTFLAG: + return s->intflag; + default: + return 0; + } +} + +static void __hercules_rti_update_capture(HerculesRtiState *s, + unsigned long compctrl) +{ + int i; + + /* + * We go through all of the capture and compare channels specified + * by @compctrl and and either enable or disable corresponding + * timer (based on the value passed in @on) + */ + for_each_set_bit(i, &compctrl, 32) { + /* + * COMPSEL1,2,3,4 are bits 0, 4, 8 and 12, so we need to + * divide bit number by 4 to get corresponding compare module + */ + HerculesRtiCompareModule *c = &s->compare[i / 4]; + const uint32_t scale = c->frc->cpuc * c->frc->period; + + if (c->udcp) { + /* + * If RTIUDCPn is specified, let's cache its value in + * nanoseconds for future use. + */ + c->udcp_ns = c->udcp * scale; + } + + if (hercules_rti_frc_is_enabled(c->frc)) { + const int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint32_t counter = hercules_rti_get_frc(c->frc); + uint32_t delta; + + if (c->comp <= counter) { + delta = UINT32_MAX - counter + c->comp; + /* + * TODO: Emulate overflow interrupt + */ + } else { + delta = c->comp - counter; + } + + timer_mod(c->timer, now + delta * scale); + } else { + timer_del(c->timer); + } + } +} + +static uint32_t hercules_rti_compctrl(HerculesRtiState *s, unsigned int idx) +{ + /* + * All compare channels that are running off of free-running + * counter 0 will have their COMPSELn bits set to 0, so we need to + * invert COMPCTRL before we mask it by compsel to flip those + * zeros to ones + */ + return idx ? s->compctrl : ~s->compctrl; +} + +static void +hercules_rti_update_capture_cnt(HerculesRtiState *s, unsigned int idx, + uint32_t compsel) +{ + __hercules_rti_update_capture(s, hercules_rti_compctrl(s, idx) & compsel); +} + +static void hercules_rti_update_capture(HerculesRtiState *s, uint32_t gctrl, + uint32_t compsel) +{ + if (gctrl & CNTnEN(0)) { + hercules_rti_update_capture_cnt(s, 0, compsel); + } + + if (gctrl & CNTnEN(1)) { + hercules_rti_update_capture_cnt(s, 1, compsel); + } +} + +static void hercules_rti_write(void *opaque, hwaddr offset, uint64_t val64, + unsigned size) +{ + HerculesRtiState *s = opaque; + HerculesRtiCompareModule *c; + const uint32_t val = val64; + uint32_t gctrl; + uint32_t intflag; + uint32_t intena; + + switch (offset) { + case RTIGCTRL: + /* + * We only update timer settings for free-running counter + * whose state was changed by this write. "on" -> "on" and + * "off" -> "off" transitions should be a no-op + */ + gctrl = s->gctrl ^ val; + s->gctrl = val; + hercules_rti_update_capture(s, gctrl, COMPSEL_ALL); + break; + case RTIFRC0: + hercules_rti_set_frc(&s->frc[0], val); + break; + case RTICPUC0: + hercules_rti_get_frc(&s->frc[0]); + s->frc[0].cpuc = val; + hercules_rti_update_capture_cnt(s, 0, COMPSEL_ALL); + break; + case RTICPUC1: + s->frc[1].cpuc = val; + hercules_rti_update_capture_cnt(s, 1, COMPSEL_ALL); + break; + case RTICOMP0: + c = &s->compare[0]; + c->comp = val; + hercules_rti_update_capture(s, s->gctrl, COMPSEL0); + break; + case RTIUDCP0: + c = &s->compare[0]; + c->udcp = val; + hercules_rti_update_capture(s, s->gctrl, COMPSEL0); + break; + case RTISETINTENA: + intena = s->intena; + s->intena |= val; + hercules_rti_update_irq(s, intena ^ s->intena); + break; + case RTICLEARINTENA: + intena = s->intena; + s->intena &= ~val; + hercules_rti_update_irq(s, intena ^ s->intena); + break; + case RTIINTFLAG: + intflag = s->intflag; + s->intflag &= ~val; + hercules_rti_update_irq(s, intflag ^ s->intflag); + break; + default: + return; + } +} + +static const MemoryRegionOps hercules_rti_ops = { + .read = hercules_rti_read, + .write = hercules_rti_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_rti_init_irq_group(HerculesRtiState *s, int group, + int line_num) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(s); + int i; + + s->irq[group] = g_new(qemu_irq, line_num); + for (i = 0; i < line_num; i++) { + sysbus_init_irq(sbd, &s->irq[group][i]); + } +} + +static void hercules_rti_reset_irq_group(HerculesRtiState *s, int group, + int line_num) +{ + int i; + for (i = 0; i < line_num; i++) { + qemu_irq_lower(s->irq[group][i]); + } +} + +static void hercules_rti_realize(DeviceState *dev, Error **errp) +{ + HerculesRtiState *s = HERCULES_RTI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_rti_ops, s, + TYPE_HERCULES_RTI ".io", + HERCULES_RTI_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + for (i = 0; i < HERCULES_RTI_INT_LINE_COMPARE_NUM; i++) { + HerculesRtiCompareModule *c = &s->compare[i]; + + c->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hercules_rti_compare_event, + c); + c->mask = BIT(i); + c->rti = s; + } + + for (i = 0; i < HERCULES_RTI_FRC_NUM; i++) { + HerculesRtiFrc *frc = &s->frc[i]; + /* + * Hardcode to 75 Mhz clock ~13ns per tick + */ + frc->period = 13; + frc->rti = s; + frc->gctrl_en = CNTnEN(i); + } + + hercules_rti_init_irq_group(s, HERCULES_RTI_INT_GROUP_COMPARE, + HERCULES_RTI_INT_LINE_COMPARE_NUM); + + hercules_rti_init_irq_group(s, HERCULES_RTI_INT_GROUP_DMA, + HERCULES_RTI_INT_LINE_DMA_NUM); + + hercules_rti_init_irq_group(s, HERCULES_RTI_INT_GROUP_TBOVL, + HERCULES_RTI_INT_LINE_TBOVL_NUM); +} + +static void hercules_compare_adjust_frc(HerculesRtiState *s) +{ + int i; + + for (i = 0; i < HERCULES_RTI_INT_LINE_COMPARE_NUM; i++) { + s->compare[i].frc = &s->frc[!!(s->compctrl & BIT(4 * i))]; + } +} + +static void hercules_rti_reset(DeviceState *d) +{ + HerculesRtiState *s = HERCULES_RTI(d); + int i; + + s->gctrl = 0; + s->intflag = 0; + s->intena = 0; + s->compctrl = 0; + + for (i = 0; i < HERCULES_RTI_FRC_NUM; i++) { + HerculesRtiFrc *frc = &s->frc[i]; + + frc->counter = 0; + frc->cpuc = 0; + frc->timestamp = 0; + + frc->enabled = true; + } + + hercules_compare_adjust_frc(s); + + for (i = 0; i < HERCULES_RTI_INT_LINE_COMPARE_NUM; i++) { + HerculesRtiCompareModule *c = &s->compare[i]; + + timer_del(c->timer); + c->comp = 0; + c->udcp = 0; + c->udcp_ns = 0; + } + + hercules_rti_reset_irq_group(s, HERCULES_RTI_INT_GROUP_COMPARE, + HERCULES_RTI_INT_LINE_COMPARE_NUM); + + hercules_rti_reset_irq_group(s, HERCULES_RTI_INT_GROUP_DMA, + HERCULES_RTI_INT_LINE_DMA_NUM); + + hercules_rti_reset_irq_group(s, HERCULES_RTI_INT_GROUP_TBOVL, + HERCULES_RTI_INT_LINE_TBOVL_NUM); +} + +static void hercules_rti_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_rti_reset; + dc->realize = hercules_rti_realize; +} + +static const TypeInfo hercules_rti_info = { + .name = TYPE_HERCULES_RTI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesRtiState), + .class_init = hercules_rti_class_init, +}; + +static void hercules_rti_register_types(void) +{ + type_register_static(&hercules_rti_info); +} + +type_init(hercules_rti_register_types) + +void hercules_rti_counter_enable(HerculesRtiState *s, uint32_t idx, + bool enable) +{ + if (s->frc[idx].enabled != enable) { + uint32_t compctrl = hercules_rti_compctrl(s, idx) & COMPSEL_ALL; + s->frc[idx].enabled = enable; + __hercules_rti_update_capture(s, compctrl); + } +} + +static HerculesRtiCompareModule * +hercules_rti_next_active_compare(HerculesRtiState *s, unsigned long compctrl, + uint32_t now, uint32_t dest) +{ + HerculesRtiCompareModule *active = NULL; + uint32_t deadline = dest; + int i; + + for_each_set_bit(i, &compctrl, 32) { + HerculesRtiCompareModule *c = &s->compare[i / 4]; + + if (c->comp < now) { + /* + * skip compare channels that are in the "past" + */ + continue; + } + + if (c->comp < deadline) { + /* + * for the ones that aren't - find the soonest one to + * expire + */ + deadline = c->comp; + active = c; + } + } + + return active; +} + +void hercules_rti_counter_advance(HerculesRtiState *s, uint32_t idx, + uint32_t delta) +{ + HerculesRtiCompareModule *c; + bool needs_disabling = s->frc[idx].enabled; + uint32_t now = s->frc[idx].counter; + uint32_t dest = now + delta; + uint32_t compctrl = hercules_rti_compctrl(s, idx) & COMPSEL_ALL; + + if (needs_disabling) { + hercules_rti_counter_enable(s, idx, false); + } + + while ((c = hercules_rti_next_active_compare(s, compctrl, now, dest))) { + now = c->comp; + __hercules_rti_compare_event(c, false); + } + + s->frc[idx].counter = dest; + + if (needs_disabling) { + hercules_rti_counter_enable(s, idx, true); + } +} diff --git a/include/hw/timer/hercules_rti.h b/include/hw/timer/hercules_rti.h new file mode 100644 index 0000000000000..a21d9bd0b3ae4 --- /dev/null +++ b/include/hw/timer/hercules_rti.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_RTI_H +#define HERCULES_RTI_H + +#include "hw/ptimer.h" + +enum HerculesRtiInterruptGroup { + HERCULES_RTI_INT_GROUP_COMPARE = 0, + HERCULES_RTI_INT_GROUP_DMA, + HERCULES_RTI_INT_GROUP_RESERVED, + HERCULES_RTI_INT_GROUP_TBOVL, + HERCULES_RTI_INT_GROUP_NUM, + HERCULES_RTI_INT_PER_GROUP = 4, +}; + +enum HerculesRtiInterruptLine { + HERCULES_RTI_INT_LINE_COMPARE0 = 0, + HERCULES_RTI_INT_LINE_COMPARE1, + HERCULES_RTI_INT_LINE_COMPARE2, + HERCULES_RTI_INT_LINE_COMPARE3, + HERCULES_RTI_INT_LINE_COMPARE_NUM, + + HERCULES_RTI_INT_LINE_DMA0 = 0, + HERCULES_RTI_INT_LINE_DMA1, + HERCULES_RTI_INT_LINE_DMA2, + HERCULES_RTI_INT_LINE_DMA3, + HERCULES_RTI_INT_LINE_DMA_NUM, + + HERCULES_RTI_INT_LINE_TB = 0, + HERCULES_RTI_INT_LINE_OVL0, + HERCULES_RTI_INT_LINE_OVL1, + HERCULES_RTI_INT_LINE_TBOVL_NUM, +}; + +enum HerculesRtiInterrupt { + HERCULES_RTI_INT_COMPARE0 = 0, + HERCULES_RTI_INT_COMPARE1, + HERCULES_RTI_INT_COMPARE2, + HERCULES_RTI_INT_COMPARE3, + HERCULES_RTI_INT_DMA0, + HERCULES_RTI_INT_DMA1, + HERCULES_RTI_INT_DMA2, + HERCULES_RTI_INT_DMA3, + HERCULES_RTI_INT_TIMEBASE, + HERCULES_RTI_INT_OVERFLOW0, + HERCULES_RTI_INT_OVERFLOW1, + HERCULES_RTI_INT_NUM +}; + +enum { + HERCULES_RTI_FRC_NUM = 2, + HERCULES_RTI_SIZE = 256, +}; + +struct HerculesRtiState; +typedef struct HerculesRtiState HerculesRtiState; + +typedef struct HerculesRtiFrc { + HerculesRtiState *rti; + uint32_t counter; + uint32_t cpuc; + uint32_t period; + int64_t timestamp; + uint32_t gctrl_en; + bool enabled; +} HerculesRtiFrc; + +typedef struct HerculesRtiCompareModule { + HerculesRtiState *rti; + HerculesRtiFrc *frc; + QEMUTimer *timer; + uint32_t comp; + uint32_t udcp; + uint32_t mask; + int64_t udcp_ns; +} HerculesRtiCompareModule; + +struct HerculesRtiState { + SysBusDevice parent_obj; + + qemu_irq *irq[HERCULES_RTI_INT_GROUP_NUM]; + MemoryRegion iomem; + + uint32_t gctrl; + uint32_t intflag; + uint32_t intena; + uint32_t compctrl; + + HerculesRtiFrc frc[HERCULES_RTI_FRC_NUM]; + HerculesRtiCompareModule compare[HERCULES_RTI_INT_LINE_COMPARE_NUM]; +}; + +#define TYPE_HERCULES_RTI "ti-hercules-rti" +#define HERCULES_RTI(obj) OBJECT_CHECK(HerculesRtiState, (obj), \ + TYPE_HERCULES_RTI) + + +void hercules_rti_counter_enable(HerculesRtiState *s, uint32_t idx, + bool enable); +void hercules_rti_counter_advance(HerculesRtiState *s, uint32_t idx, + uint32_t delta); +#endif From cd020f51e0eca4da0bbdf5683345cada8efa0961 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 17 Apr 2019 15:08:28 -0700 Subject: [PATCH 10/27] hw/net: Add support for emulating Hercules EMAC Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/net/Makefile.objs | 1 + hw/net/hercules_emac.c | 653 +++++++++++++++++++++++++++++++++ include/hw/net/hercules_emac.h | 57 +++ 3 files changed, 711 insertions(+) create mode 100644 hw/net/hercules_emac.c create mode 100644 include/hw/net/hercules_emac.h diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index f2b73983eeca7..a8fd43365c0e7 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -56,3 +56,4 @@ obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o common-obj-$(CONFIG_CAN_BUS) += can/ common-obj-$(CONFIG_MSF2) += msf2-emac.o +common-obj-$(CONFIG_HERCULES) += hercules_emac.o diff --git a/hw/net/hercules_emac.c b/hw/net/hercules_emac.c new file mode 100644 index 0000000000000..0884dac69aff8 --- /dev/null +++ b/hw/net/hercules_emac.c @@ -0,0 +1,653 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "net/eth.h" +#include "sysemu/dma.h" + +#include "hw/net/hercules_emac.h" + +typedef struct QEMU_PACKED HerculesCppiDescriptor { + uint32_t next; + + uint32_t buffer_pointer; + uint16_t buffer_length; + uint16_t buffer_offset; + + uint16_t packet_length; + uint16_t flags; +} HerculesCppiDescriptor; + +#define HERCULES_CPPI_RAM_SIZE (8 * 1024) + +enum HerculesCppiDescriptorFlags { + SOP = BIT(31 - 16), + EOP = BIT(30 - 16), + OWNER = BIT(29 - 16), + EOQ = BIT(28 - 16), +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesEmacControlRegisters { + SOFTRESET = 0x04, + RESET = BIT(0), +}; + +enum HerculesEmacModuleRegister { + TXCONTROL = 0x004, + RXCONTROL = 0x014, + RXEN = BIT(0), + RXMBPENABLE = 0x100, + RXMULTEN = BIT(5), + RXBROADEN = BIT(13), + RXNOCHAIN = BIT(28), + RXUNICASTSET = 0x104, + RXUNICASTCLEAR = 0x108, + VALID = BIT(20), + MATCHFILT = BIT(19), + RXBUFFEROFFSET = 0x110, + MACCONTROL = 0x160, + MACHASH1 = 0x1D8, + MACHASH2 = 0x1DC, + MACADDRLO = 0x500, + MACADDRHI = 0x504, + MACINDEX = 0x508, + TX0HDP = 0x600, + TX7HDP = 0x61C, + RX0HDP = 0x620, + RX7HDP = 0x63C, + TX0CP = 0x640, + TX7CP = 0x65C, + RX0CP = 0x660, + RX7CP = 0x67C, +}; + +#define MACADDRLO_CHANNEL(s, idx) extract32(s->mac_lo[idx], 16, 3) +#define RXMPBENABLE_RXMULTCH(s) extract32(s->rxmbpenable, 0, 3) +#define RXMPBENABLE_BROADCH(s) extract32(s->rxmbpenable, 8, 3) + +static uint64_t hercules_emac_control_read(void *opaque, hwaddr offset, + unsigned size) +{ + qemu_log_bad_offset(offset); + return 0; +} + +static void hercules_emac_control_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + switch (offset) { + case SOFTRESET: + if (val & RESET) { + device_cold_reset(opaque); + } + break; + default: + qemu_log_bad_offset(offset); + break; + } +} + +static void hercules_emac_unmap_iov(QEMUIOVector *qiov) +{ + int i; + + for (i = 0; i < qiov->niov; i++) { + dma_memory_unmap(&address_space_memory, + qiov->iov[i].iov_base, + qiov->iov[i].iov_len, + DMA_DIRECTION_TO_DEVICE, + qiov->iov[i].iov_len); + }; +} + +static void hercules_emac_channel_process_tx(HerculesEmacState *s, int idx) +{ + NetClientState *nc = qemu_get_queue(s->nic); + QEMUIOVector qiov; + + qemu_iovec_init(&qiov, 1); + + while (s->txhdp[idx]) { + HerculesCppiDescriptor txd; + dma_addr_t addr, len; + void *chunk; + + dma_memory_read(&address_space_memory, s->txhdp[idx], &txd, + sizeof(txd)); + + addr = le32toh(txd.buffer_pointer); + len = le16toh(txd.buffer_length); + if (txd.flags & SOP) { + addr += le16toh(txd.buffer_offset); + } + + chunk = dma_memory_map(&address_space_memory, + addr, &len, DMA_DIRECTION_TO_DEVICE); + + qemu_iovec_add(&qiov, chunk, (int)len); + + if (le16toh(txd.flags) & EOP) { + qemu_sendv_packet(nc, qiov.iov, qiov.niov); + hercules_emac_unmap_iov(&qiov); + qemu_iovec_reset(&qiov); + } + /* + * We cheat here and clear ownership flag early + */ + txd.flags &= htole16(~OWNER); + if (!txd.next) { + txd.flags |= htole16(EOQ); + } + + dma_memory_write(&address_space_memory, s->txhdp[idx], &txd, + sizeof(txd)); + + s->txhdp[idx] = le32toh(txd.next); + } + /* + * Should normally be a no-op. Would be necessary for malformed + * descriptor chain (lacking EOP marker). + */ + hercules_emac_unmap_iov(&qiov); + qemu_iovec_destroy(&qiov); +} + +static uint64_t hercules_emac_module_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesEmacState *s = opaque; + NetClientState *nc = qemu_get_queue(s->nic); + unsigned int base; + uint32_t *reg; + + switch (offset) { + case TXCONTROL: + return s->txcontrol; + case RXCONTROL: + return s->rxcontrol; + case RXMBPENABLE: + return s->rxmbpenable; + case RXUNICASTSET: + /* fall-through */ + case RXUNICASTCLEAR: + return s->rxunicast; + case RXBUFFEROFFSET: + return s->rxbufferoffset; + case MACCONTROL: + return s->maccontrol; + case MACHASH1: + return s->machash[0]; + case MACHASH2: + return s->machash[1]; + case MACADDRLO: + return s->mac_lo[s->macindex]; + case MACADDRHI: + return s->mac_hi; + case MACINDEX: + return s->macindex; + default: + qemu_log_bad_offset(offset); + return 0; + /* + * Indexed register handling below + */ + case TX0HDP ... TX7HDP: + base = TX0HDP; + reg = s->txhdp; + break; + case RX0HDP ... RX7HDP: + base = RX0HDP; + reg = s->rxhdp; + break; + case TX0CP ... TX7CP: + base = TX0CP; + reg = s->txcp; + break; + case RX0CP ... RX7CP: + base = RX0CP; + reg = s->rxcp; + break; + } + + qemu_flush_queued_packets(nc); + return reg[(offset - base) / sizeof(uint32_t)]; +} + +static void hercules_emac_update_active_channels(HerculesEmacState *s) +{ + int i; + s->active_channels = 0; + + for (i = 0; i < HERCULES_EMAC_NUM_CHANNELS; i++) { + int channel = MACADDRLO_CHANNEL(s, i); + + if (s->mac_lo[i] & VALID && s->mac_lo[i] & MATCHFILT && + s->rxunicast & BIT(channel)) { + s->active_channels |= BIT(i); + } + } +} + +static void hercules_emac_module_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesEmacState *s = opaque; + unsigned int idx, base; + const uint32_t val = val64; + uint32_t *reg; + + switch (offset) { + case TXCONTROL: + s->txcontrol = val; + return; + case RXCONTROL: + s->rxcontrol = val; + return; + case RXMBPENABLE: + s->rxmbpenable = val; + return; + case RXUNICASTSET: + s->rxunicast |= val; + hercules_emac_update_active_channels(s); + return; + case RXUNICASTCLEAR: + s->rxunicast &= ~val; + hercules_emac_update_active_channels(s); + return; + case RXBUFFEROFFSET: + s->rxbufferoffset = val; + return; + case MACCONTROL: + s->maccontrol = val; + return; + case MACHASH1: + s->machash[0] = val; + return; + case MACHASH2: + s->machash[1] = val; + return; + case MACADDRLO: + s->mac_lo[s->macindex] = val; + hercules_emac_update_active_channels(s); + return; + case MACADDRHI: + s->mac_hi = val; + return; + case MACINDEX: + s->macindex = val & 0b111; + return; + default: + qemu_log_bad_offset(offset); + return; + /* + * Indexed register handling below + */ + case TX0HDP ... TX7HDP: + base = TX0HDP; + reg = s->txhdp; + break; + case RX0HDP ... RX7HDP: + base = RX0HDP; + reg = s->rxhdp; + break; + case TX0CP ... TX7CP: + base = TX0CP; + reg = s->txcp; + break; + case RX0CP ... RX7CP: + base = RX0CP; + reg = s->rxcp; + break; + } + + idx = (offset - base) / sizeof(uint32_t); + reg[idx] = val; + if (base == TX0HDP) { + hercules_emac_channel_process_tx(s, idx); + } +} + +static bool emac_can_receive(NetClientState *nc) +{ + HerculesEmacState *s = qemu_get_nic_opaque(nc); + + return s->rxcontrol & RXEN; +} + +static ssize_t hercules_emac_channel_process_rx(HerculesEmacState *s, int idx, + const uint8_t *buf, + size_t size) +{ + HerculesCppiDescriptor rxd; + size_t residue = size; + size_t chunk; + uint16_t available; + uint32_t addr; + + while (s->rxhdp[idx] && residue) { + dma_memory_read(&address_space_memory, s->rxhdp[idx], &rxd, + sizeof(rxd)); + + /* + * If this is the first buffer we are processing mark it with SOP + */ + if (residue == size) { + rxd.flags |= le16toh(SOP); + rxd.packet_length = le16toh(size); + rxd.buffer_offset = s->rxbufferoffset; + } + + available = le16toh(rxd.buffer_length) - le16toh(rxd.buffer_offset); + chunk = MIN(size, available); + + if (s->rxmbpenable & RXNOCHAIN) { + /* + * If RXNOCHAIN is set we only process as much data as + * would fit in a single buffer and drop the reset, so we + * adjust 'residue' accordingly + */ + residue = chunk; + } + + addr = le32toh(rxd.buffer_pointer) + le16toh(rxd.buffer_offset); + dma_memory_write(&address_space_memory, + addr, + buf, chunk); + + rxd.buffer_length = htole16(chunk); + rxd.flags &= htole16(~OWNER); + + residue -= chunk; + buf += chunk; + /* + * If there's no more packet data to DMA, mark this buffer + * with EOP + */ + if (!residue) { + rxd.flags |= htole16(EOP); + } + /* + * If this is the last descriptor we can process, set EOQ to + * indicate that + */ + if (!rxd.next) { + rxd.flags |= htole16(EOQ); + } + + dma_memory_write(&address_space_memory, s->rxhdp[idx], &rxd, + sizeof(rxd)); + + s->rxcp[idx] = s->rxhdp[idx]; + s->rxhdp[idx] = le32toh(rxd.next); + } + + return size; +} + +static bool hercules_emac_machash_filter(HerculesEmacState *s, + const struct eth_header *ethpacket) +{ + /* + * Taken from 32.5.37 MAC Hash Address Register 1 (MACHASH1) + */ + const uint64_t hash_fun[] = { + BIT(0) | BIT(6) | BIT(12) | BIT(18) | + BIT(24) | BIT(30) | BIT(36) | BIT(42), + + BIT(1) | BIT(7) | BIT(13) | BIT(19) | + BIT(25) | BIT(31) | BIT(37) | BIT(43), + + BIT(2) | BIT(8) | BIT(14) | BIT(20) | + BIT(26) | BIT(32) | BIT(38) | BIT(44), + + BIT(3) | BIT(9) | BIT(15) | BIT(21) | + BIT(27) | BIT(33) | BIT(39) | BIT(45), + + BIT(4) | BIT(10) | BIT(16) | BIT(22) | + BIT(28) | BIT(34) | BIT(40) | BIT(46), + + BIT(5) | BIT(11) | BIT(17) | BIT(23) | + BIT(29) | BIT(35) | BIT(41) | BIT(47), + }; + const uint64_t da = be64toh(*(const uint64_t *)ethpacket->h_dest) >> 16; + unsigned idx = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(hash_fun); i++) { + /* + * Result of XOR is going to be 1 is the number of 1's is odd + * and 0 if number of 1's is even + */ + idx |= ctpop64(da & hash_fun[i]) % 2; + idx <<= 1; + } + + return s->machash[idx / 32] & BIT(idx % 32); +} + +static ssize_t hercules_emac_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + HerculesEmacState *s = qemu_get_nic_opaque(nc); + const struct eth_header *ethpacket = (const struct eth_header *)buf; + const uint32_t *h_dest_hi = (const uint32_t *)ethpacket->h_dest; + const uint16_t *h_dest_lo = + (const uint16_t *)(ethpacket->h_dest + sizeof(uint32_t)); + unsigned long active_channels = s->active_channels; + uint32_t channel; + int i; + + /* Broadcast */ + if (s->rxmbpenable & RXBROADEN && + is_broadcast_ether_addr(ethpacket->h_dest)) { + + channel = RXMPBENABLE_BROADCH(s); + return hercules_emac_channel_process_rx(s, channel, buf, size); + } + + if (s->rxmbpenable & RXMULTEN && + is_multicast_ether_addr(ethpacket->h_dest)) { + + /* Returns true if packet should be filtered */ + if (hercules_emac_machash_filter(s, ethpacket)) { + return size; + } + + channel = RXMPBENABLE_RXMULTCH(s); + return hercules_emac_channel_process_rx(s, channel, buf, size); + } + + /* + * Both MACADDRHI stores MAC address as follows: + * + * 31-24 MACADDR2 MAC source address bits 23-16 (byte 2). + * 23-16 MACADDR3 MAC source address bits 31-24 (byte 3). + * 15-8 MACADDR4 MAC source address bits 39-32 (byte 4). + * 7-0 MACADDR5 MAC source address bits 47-40 (byte 5). + * + * so we intentionally read h_dest_hi as LE + */ + if (s->mac_hi == le32toh(*h_dest_hi)) { + for_each_set_bit(i, &active_channels, HERCULES_EMAC_NUM_CHANNELS) { + /* + * h_dest_lo is the same as h_dest_hi (see comment above) + */ + if ((s->mac_lo[i] & 0xffff) == le16toh(*h_dest_lo)) { + channel = MACADDRLO_CHANNEL(s, i); + return hercules_emac_channel_process_rx(s, channel, buf, size); + } + } + } + + return size; +} + +static void hercules_emac_set_link_status(NetClientState *nc) +{ + /* We don't do anything for now */ +} + +static void hercules_emac_initfn(Object *obj) +{ + HerculesEmacState *s = HERCULES_EMAC(obj); + + sysbus_init_child_obj(obj, "mdio", &s->mdio, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); +} + +static void emac_reset(DeviceState *d) +{ + HerculesEmacState *s = HERCULES_EMAC(d); + NetClientState *const nc = qemu_get_queue(s->nic); + + qemu_purge_queued_packets(nc); + + s->txcontrol = 0; + s->rxcontrol = 0; + s->maccontrol = 0; + s->rxbufferoffset = 0; + s->mac_hi = 0; + s->macindex = 0; + s->rxmbpenable = 0; + s->rxunicast = 0; + s->active_channels = 0; + + memset(s->mac_lo, 0, sizeof(s->mac_lo)); + memset(s->machash, 0, sizeof(s->machash)); + memset(s->txhdp, 0, sizeof(s->txhdp)); + memset(s->rxhdp, 0, sizeof(s->rxhdp)); + memset(s->txcp, 0, sizeof(s->txcp)); + memset(s->rxcp, 0, sizeof(s->rxcp)); +} + +static const MemoryRegionOps hercules_emac_module_ops = { + .read = hercules_emac_module_read, + .write = hercules_emac_module_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps hercules_emac_contrl_ops = { + .read = hercules_emac_control_read, + .write = hercules_emac_control_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static NetClientInfo net_emac_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = emac_can_receive, + .receive = hercules_emac_receive, + .link_status_changed = hercules_emac_set_link_status, +}; + +static void hercules_emac_realize(DeviceState *dev, Error **errp) +{ + HerculesEmacState *s = HERCULES_EMAC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + MemoryRegion *io; + + /* + * Init controller mmap'd interface + * 0x000 - 0x800 : emac + * 0x800 - 0x900 : ctrl + * 0x900 - 0xA00 : mdio + */ + memory_region_init_io(&s->module, OBJECT(dev), &hercules_emac_module_ops, + s, TYPE_HERCULES_EMAC ".io.module", + HERCULES_EMAC_MODULE_SIZE); + sysbus_init_mmio(sbd, &s->module); + + memory_region_init_io(&s->control, OBJECT(dev), &hercules_emac_contrl_ops, + s, TYPE_HERCULES_EMAC ".io.control", + HERCULES_EMAC_CONTROL_SIZE); + sysbus_init_mmio(sbd, &s->control); + + qdev_prop_set_string(DEVICE(&s->mdio), "name", + TYPE_HERCULES_EMAC ".io.mdio"); + qdev_prop_set_uint64(DEVICE(&s->mdio), "size", + HERCULES_EMAC_MDIO_SIZE); + object_property_set_bool(OBJECT(&s->mdio), true, "realized", &error_fatal); + io = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0); + sysbus_init_mmio(sbd, io); + + memory_region_init_ram(&s->ram, OBJECT(dev), + TYPE_HERCULES_EMAC ".cppi-ram", + HERCULES_CPPI_RAM_SIZE, &error_fatal); + sysbus_init_mmio(sbd, &s->ram); + + qemu_macaddr_default_if_unset(&s->conf.macaddr); + + s->nic = qemu_new_nic(&net_emac_info, &s->conf, + object_get_typename(OBJECT(dev)), + dev->id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static Property hercules_emac_properties[] = { + DEFINE_NIC_PROPERTIES(HerculesEmacState, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void hercules_emac_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, hercules_emac_properties); + dc->reset = emac_reset; + dc->realize = hercules_emac_realize; + + dc->desc = "Hercules EMAC Controller"; + + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); +} + +static const TypeInfo hercules_emac_info = { + .name = TYPE_HERCULES_EMAC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesEmacState), + .instance_init = hercules_emac_initfn, + .class_init = hercules_emac_class_init, +}; + +static void hercules_emac_register_types(void) +{ + type_register_static(&hercules_emac_info); +} + +type_init(hercules_emac_register_types) diff --git a/include/hw/net/hercules_emac.h b/include/hw/net/hercules_emac.h new file mode 100644 index 0000000000000..08dd36be63e19 --- /dev/null +++ b/include/hw/net/hercules_emac.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_EMAC_H +#define HERCULES_EMAC_H + +#include "net/net.h" +#include "hw/misc/unimp.h" + +enum { + HERCULES_EMAC_NUM_CHANNELS = 8, + HERCULES_EMAC_MODULE_SIZE = 2 * 1024, + HERCULES_EMAC_CONTROL_SIZE = 256, + HERCULES_EMAC_MDIO_SIZE = 256, +}; + +typedef struct HerculesEmacState { + SysBusDevice parent_obj; + + MemoryRegion module; + MemoryRegion control; + UnimplementedDeviceState mdio; + MemoryRegion ram; + + NICState *nic; + NICConf conf; + + uint32_t mac_lo[HERCULES_EMAC_NUM_CHANNELS]; + + uint32_t txcontrol; + uint32_t rxcontrol; + uint32_t maccontrol; + uint16_t rxbufferoffset; + uint32_t machash[2]; + + uint32_t mac_hi; + uint32_t macindex; + uint32_t rxmbpenable; + uint32_t rxunicast; + + uint32_t txhdp[HERCULES_EMAC_NUM_CHANNELS]; + uint32_t rxhdp[HERCULES_EMAC_NUM_CHANNELS]; + uint32_t txcp[HERCULES_EMAC_NUM_CHANNELS]; + uint32_t rxcp[HERCULES_EMAC_NUM_CHANNELS]; + + uint32_t active_channels; +} HerculesEmacState; + +#define TYPE_HERCULES_EMAC "ti-hercules-emac" +#define HERCULES_EMAC(obj) OBJECT_CHECK(HerculesEmacState, (obj), \ + TYPE_HERCULES_EMAC) + +#endif From 859c606ed9214df228b066f5ce7c733f77f69146 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sat, 20 Apr 2019 00:23:08 -0700 Subject: [PATCH 11/27] hw/ssi: Add support for Hercules MiB SPI Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/ssi/Makefile.objs | 1 + hw/ssi/hercules_spi.c | 726 ++++++++++++++++++++++++++++++++++ include/hw/ssi/hercules_spi.h | 85 ++++ 3 files changed, 812 insertions(+) create mode 100644 hw/ssi/hercules_spi.c create mode 100644 include/hw/ssi/hercules_spi.h diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs index 07a85f1967a80..6b492bcb1b696 100644 --- a/hw/ssi/Makefile.objs +++ b/hw/ssi/Makefile.objs @@ -8,3 +8,4 @@ common-obj-$(CONFIG_MSF2) += mss-spi.o common-obj-$(CONFIG_OMAP) += omap_spi.o common-obj-$(CONFIG_IMX) += imx_spi.o +common-obj-$(CONFIG_HERCULES) += hercules_spi.o diff --git a/hw/ssi/hercules_spi.c b/hw/ssi/hercules_spi.c new file mode 100644 index 0000000000000..812b869bf5212 --- /dev/null +++ b/hw/ssi/hercules_spi.c @@ -0,0 +1,726 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" + +#include "sysemu/dma.h" + +#include "hw/ssi/hercules_spi.h" + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + + +/* + * Current assumptions in this MIBSPI implementation + * + * - Only MIBSPI mode is implemented, no compatability mode + * + * - For MIBSPI mode, we are assuming that the first buffer in a + * transfer group has the same control settings as the rest (number + * of bits, shift direction, etc) + * + * - We only implement the SW trigger one-shot mode + * + * - We only are only using a single chip select per device + * + * - Only support word length of 8 / 16 + * + */ + +#define HERCULES_KEY_ENABLE 0xA +#define HERCULES_KEY_DISABLE 0x5 + +enum HerculesMibSpiRegisters { + SPIGCR0 = 0x000, + SPIGCR1 = 0x004, + SPIEN = BIT(24), + SPIINT0 = 0x008, + DMAREQEN = BIT(16), + SPIFLG = 0x010, + SPIFLG_W1C_MASK = 0x15f, + SPIPC0 = 0x014, + ENAFUN = BIT(8), + SPIPC1 = 0x018, + SPIPC8 = 0x034, + SPIDAT0 = 0x038, + SPIDAT1 = 0x03C, + SPIBUF = 0x040, + SPIFMT0 = 0x050, + SPIFMT1 = 0x054, + SPIFMT2 = 0x058, + SPIFMT3 = 0x05C, + MIBSPIE = 0x070, + RXRAM_ACCESS = BIT(16), + TGINTFLAG = 0x084, + LTGPEND = 0x094, + TG0CTRL = 0x098, + TG14CTRL = 0x0D0, + TG15CTRL = 0x0D4, + DMA0CTRL = 0x0DF, + RXDMAENA = BIT(15), + TXDMAENA = BIT(14), + DMA8CTRL = 0x0F4, + DMA0COUNT = 0x0F8, + DMA8COUNT = 0x112, + DMACNTLEN = 0x118, + TGENA = BIT(31), + ONESHOTx = BIT(30), + PAR_ECC_CTRL = 0x120, + PAR_ECC_STAT = 0x124, + UERR_FLG0 = BIT(0), + UERR_FLG1 = BIT(1), + SBE_FLG0 = BIT(8), + SBE_FLG1 = BIT(9), + UERRADDR1 = 0x128, + UERRADDR0 = 0x12C, + IOLPBKTSTCR = 0x134, + IOLPBKSTENA = 0x0A, + ECCDIAG_CTRL = 0x140, + ECCDIAG_STAT = 0x144, + SEFLG0 = BIT(0), + SEFLG1 = BIT(1), + DEFLG0 = BIT(16), + DEFLG1 = BIT(17), + SBERRADDR1 = 0x148, + SBERRADDR0 = 0x14C, +}; + +#define INTFLGRDY(n) BIT((n) + 16) + +#define TXRAM_CSNR(w) extract32(w, 16, 8) +#define TXRAM_DFSEL(w) extract32(w, 24, 2) +#define TXRAM_TXDATA(w, l) extract32(w, 0, l) +#define TXRAM_CSHOLD BIT(28) + +#define SPIFMT_CHARLEN(w) extract32(w, 0, 5) + +#define TGxCTRL_PSTART(w) extract32(w, 8, 8) + +#define IOLPBKTSTCR_IOLPBKSTENA(w) extract32(w, 8, 4) + +#define TXDMA_MAPx(w) extract32(w, 16, 4) +#define RXDMA_MAPx(w) extract32(w, 20, 4) + +#define SBE_EVT_EN(w) extract32(w, 24, 4) + +static uint16_t hercules_spi_tx_single(HerculesMibSpiState *s, uint32_t txword) +{ + const uint8_t dfsel = TXRAM_DFSEL(txword); + const uint32_t spifmt = s->spifmt[dfsel]; + const uint8_t charlen = SPIFMT_CHARLEN(spifmt); + const uint16_t txdata = TXRAM_TXDATA(txword, charlen); + + /* + * FIXME: Should probably cache this as a pivate boolean flag + */ + if (unlikely(IOLPBKTSTCR_IOLPBKSTENA(s->iolpbktstcr) == IOLPBKSTENA)) { + return txdata; + } + + return ssi_transfer(s->ssi, txdata); +} + +static bool hercules_spi_ram_big_endian(HerculesMibSpiState *s) +{ + return s->io.regs.ops->endianness == DEVICE_BIG_ENDIAN; +} + +static uint32_t hercules_spi_ram_read(HerculesMibSpiState *s, uint32_t *word) +{ + return hercules_spi_ram_big_endian(s) ? ldl_be_p(word) : ldl_le_p(word); +} + +static void hercules_spi_ram_write(HerculesMibSpiState *s, uint32_t *word, + uint32_t value) +{ + if (hercules_spi_ram_big_endian(s)) { + stl_be_p(word, value); + } else { + stl_le_p(word, value); + } +} + +static void hercules_spi_assert_cs(HerculesMibSpiState *s, + unsigned long csnr, int val) +{ + int i; + + for_each_set_bit(i, &csnr, HERCULES_SPI_NUM_CS_LINES) { + qemu_set_irq(s->cs_lines[i], val); + } +} + +static void hercules_ecc_error_raise(HerculesMibSpiState *s, + unsigned int idx, + unsigned int err_idx, + uint32_t uerr_flg, uint32_t sbe_flg) +{ + if (s->par_ecc_stat & uerr_flg && + s->uerraddr[err_idx] == idx * sizeof(uint32_t)) { + qemu_irq_raise(s->uncorrectable_error); + } + + if (s->par_ecc_stat & sbe_flg && + s->sberraddr[err_idx] == idx * sizeof(uint32_t)) { + qemu_irq_raise(s->single_bit_error); + } +} + +static void ___hercules_spi_process_tg(HerculesMibSpiState *s, unsigned int n, + unsigned int end) +{ + const bool tgena = s->tgxctrl[n] & TGENA; + const bool oneshot = s->tgxctrl[n] & ONESHOTx; + + if (tgena && oneshot) { + const unsigned int start = TGxCTRL_PSTART(s->tgxctrl[n]); + unsigned int i; + + + for (i = start; i <= end; i++) { + uint32_t txword; + uint32_t rxword; + uint8_t csnr; + + hercules_ecc_error_raise(s, i, 0, UERR_FLG0, SBE_FLG0); + + /* + * FIXME: Handle CS assertion here + */ + txword = hercules_spi_ram_read(s, &s->txram[i]); + csnr = ~TXRAM_CSNR(txword); + + hercules_spi_assert_cs(s, csnr, 0); + + rxword = hercules_spi_tx_single(s, txword); + hercules_spi_ram_write(s, &s->rxram[i], rxword); + + if (txword & TXRAM_CSHOLD) { + continue; + } + + hercules_spi_assert_cs(s, csnr, 1); + } + + s->tgintflag |= INTFLGRDY(n); + } +} + +static void __hercules_spi_process_tg(HerculesMibSpiState *s, unsigned int n, + unsigned int end) +{ + if (s->spiena != SPIENA_IDLE) { + s->pending_tg.n = n; + s->pending_tg.end = end; + s->spiena = SPIENA_PENDING_TG; + return; + } + + ___hercules_spi_process_tg(s, n, end); +} + +static void hercules_spi_process_tg(HerculesMibSpiState *s, + unsigned int n) +{ + const unsigned int end = TGxCTRL_PSTART(s->tgxctrl[n + 1]) - 1; + + __hercules_spi_process_tg(s, n, end); +} + +static void hercules_spi_process_tg_last(HerculesMibSpiState *s) +{ + + const unsigned int end = TGxCTRL_PSTART(s->ltgpend); + + __hercules_spi_process_tg(s, 15, end); +} + +static uint64_t hercules_spi_read32(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesMibSpiState *s = opaque; + unsigned int idx; + uint32_t erraddr; + + switch (offset) { + case SPIGCR0: + return s->spigcr0; + case SPIGCR1: + return s->spigcr1; + case SPIINT0: + return s->spiint0; + case SPIFLG: + return s->spiflg; + case SPIPC0: + return s->spipc[0]; + case SPIPC1 ... SPIPC8: + return 0; + case SPIFMT0 ... SPIFMT3: + idx = (offset - SPIFMT0) / sizeof(uint32_t); + return s->spifmt[idx]; + case MIBSPIE: + return s->mibspie; + case TGINTFLAG: + return s->tgintflag; + case LTGPEND: + return s->ltgpend; + case TG0CTRL ... TG15CTRL: + idx = (offset - TG0CTRL) / sizeof(uint32_t); + return s->tgxctrl[idx]; + case PAR_ECC_CTRL: + return s->par_ecc_ctrl; + case PAR_ECC_STAT: + return s->par_ecc_stat; + case IOLPBKTSTCR: + return s->iolpbktstcr; + case ECCDIAG_CTRL: + return s->eccdiag_ctrl; + case ECCDIAG_STAT: + return s->eccdiag_stat; + case SBERRADDR1: + erraddr = s->sberraddr[1] + sizeof(s->txram); + s->sberraddr[1] = 0; + return erraddr; + case SBERRADDR0: + erraddr = s->sberraddr[0]; + s->sberraddr[0] = 0; + return erraddr; + case UERRADDR1: + erraddr = s->uerraddr[1] + sizeof(s->txram); + s->uerraddr[1] = 0; + return erraddr; + case UERRADDR0: + erraddr = s->uerraddr[0]; + s->uerraddr[0] = 0; + return erraddr; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static uint64_t hercules_spi_read16(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesMibSpiState *s = opaque; + + /* FIXME: This assumes BE */ + switch (offset) { + case SPIBUF: + return extract32(s->spibuf, 16, 16); + case SPIBUF + sizeof(uint16_t): + return extract32(s->spibuf, 0, 16); + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static uint64_t hercules_spi_read(void *opaque, hwaddr offset, + unsigned size) +{ + switch (size) { + case sizeof(uint16_t): + return hercules_spi_read16(opaque, offset, size); + case sizeof(uint32_t): + return hercules_spi_read32(opaque, offset, size); + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad size %u\n", + __func__, size); + break; + } + + return 0; +} + +static bool hercules_spi_compatibility_dma_enabled(HerculesMibSpiState *s) +{ + return s->spiint0 & DMAREQEN && s->spigcr1 & SPIEN; +} + +static void hercules_spi_assert_dmareq(void *opaque) +{ + HerculesMibSpiState *s = opaque; + + if (s->spiint0 & DMAREQEN) { + qemu_irq_raise(s->dmareq[0]); + } +} + +static void hercules_spi_process_compatibility_dma(HerculesMibSpiState *s) +{ + if (hercules_spi_compatibility_dma_enabled(s)) { + qemu_irq_raise(s->dmareq[0]); + } else { + qemu_bh_cancel(s->compatibility_dma_req); + } +} + +static void __hercules_spi_process_spidata(HerculesMibSpiState *s) +{ + s->spibuf = hercules_spi_tx_single(s, s->spidat1); + + if (hercules_spi_compatibility_dma_enabled(s)) { + qemu_irq_raise(s->dmareq[1]); + qemu_bh_schedule(s->compatibility_dma_req); + } +} + +static void hercules_spi_process_spidata(HerculesMibSpiState *s) +{ + if (s->spiena != SPIENA_IDLE) { + s->spiena = SPIENA_PENDING_SPIDATA; + return; + } + + __hercules_spi_process_spidata(s); +} + +static void hercules_spi_write16(void *opaque, hwaddr offset, uint64_t val64, + unsigned size) +{ + HerculesMibSpiState *s = opaque; + const uint16_t val = val64; + + /* FIXME: This assumes BE */ + switch (offset) { + case SPIDAT1: + s->spidat1 = deposit32(s->spidat1, 16, 16, val); + break; + case SPIDAT1 + sizeof(uint16_t): + s->spidat1 = deposit32(s->spidat1, 0, 16, val); + hercules_spi_process_spidata(s); + break; + default: + qemu_log_bad_offset(offset); + } +} + +static void hercules_spi_write32(void *opaque, hwaddr offset, uint64_t val64, + unsigned size) +{ + HerculesMibSpiState *s = opaque; + const uint32_t val = val64; + unsigned int idx; + + switch (offset) { + case SPIGCR0: + s->spigcr0 = val; + break; + case SPIGCR1: + s->spigcr1 = val; + hercules_spi_process_compatibility_dma(s); + break; + case SPIINT0: + s->spiint0 = val; + hercules_spi_process_compatibility_dma(s); + break; + case SPIFLG: + s->spiflg &= ~(val & SPIFLG_W1C_MASK); + break; + case SPIPC0: + s->spipc[0] = val; + break; + case SPIPC1 ... SPIPC8: + break; + case SPIDAT1: + s->spidat1 = val; + break; + case SPIFMT0 ... SPIFMT3: + idx = (offset - SPIFMT0) / sizeof(uint32_t); + s->spifmt[idx] = val; + break; + case MIBSPIE: + s->mibspie = val; + break; + case TGINTFLAG: + s->tgintflag &= ~val; + break; + case LTGPEND: + s->ltgpend = val; + break; + case TG0CTRL ... TG14CTRL: + idx = (offset - TG0CTRL) / sizeof(uint32_t); + s->tgxctrl[idx] = val; + hercules_spi_process_tg(s, idx); + break; + case TG15CTRL: + s->tgxctrl[15] = val; + hercules_spi_process_tg_last(s); + break; + case PAR_ECC_CTRL: + s->par_ecc_ctrl = val; + break; + case PAR_ECC_STAT: + s->par_ecc_stat &= ~val; + break; + case IOLPBKTSTCR: + s->iolpbktstcr = val; + break; + case ECCDIAG_CTRL: + s->eccdiag_ctrl = val; + break; + case ECCDIAG_STAT: + s->eccdiag_stat &= ~val; + break; + case SBERRADDR1: + case SBERRADDR0: + case UERRADDR1: + case UERRADDR0: + break; + default: + qemu_log_bad_offset(offset); + } +} + +static void hercules_spi_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + switch (size) { + case sizeof(uint16_t): + hercules_spi_write16(opaque, offset, val, size); + break; + case sizeof(uint32_t): + hercules_spi_write32(opaque, offset, val, size); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad size %u\n", + __func__, size); + break; + } +} + +static void hercules_spi_rxram_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + HerculesMibSpiState *s = opaque; + + if (s->mibspie & RXRAM_ACCESS) { + if (s->eccdiag_ctrl == 0x5) { + switch (ctpop32(val)) { + default: + s->par_ecc_stat |= UERR_FLG1; + s->eccdiag_stat |= DEFLG1; + s->uerraddr[1] = offset; + break; + case 1: + s->par_ecc_stat |= SBE_FLG1; + s->eccdiag_stat |= SEFLG1; + s->sberraddr[1] = offset; + case 0: + break; + } + } + + switch (size) { + case 1: + *(uint8_t *)((void *)s->rxram + offset) = (uint8_t)val; + break; + case 2: + *(uint16_t *)((void *)s->rxram + offset) = (uint16_t)val; + break; + case 4: + *(uint32_t *)((void *)s->rxram + offset) = (uint32_t)val; + break; + } + } +} + +static uint64_t hercules_spi_rxram_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesMibSpiState *s = opaque; + const unsigned int idx = offset / sizeof(uint32_t); + uint64_t data = (uint64_t)~0; + + hercules_ecc_error_raise(s, idx, 1, UERR_FLG1, SBE_FLG1); + + switch (size) { + case 1: + data = *(uint8_t *)((void *)s->rxram + offset); + break; + case 2: + data = *(uint16_t *)((void *)s->rxram + offset); + break; + case 4: + data = *(uint32_t *)((void *)s->rxram + offset); + break; + } + + return data; +} + +static void hercules_spi_set_spiena(void *opaque, int req, int level) +{ + HerculesMibSpiState *s = opaque; + const bool asserted = !level; + + if (likely(s->spipc[0] & ENAFUN)) { + switch (s->spiena) { + case SPIENA_PENDING_TG: + if (asserted) { + ___hercules_spi_process_tg(s, s->pending_tg.n, + s->pending_tg.end); + s->spiena = SPIENA_IDLE; + } + break; + case SPIENA_PENDING_SPIDATA: + if (asserted) { + __hercules_spi_process_spidata(s); + s->spiena = SPIENA_IDLE; + } + break; + case SPIENA_IDLE: + if (level) { + s->spiena = SPIENA_BUSY; + } + break; + case SPIENA_BUSY: + if (asserted) { + s->spiena = SPIENA_IDLE; + } + } + } +} + +static const MemoryRegionOps hercules_spi_rxram_ops = { + .read = hercules_spi_rxram_read, + .write = hercules_spi_rxram_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 1, + .max_access_size = 4, + .unaligned = true, + }, +}; + +static const MemoryRegionOps hercules_spi_ops = { + .read = hercules_spi_read, + .write = hercules_spi_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_spi_realize(DeviceState *dev, Error **errp) +{ + HerculesMibSpiState *s = HERCULES_SPI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + memory_region_init_io(&s->io.regs, OBJECT(dev), &hercules_spi_ops, s, + TYPE_HERCULES_SPI ".io", HERCULES_SPI_SIZE); + sysbus_init_mmio(sbd, &s->io.regs); + + memory_region_init_ram_ptr(&s->io.txram, OBJECT(dev), + TYPE_HERCULES_SPI ".ram.tx", + sizeof(s->txram), s->txram); + + memory_region_init_io(&s->io.rxram, OBJECT(dev), + &hercules_spi_rxram_ops, + s, TYPE_HERCULES_SPI ".ram.rx", + sizeof(s->rxram)); + + memory_region_init(&s->io.ram, OBJECT(dev), + TYPE_HERCULES_SPI ".ram", + sizeof(s->txram) + + sizeof(s->rxram)); + + memory_region_add_subregion(&s->io.ram, + 0x0, + &s->io.txram); + memory_region_add_subregion(&s->io.ram, + sizeof(s->txram), + &s->io.rxram); + + sysbus_init_mmio(sbd, &s->io.ram); + + sysbus_init_irq(sbd, &s->irq[0]); + sysbus_init_irq(sbd, &s->irq[1]); + + s->ssi = ssi_create_bus(dev, "ssi"); + ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->ssi); + + for (i = 0; i < ARRAY_SIZE(s->cs_lines); ++i) { + sysbus_init_irq(sbd, &s->cs_lines[i]); + } + + for (i = 0; i < ARRAY_SIZE(s->dmareq); i++) { + sysbus_init_irq(sbd, &s->dmareq[i]); + } + + s->compatibility_dma_req = qemu_bh_new(hercules_spi_assert_dmareq, s); + + qdev_init_gpio_in_named(dev, hercules_spi_set_spiena, + HERCULES_SPI_SPIENA, 1); + + sysbus_init_irq(sbd, &s->single_bit_error); + sysbus_init_irq(sbd, &s->uncorrectable_error); +} + +static void hercules_spi_reset(DeviceState *d) +{ + HerculesMibSpiState *s = HERCULES_SPI(d); + + s->mibspie = 5 << 8; + s->spigcr0 = 0; + s->spigcr1 = 0; + s->spiint0 = 0; + s->spiflg = 0; + s->spipc[0] = 0; + s->tgintflag = 0; + s->iolpbktstcr = 0; + s->ltgpend = 0; + s->spiena = SPIENA_IDLE; + + memset(s->spifmt, 0, sizeof(s->spifmt)); + memset(s->tgxctrl, 0, sizeof(s->tgxctrl)); + memset(s->txram, 0, sizeof(s->txram)); + memset(s->rxram, 0, sizeof(s->rxram)); +} + +static void hercules_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_spi_reset; + dc->realize = hercules_spi_realize; + + dc->desc = "Hercules MiBSPI Controller"; +} + +static const TypeInfo hercules_spi_info = { + .name = TYPE_HERCULES_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesMibSpiState), + .class_init = hercules_spi_class_init, +}; + +static void hercules_spi_register_types(void) +{ + type_register_static(&hercules_spi_info); +} + +type_init(hercules_spi_register_types) diff --git a/include/hw/ssi/hercules_spi.h b/include/hw/ssi/hercules_spi.h new file mode 100644 index 0000000000000..f82a09a094530 --- /dev/null +++ b/include/hw/ssi/hercules_spi.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_SPI_H +#define HERCULES_SPI_H + +#include "hw/ssi/ssi.h" + +enum { + HERCULES_SPI_RAM_SIZE = 128, + HERCULES_SPI_SIZE = 512, + HERCULES_SPI_NUM_IRQ_LINES = 2, + HERCULES_SPI_NUM_CS_LINES = 8, + HERCULES_SPI_NUM_DMAREQS = 16, +}; + +typedef struct HerculesMibSpiState { + SysBusDevice parent_obj; + + struct { + MemoryRegion regs; + MemoryRegion txram; + MemoryRegion rxram; + MemoryRegion ram; + } io; + + uint32_t txram[HERCULES_SPI_RAM_SIZE]; + uint32_t rxram[HERCULES_SPI_RAM_SIZE]; + + uint32_t spigcr0; + uint32_t spigcr1; + uint32_t spiint0; + uint32_t spiflg; + uint32_t spipc[1]; + uint32_t spidat0; + uint32_t spidat1; + uint32_t spibuf; + uint32_t spifmt[4]; + uint32_t mibspie; + uint32_t tgintflag; + uint32_t tgxctrl[16]; + uint32_t iolpbktstcr; + uint32_t ltgpend; + uint32_t par_ecc_ctrl; + uint32_t par_ecc_stat; + uint32_t uerraddr[2]; + uint32_t eccdiag_ctrl; + uint32_t eccdiag_stat; + uint32_t sberraddr[2]; + qemu_irq irq[HERCULES_SPI_NUM_IRQ_LINES]; + + SSIBus *ssi; + qemu_irq cs_lines[HERCULES_SPI_NUM_CS_LINES]; + + qemu_irq dmareq[HERCULES_SPI_NUM_DMAREQS]; + + qemu_irq single_bit_error; + qemu_irq uncorrectable_error; + + enum { + SPIENA_IDLE, + SPIENA_BUSY, + SPIENA_PENDING_TG, + SPIENA_PENDING_SPIDATA, + } spiena; + + struct { + unsigned int n; + unsigned int end; + } pending_tg; + + QEMUBH *compatibility_dma_req; +} HerculesMibSpiState; + +#define TYPE_HERCULES_SPI "ti-hercules-spi" +#define HERCULES_SPI(obj) OBJECT_CHECK(HerculesMibSpiState, (obj), \ + TYPE_HERCULES_SPI) + +#define HERCULES_SPI_SPIENA TYPE_HERCULES_SPI "-spiena" + +#endif From 8be9c9afebd647117a5c6535e4b030906890c1f7 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 24 Apr 2019 07:41:40 -0700 Subject: [PATCH 12/27] hw/adc: Add support for Hercules MiBADC Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/adc/Makefile.objs | 1 + hw/adc/hercules_mibadc.c | 386 +++++++++++++++++++++++++++++++ include/hw/adc/hercules_mibadc.h | 50 ++++ 3 files changed, 437 insertions(+) create mode 100644 hw/adc/hercules_mibadc.c create mode 100644 include/hw/adc/hercules_mibadc.h diff --git a/hw/adc/Makefile.objs b/hw/adc/Makefile.objs index 2b9dc36c7f942..7756439bdd4d5 100644 --- a/hw/adc/Makefile.objs +++ b/hw/adc/Makefile.objs @@ -1 +1,2 @@ common-obj-$(CONFIG_STM32F2XX_ADC) += stm32f2xx_adc.o +common-obj-$(CONFIG_HERCULES) += hercules_mibadc.o diff --git a/hw/adc/hercules_mibadc.c b/hw/adc/hercules_mibadc.c new file mode 100644 index 0000000000000..0b4f91e7b682b --- /dev/null +++ b/hw/adc/hercules_mibadc.c @@ -0,0 +1,386 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/ptimer.h" +#include "qemu/main-loop.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" + +#include "hw/adc/hercules_mibadc.h" + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesMibAdcRegisters { + ADOPMODECR = 0x004, + _10_12_BIT = BIT(31), + COS = BIT(24), + ADEVINTFLG = 0x34, + ADG1INTFLG = 0x38, + ADG2INTFLG = 0x3C, + ADGxINTFLG_END = BIT(3), + ADG1THRINTCR = 0x44, + ADG2THRINTCR = 0x48, + ADBNDCR = 0x58, + ADBNDEND = 0x5C, + ADEVSR = 0x6C, + ADG1SR = 0x70, + ADG2SR = 0x74, + ADGxSR_END = BIT(0), + ADEVSEL = 0x78, + ADG1SEL = 0x7C, + ADG2SEL = 0x80, + ADG1BUFFER0 = 0xB0, + ADG1BUFFER7 = 0xCC, + ADG2BUFFER0 = 0xD0, + ADG2BUFFER7 = 0xEC, + + ADPARCR = 0x180, + TEST = BIT(8), + ADPARADDR = 0x184, +}; + +enum { + HERCULES_MIBADC_CONTAINER_SIZE = 8 * 1024, + HERCULES_MIBADC_RAM_OFFSET = 0, + HERCULES_MIBADC_ECC_OFFSET = HERCULES_MIBADC_CONTAINER_SIZE / 2, + + HERCULES_MIBADC_REGS_SIZE = 512, +}; + +#define Gx_EMPTY(adopmodecr) ((adopmodecr & _10_12_BIT) ? BIT(31) : BIT(15)) +static bool hercules_mibadc_group_invalid(HerculesMibAdcGroup *group) +{ + if (group->start >= group->end) { + return true; + } + + return false; +} + +static void hercules_mibadc_group_reset(HerculesMibAdcGroup *group) +{ + group->rdidx = group->wridx = group->start; +} + +static void hercules_mibadc_push_result(HerculesMibAdcState *s, + HerculesMibAdcGroup *group, + uint32_t chid, + uint32_t result) +{ + unsigned int start; + + if (hercules_mibadc_group_invalid(group)) { + return; + } + + if (group->wridx == group->end) { + /* + * Results RAM is full. Ingore new result + */ + return; + } + + start = (s->adopmodecr & _10_12_BIT) ? 16 : 10; + s->results[group->wridx++] = deposit32(result, start, 5, chid); +} + +static uint32_t hercules_mibadc_pop_result(HerculesMibAdcState *s, + HerculesMibAdcGroup *group) +{ + const uint32_t empty = Gx_EMPTY(s->adopmodecr); + uint32_t result; + + if (hercules_mibadc_group_invalid(group)) { + return empty; + } + + if (group->wridx == group->start) { + /* + * Results RAM is empty + */ + return empty; + } + + result = s->results[group->rdidx]; + s->results[group->rdidx++] = empty; + + if (group->rdidx == group->wridx) { + /* + * All results were read out we can rewind results RAM indices + */ + hercules_mibadc_group_reset(group); + } + + return result; +} + +static void hercules_mibadc_do_conversion(HerculesMibAdcState *s, + HerculesMibAdcGroup *group) +{ + unsigned long sel = group->sel; + uint32_t chid; + + for_each_set_bit(chid, &sel, ARRAY_SIZE(s->channel)) { + hercules_mibadc_push_result(s, group, chid, s->channel[chid]); + } + + group->sr |= ADGxSR_END; + group->intflg |= ADGxINTFLG_END; +} + +#define IDX(o, s) (((o) - (s)) / sizeof(uint32_t)) + +static void hercules_mibadc_ram_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + HerculesMibAdcState *s = opaque; + + s->results[IDX(offset, 0)] = val; +} + +static uint64_t hercules_mibadc_ram_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesMibAdcState *s = opaque; + unsigned int idx = IDX(offset, 0); + + if (s->ecc[idx]) { + /* + * TODO this isn't how real HW would do it, but its enough to + * pass our ADC error signalling functionality test + */ + s->adparaddr = offset; + qemu_irq_raise(s->parity_error); + } + + return s->results[idx]; +} + +static void hercules_mibadc_ecc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + HerculesMibAdcState *s = opaque; + + if (s->adparcr & TEST) { + s->ecc[IDX(offset, 0)] = val; + } +} + +static uint64_t hercules_mibadc_ecc_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesMibAdcState *s = opaque; + + if (s->adparcr & TEST) { + return s->ecc[IDX(offset, 0)]; + } + + return 0; +} + +static uint64_t hercules_mibadc_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesMibAdcState *s = opaque; + + switch (offset) { + case ADOPMODECR: + return s->adopmodecr | COS; + case ADEVINTFLG ... ADG2INTFLG: + return s->adg[IDX(offset, ADEVINTFLG)].intflg; + case ADEVSR ... ADG2SR: + return s->adg[IDX(offset, ADEVSR)].sr; + case ADG1BUFFER0 ... ADG1BUFFER7: + return hercules_mibadc_pop_result(s, &s->adg[1]); + case ADG2BUFFER0 ... ADG2BUFFER7: + return hercules_mibadc_pop_result(s, &s->adg[2]); + case ADPARCR: + return s->adparcr; + case ADBNDCR: + case ADBNDEND: + case ADPARADDR: + break; + case ADG1THRINTCR ... ADG2THRINTCR: + return 0; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_mibadc_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesMibAdcState *s = opaque; + const uint32_t val = val64; + HerculesMibAdcGroup *group; + + switch (offset) { + case ADG1SEL: + case ADG2SEL: + group = &s->adg[IDX(offset, ADEVSEL)]; + group->sel = val; + hercules_mibadc_do_conversion(s, group); + break; + case ADOPMODECR: + s->adopmodecr = val; + break; + case ADBNDCR: + /* + * We don't support more than 64 word buffer, so we only + * extract 6 bits + */ + s->adg[0].end = s->adg[1].start = 2 * extract32(val, 16, 6); + s->adg[1].end = s->adg[2].start = 2 * extract32(val, 0, 6); + + hercules_mibadc_group_reset(&s->adg[1]); + hercules_mibadc_group_reset(&s->adg[2]); + break; + case ADBNDEND: + s->adg[2].end = 16 << MIN(extract32(val, 0, 2), 2); + break; + case ADEVSR ... ADG2SR: + s->adg[IDX(offset, ADEVSR)].sr &= ~(val & ADGxSR_END); + break; + case ADEVINTFLG ... ADG2INTFLG: + s->adg[IDX(offset, ADEVINTFLG)].intflg &= ~(val & ADGxINTFLG_END); + break; + case ADPARCR: + s->adparcr = val; + break; + case ADPARADDR: + case ADG1BUFFER0 ... ADG2BUFFER7: + return; + default: + qemu_log_bad_offset(offset); + } +} + +#undef IDX + +static const MemoryRegionOps hercules_mibadc_ecc_ops = { + .read = hercules_mibadc_ecc_read, + .write = hercules_mibadc_ecc_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps hercules_mibadc_ram_ops = { + .read = hercules_mibadc_ram_read, + .write = hercules_mibadc_ram_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps hercules_mibadc_ops = { + .read = hercules_mibadc_read, + .write = hercules_mibadc_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_mibadc_realize(DeviceState *dev, Error **errp) +{ + HerculesMibAdcState *s = HERCULES_MIBADC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->regs, OBJECT(dev), &hercules_mibadc_ops, s, + TYPE_HERCULES_MIBADC ".regs", + HERCULES_MIBADC_REGS_SIZE); + sysbus_init_mmio(sbd, &s->regs); + + memory_region_init_io(&s->io.ram, OBJECT(dev), &hercules_mibadc_ram_ops, + s, TYPE_HERCULES_MIBADC ".io.ram", + sizeof(s->results)); + + memory_region_init_io(&s->io.ecc, OBJECT(dev), &hercules_mibadc_ecc_ops, + s, TYPE_HERCULES_MIBADC ".io.ecc", + sizeof(s->ecc)); + + memory_region_init(&s->io.container, OBJECT(dev), + TYPE_HERCULES_MIBADC ".io.container", + HERCULES_MIBADC_CONTAINER_SIZE); + + memory_region_add_subregion(&s->io.container, HERCULES_MIBADC_RAM_OFFSET, + &s->io.ram); + memory_region_add_subregion(&s->io.container, HERCULES_MIBADC_ECC_OFFSET, + &s->io.ecc); + sysbus_init_mmio(sbd, &s->io.container); + + sysbus_init_irq(sbd, &s->parity_error); +} + +static void hercules_mibadc_reset(DeviceState *dev) +{ + HerculesMibAdcState *s = HERCULES_MIBADC(dev); + + s->adopmodecr = 0; + + memset(s->adg, 0, sizeof(s->adg)); + memset(s->results, 0, sizeof(s->results)); + + qemu_irq_lower(s->parity_error); +} + +static void hercules_mibadc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_mibadc_reset; + dc->realize = hercules_mibadc_realize; +} + +static const TypeInfo hercules_mibadc_info = { + .name = TYPE_HERCULES_MIBADC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesMibAdcState), + .class_init = hercules_mibadc_class_init, +}; + +static void hercules_mibadc_register_types(void) +{ + type_register_static(&hercules_mibadc_info); +} + +type_init(hercules_mibadc_register_types) diff --git a/include/hw/adc/hercules_mibadc.h b/include/hw/adc/hercules_mibadc.h new file mode 100644 index 0000000000000..2efe62ce587c5 --- /dev/null +++ b/include/hw/adc/hercules_mibadc.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_MIBADC_H +#define HERCULES_MIBADC_H + +#include "hw/sysbus.h" + +typedef struct HerculesMibAdcGroup { + uint32_t sr; + uint32_t sel; + uint32_t intflg; + + uint8_t start; + uint8_t end; + uint8_t wridx; + uint8_t rdidx; +} HerculesMibAdcGroup; + +typedef struct HerculesMibAdcState { + SysBusDevice parent_obj; + + MemoryRegion regs; + struct { + MemoryRegion container; + MemoryRegion ram; + MemoryRegion ecc; + } io; + + uint32_t adopmodecr; + uint32_t adparcr; + uint32_t adparaddr; + HerculesMibAdcGroup adg[3]; + + uint32_t results[64]; + uint32_t ecc[64]; + uint16_t channel[32]; + + qemu_irq parity_error; +} HerculesMibAdcState; + +#define TYPE_HERCULES_MIBADC "ti-hercules-mibadc" +#define HERCULES_MIBADC(obj) OBJECT_CHECK(HerculesMibAdcState, (obj), \ + TYPE_HERCULES_MIBADC) + +#endif From 93fdb83a3cec95b7e61ebaf9edbe018e8ad46362 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 22 May 2019 11:40:36 -0700 Subject: [PATCH 13/27] hw/dma: Add support for Hercules DMA Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/dma/Makefile.objs | 1 + hw/dma/hercules_dma.c | 437 ++++++++++++++++++++++++++++++++++ hw/dma/trace-events | 3 + include/hw/dma/hercules_dma.h | 59 +++++ 4 files changed, 500 insertions(+) create mode 100644 hw/dma/hercules_dma.c create mode 100644 include/hw/dma/hercules_dma.h diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index f4b1cfe26da0f..bd43608900d0b 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -14,3 +14,4 @@ common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o common-obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o common-obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o common-obj-$(CONFIG_RASPI) += bcm2835_dma.o +common-obj-$(CONFIG_HERCULES) += hercules_dma.o diff --git a/hw/dma/hercules_dma.c b/hw/dma/hercules_dma.c new file mode 100644 index 0000000000000..0568fb47fd8d8 --- /dev/null +++ b/hw/dma/hercules_dma.c @@ -0,0 +1,437 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/core/cpu.h" +#include "sysemu/dma.h" +#include "trace.h" + +#include "hw/dma/hercules_dma.h" + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesDMARegisters { + GCTRL = 0x000, + HWCHENAS = 0x014, + HWCHENAR = 0x01C, + DREQASI0 = 0x054, + DREQASI7 = 0x070, + PAR0 = 0x094, + PAR3 = 0x0A0, + BTCFLAG = 0x13C, + PTCRL = 0x178, + TTYPE = BIT(8), + + ISADDR = 0x00, + IDADDR = 0x04, + ITCOUNT = 0x08, + CHCTRL = 0x10, + ADDM_POST_INCREMENT = 1, + ADDM_INDEXED = 2, + + EIOFF = 0x14, + FIOFF = 0x18, + + CSADDR = 0x00, + CDADDR = 0x04, + CTCOUNT = 0x08, +}; + +enum { + HERCULES_DMA_SIZE = 1024, + HERCULES_DMA_RAM_SIZE = 4 * 1024, + + HERCULES_DMA_PCP_OFFSET = 0x000, + HERCULES_DMA_PCP_SIZE = 32, + + HERCULES_DMA_WCP_OFFSET = 0x800, + HERCULES_DMA_WCP_SIZE = 32, +}; + +#define CHCTRL_ADDMR(w) extract32(w, 3, 2) +#define CHCTRL_ADDMW(w) extract32(w, 1, 2) +#define CHCTRL_RES(w) extract32(w, 14, 2) +#define CHCTRL_WES(w) extract32(w, 12, 2) + +static void hercules_dma_adjust_addr(uint32_t *caddr, unsigned int addm, + unsigned int es, unsigned int eidx, + unsigned int fidx, int cetcount) +{ + switch (addm) { + case ADDM_INDEXED: + *caddr += es * ((cetcount == 1) ? fidx : eidx); + break; + case ADDM_POST_INCREMENT: + *caddr += es; + break; + default: + break; + } +} + +static void hercules_dma_set_request(void *opaque, int req, int level) +{ + HerculesDMAState *s = opaque; + const uint32_t enabled = s->reqmap[req] & s->hwchena; + + if (level && enabled) { + const unsigned int channel = ctzl(enabled); + HerculesDMAChannel *ch = &s->channel[channel]; + const uint8_t addmr = CHCTRL_ADDMR(ch->pcp.chctrl); + const uint8_t addmw = CHCTRL_ADDMW(ch->pcp.chctrl); + const unsigned int res = CHCTRL_RES(ch->pcp.chctrl) + 1; + const unsigned int wes = CHCTRL_WES(ch->pcp.chctrl) + 1; + + while (ch->wcp.cftcount) { + uint64_t buffer = 0; + int i; + + for (i = ch->wcp.cetcount; i > 0; i--) { + trace_hercules_dma_transfer(channel, + (int)ch->wcp.cftcount, + (int)ch->wcp.cetcount, + ch->wcp.csaddr, res, + ch->wcp.cdaddr, wes); + + dma_memory_read(&address_space_memory, ch->wcp.csaddr, + &buffer, res); + hercules_dma_adjust_addr(&ch->wcp.csaddr, addmr, res, + ch->pcp.eidxs, ch->pcp.fidxs, i); + + dma_memory_write(&address_space_memory, ch->wcp.cdaddr, + &buffer, wes); + hercules_dma_adjust_addr(&ch->wcp.cdaddr, addmw, wes, + ch->pcp.eidxd, ch->pcp.fidxd, i); + } + + if (!--ch->wcp.cftcount) { + s->btcflag |= BIT(channel); + } + + if (!(ch->pcp.chctrl & TTYPE)) { + /* + * Exit this loop early if we are configured for + * frame transfer + */ + break; + } + } + } +} + +#define IDX(o, s) (((o) - (s)) / sizeof(uint32_t)) + +static uint64_t hercules_dma_read(void *opaque, hwaddr offset, unsigned size) +{ + HerculesDMAState *s = opaque; + + switch (offset) { + case GCTRL: + return s->gctrl; + case HWCHENAS: + case HWCHENAR: + return s->hwchena; + case BTCFLAG: + return s->btcflag; + case DREQASI0 ... DREQASI7: + return s->dreqasi[IDX(offset, DREQASI0)]; + case PTCRL: + case PAR0 ... PAR3: + break; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_dma_update_reqmap(HerculesDMAState *s) +{ + int i, channel; + uint8_t req; + + memset(s->reqmap, 0, sizeof(s->reqmap)); + + for (i = 0, channel = 0; i < ARRAY_SIZE(s->dreqasi); i++) { + req = extract32(s->dreqasi[i], 24, 6); + s->reqmap[req] |= BIT(channel++); + req = extract32(s->dreqasi[i], 16, 6); + s->reqmap[req] |= BIT(channel++); + req = extract32(s->dreqasi[i], 8, 6); + s->reqmap[req] |= BIT(channel++); + req = extract32(s->dreqasi[i], 0, 6); + s->reqmap[req] |= BIT(channel++); + } +} + +static void hercules_dma_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesDMAState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case GCTRL: + s->gctrl = val; + break; + case HWCHENAS: + s->hwchena |= val; + break; + case HWCHENAR: + s->hwchena &= ~val; + break; + case BTCFLAG: + s->btcflag &= ~val; + break; + case DREQASI0 ... DREQASI7: + s->dreqasi[IDX(offset, DREQASI0)] = val; + hercules_dma_update_reqmap(s); + break; + case PTCRL: + case PAR0 ... PAR3: + break; + default: + qemu_log_bad_offset(offset); + } +} + +#undef IDX + +static void hercules_dma_ram_pcp_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesDMAChannel *ch = opaque; + const uint32_t val = val64; + + switch (offset) { + case ISADDR: + ch->wcp.csaddr = ch->pcp.isaddr = val; + break; + case IDADDR: + ch->wcp.cdaddr = ch->pcp.idaddr = val; + break; + case ITCOUNT: + ch->wcp.cetcount = ch->pcp.ietcount = extract32(val, 0, 16); + ch->wcp.cftcount = ch->pcp.iftcount = extract32(val, 16, 16); + break; + case CHCTRL: + ch->pcp.chctrl = val; + break; + case EIOFF: + ch->pcp.eidxs = extract32(val, 0, 16); + ch->pcp.eidxd = extract32(val, 16, 16); + break; + case FIOFF: + ch->pcp.fidxs = extract32(val, 0, 16); + ch->pcp.fidxd = extract32(val, 16, 16); + break; + default: + qemu_log_bad_offset(offset); + break; + }; +} + +static uint64_t hercules_dma_ram_pcp_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesDMAChannel *ch = opaque; + + switch (offset) { + case ISADDR: + return ch->pcp.isaddr; + case IDADDR: + return ch->pcp.idaddr; + case ITCOUNT: + return deposit32(ch->pcp.ietcount, 16, 16, ch->pcp.iftcount); + case CHCTRL: + return ch->pcp.chctrl; + case EIOFF: + return deposit32(ch->pcp.eidxs, 16, 16, ch->pcp.eidxd); + case FIOFF: + return deposit32(ch->pcp.fidxs, 16, 16, ch->pcp.fidxd); + default: + qemu_log_bad_offset(offset); + }; + + return 0; +} + +static uint64_t hercules_dma_ram_wcp_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesDMAChannel *ch = opaque; + + switch (offset) { + case CSADDR: + return ch->wcp.csaddr; + case CDADDR: + return ch->wcp.cdaddr; + case CTCOUNT: + return deposit32(ch->wcp.cetcount, 16, 16, ch->wcp.cftcount); + default: + qemu_log_bad_offset(offset); + break; + }; + + return 0; +} + +static const MemoryRegionOps hercules_dma_ops = { + .read = hercules_dma_read, + .write = hercules_dma_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps hercules_dma_ram_pcp_ops = { + .read = hercules_dma_ram_pcp_read, + .write = hercules_dma_ram_pcp_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps hercules_dma_ram_wcp_ops = { + .read = hercules_dma_ram_wcp_read, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_dma_reset(DeviceState *d) +{ + HerculesDMAState *s = HERCULES_DMA(d); + int i; + + for (i = 0; i < HERCULES_DMA_CHANNEL_NUM; i++) { + HerculesDMAChannel *ch = &s->channel[i]; + + ch->pcp.isaddr = 0; + ch->pcp.idaddr = 0; + ch->pcp.iftcount = 0; + ch->pcp.ietcount = 0; + ch->pcp.chctrl = 0; + ch->pcp.eidxd = 0; + ch->pcp.eidxs = 0; + ch->pcp.fidxd = 0; + ch->pcp.fidxs = 0; + + ch->wcp.csaddr = 0; + ch->wcp.cdaddr = 0; + ch->wcp.cftcount = 0; + ch->wcp.cetcount = 0; + } + + s->hwchena = 0; + s->btcflag = 0; + s->gctrl = 0; + + for (i = 0; i < ARRAY_SIZE(s->dreqasi); i++) { + s->dreqasi[i] = 0x00010203 + i * 0x04040404; + } + hercules_dma_update_reqmap(s); +} + +static void hercules_dma_initfn(Object *obj) +{ +} + +static void hercules_dma_realize(DeviceState *dev, Error **errp) +{ + HerculesDMAState *s = HERCULES_DMA(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_dma_ops, + s, TYPE_HERCULES_DMA ".io", HERCULES_DMA_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + memory_region_init(&s->ram, OBJECT(dev), + TYPE_HERCULES_DMA ".ram", + HERCULES_DMA_RAM_SIZE); + + for (i = 0; i < HERCULES_DMA_CHANNEL_NUM; i++) { + MemoryRegion *io, *parent = &s->ram; + hwaddr offset; + + io = &s->channel[i].pcp.io; + memory_region_init_io(io, OBJECT(dev), &hercules_dma_ram_pcp_ops, + &s->channel[i], + TYPE_HERCULES_DMA ".pcp.io", + HERCULES_DMA_PCP_SIZE); + offset = HERCULES_DMA_PCP_OFFSET + i * HERCULES_DMA_PCP_SIZE; + memory_region_add_subregion(parent, offset, io); + + io = &s->channel[i].wcp.io; + memory_region_init_io(io, OBJECT(dev), &hercules_dma_ram_wcp_ops, + &s->channel[i], + TYPE_HERCULES_DMA ".wcp.io", + HERCULES_DMA_WCP_SIZE); + offset = HERCULES_DMA_WCP_OFFSET + i * HERCULES_DMA_WCP_SIZE; + memory_region_add_subregion(parent, offset, io); + } + + sysbus_init_mmio(sbd, &s->ram); + + qdev_init_gpio_in(dev, hercules_dma_set_request, HERCULES_DMA_REQUEST_NUM); +} + +static void hercules_dma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_dma_reset; + dc->realize = hercules_dma_realize; +} + +static const TypeInfo hercules_dma_info = { + .name = TYPE_HERCULES_DMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesDMAState), + .instance_init = hercules_dma_initfn, + .class_init = hercules_dma_class_init, +}; + +static void hercules_dma_register_types(void) +{ + type_register_static(&hercules_dma_info); +} + +type_init(hercules_dma_register_types) diff --git a/hw/dma/trace-events b/hw/dma/trace-events index 44893995f631a..b77003eaa8016 100644 --- a/hw/dma/trace-events +++ b/hw/dma/trace-events @@ -44,3 +44,6 @@ pl330_debug_exec_stall(void) "stall of debug instruction not implemented" pl330_iomem_write(uint32_t offset, uint32_t value) "addr: 0x%08"PRIx32" data: 0x%08"PRIx32 pl330_iomem_write_clr(int i) "event interrupt lowered %d" pl330_iomem_read(uint32_t addr, uint32_t data) "addr: 0x%08"PRIx32" data: 0x%08"PRIx32 + +# hercules_dma.c +hercules_dma_transfer(int channel, int cftcount, int cetcount, uint32_t saddr, unsigned int res, uint32_t daddr, unsigned int wes) "DMA (%d): %d,%d 0x%x(%u) -> 0x%x(%u)" diff --git a/include/hw/dma/hercules_dma.h b/include/hw/dma/hercules_dma.h new file mode 100644 index 0000000000000..8f330fd503af8 --- /dev/null +++ b/include/hw/dma/hercules_dma.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_DMA_H +#define HERCULES_DMA_H + +enum { + HERCULES_DMA_CHANNEL_NUM = 32, + HERCULES_DMA_REQUEST_NUM = 48, +}; + +typedef struct HerculesDMAChannel { + struct { + MemoryRegion io; + + uint32_t isaddr; + uint32_t idaddr; + uint16_t iftcount; + uint16_t ietcount; + uint32_t chctrl; + uint16_t eidxd; + uint16_t eidxs; + uint16_t fidxd; + uint16_t fidxs; + } pcp; + + struct { + MemoryRegion io; + + uint32_t csaddr; + uint32_t cdaddr; + uint16_t cftcount; + uint16_t cetcount; + } wcp; +} HerculesDMAChannel; + +typedef struct HerculesDMAState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion ram; + + uint32_t hwchena; + uint32_t dreqasi[8]; + uint32_t btcflag; + uint32_t gctrl; + + uint32_t reqmap[HERCULES_DMA_REQUEST_NUM]; + HerculesDMAChannel channel[HERCULES_DMA_CHANNEL_NUM]; +} HerculesDMAState; + +#define TYPE_HERCULES_DMA "ti-hercules-dma" +#define HERCULES_DMA(obj) OBJECT_CHECK(HerculesDMAState, (obj), \ + TYPE_HERCULES_DMA) +#endif From 49dcf5b1619735f18496f7c0f8daf5881dcb8f21 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sat, 1 Jun 2019 23:26:28 -0700 Subject: [PATCH 14/27] hw/misc: Add support for Hercules SCM module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 2 +- hw/misc/hercules_scm.c | 200 +++++++++++++++++++++++++++++++++ include/hw/misc/hercules_scm.h | 25 +++++ 3 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_scm.c create mode 100644 include/hw/misc/hercules_scm.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 35cc1464c301b..53455f6f9a705 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -86,7 +86,7 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_xdma.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o common-obj-$(CONFIG_MSF2) += msf2-sysreg.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o -common-obj-$(CONFIG_HERCULES) += hercules_system.o +common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_scm.c b/hw/misc/hercules_scm.c new file mode 100644 index 0000000000000..841ffdbfe8fc3 --- /dev/null +++ b/hw/misc/hercules_scm.c @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_scm.h" + +enum { + HERCULES_SCM_SIZE = 256, + HERCULES_SDR_MMR_SIZE = 16 * 1024 * 1024, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesSCMRegisters { + SCMCNTRL = 0x04, +}; + +#define DTC_SOFT_RESET(w) extract32(w, 8, 4) + +enum HerculesSDCRegisters { + SDC_STATUS = 0x00, + NT_OK = BIT(3), + PT_OK = BIT(1), +}; + +static void hercules_scm_self_test(void *opaque) +{ + HerculesSCMState *s = opaque; + CPUState *cpu = qemu_get_cpu(0); + + if (cpu->halted) { + s->sdc_status |= NT_OK | PT_OK; + qemu_irq_raise(s->icrst); + return; + } + + qemu_bh_schedule(s->self_test); +} + +static uint64_t hercules_scm_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesSCMState *s = opaque; + + switch (offset) { + case SCMCNTRL: + return s->scmcntrl; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_scm_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesSCMState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case SCMCNTRL: + if (DTC_SOFT_RESET(val) == 0xA) { + qemu_bh_schedule(s->self_test); + } + break; + default: + qemu_log_bad_offset(offset); + } +} + +static uint64_t hercules_sdr_mmr_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesSCMState *s = opaque; + + switch (offset) { + case SDC_STATUS: + return s->sdc_status; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_sdr_mmr_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + switch (offset) { + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_scm_ops = { + .read = hercules_scm_read, + .write = hercules_scm_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps hercules_sdr_mmr_ops = { + .read = hercules_sdr_mmr_read, + .write = hercules_sdr_mmr_write, + /* + * This is not BE on TMS570 as per Device#51 errata + */ + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_scm_realize(DeviceState *dev, Error **errp) +{ + HerculesSCMState *s = HERCULES_SCM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->io.scm, OBJECT(dev), &hercules_scm_ops, + s, TYPE_HERCULES_SCM ".io.scm", HERCULES_SCM_SIZE); + sysbus_init_mmio(sbd, &s->io.scm); + + memory_region_init_io(&s->io.sdr_mmr, OBJECT(dev), &hercules_sdr_mmr_ops, + s, TYPE_HERCULES_SCM ".io.sdr-mmr", + HERCULES_SDR_MMR_SIZE); + sysbus_init_mmio(sbd, &s->io.sdr_mmr); + + s->self_test = qemu_bh_new(hercules_scm_self_test, s); + + sysbus_init_irq(sbd, &s->icrst); +} + +static void hercules_scm_reset(DeviceState *d) +{ + HerculesSCMState *s = HERCULES_SCM(d); + + s->scmcntrl = 0x05050505; + + /* + * s->sdc_status is left alone on purpose + */ + + qemu_bh_cancel(s->self_test); +} + +static void hercules_scm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_scm_reset; + dc->realize = hercules_scm_realize; +} + +static const TypeInfo hercules_scm_info = { + .name = TYPE_HERCULES_SCM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesSCMState), + .class_init = hercules_scm_class_init, +}; + +static void hercules_scm_register_types(void) +{ + type_register_static(&hercules_scm_info); +} + +type_init(hercules_scm_register_types) diff --git a/include/hw/misc/hercules_scm.h b/include/hw/misc/hercules_scm.h new file mode 100644 index 0000000000000..d42b982a5b4c6 --- /dev/null +++ b/include/hw/misc/hercules_scm.h @@ -0,0 +1,25 @@ +#ifndef HERCULES_SCM_H +#define HERCULES_SCM_H + +#include "hercules_system.h" + +typedef struct HerculesSCMState { + SysBusDevice parent_obj; + + struct { + MemoryRegion scm; + MemoryRegion sdr_mmr; + } io; + + uint32_t scmcntrl; + + uint32_t sdc_status; + + QEMUBH *self_test; + qemu_irq icrst; +} HerculesSCMState; + +#define TYPE_HERCULES_SCM "ti-hercules-scm" +#define HERCULES_SCM(obj) OBJECT_CHECK(HerculesSCMState, (obj), \ + TYPE_HERCULES_SCM) +#endif From 9b187b4e587a89fb40b95fc3f9d8594d4b5b9cf1 Mon Sep 17 00:00:00 2001 From: Benjamin Kamath Date: Fri, 5 Jun 2020 00:38:56 -0700 Subject: [PATCH 15/27] hw/misc: Add support for Hercules ESM module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 2 +- hw/misc/hercules_esm.c | 414 +++++++++++++++++++++++++++++++++ include/hw/misc/hercules_esm.h | 79 +++++++ 3 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_esm.c create mode 100644 include/hw/misc/hercules_esm.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 53455f6f9a705..706f383860528 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -86,7 +86,7 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_xdma.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o common-obj-$(CONFIG_MSF2) += msf2-sysreg.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o -common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o +common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o hercules_esm.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_esm.c b/hw/misc/hercules_esm.c new file mode 100644 index 0000000000000..b2dc0ae45e1c4 --- /dev/null +++ b/hw/misc/hercules_esm.c @@ -0,0 +1,414 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_esm.h" + +enum { + HERCULES_ESM_SIZE = 256, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesESMRegisters { + ESMDEPAPR1 = 0x00, + ESMEEPAPR1 = 0x04, + ESMIESR1 = 0x08, + ESMIECR1 = 0x0C, + ESMILSR1 = 0x10, + ESMILCR1 = 0x14, + ESMSR1 = 0x18, + ESMSR2 = 0x1C, + ESMSR3 = 0x20, + ESMEPSR = 0x24, + ESMIOFFHR = 0x28, + ESMIOFFLR = 0x2C, + + ESMLTCPR = 0x34, + ESMEKR = 0x38, + ESMSSR2 = 0x3C, + + ESMIEPSR4 = 0x40, + ESMIEPCR4 = 0x44, + ESMIESR4 = 0x48, + ESMIECR4 = 0x4C, + ESMILSR4 = 0x50, + ESMILCR4 = 0x54, + ESMSR4 = 0x58, + + ESMIEPSR7 = 0x80, + ESMIEPSC7 = 0x84, + ESMIESR7 = 0x88, + ESMIECR7 = 0x8C, + ESMILSR7 = 0x90, + ESMILCR7 = 0x94, + ESMSR7 = 0x98, +}; + +enum { + ESM_R1, + ESM_R4, + ESM_R7, + ESM_R2, + ESM_R3, +}; + +static void hercules_esm_irq_lower(HerculesESMState *s, int n) +{ + const unsigned int bit = BIT(n); + + if (s->irq_state & bit) { + qemu_irq_lower(s->irq[n]); + s->irq_state &= ~bit; + } +} + +static void hercules_esm_irq_raise(HerculesESMState *s, int n) +{ + const unsigned int bit = BIT(n); + + if (s->irq_state & bit) { + return; + } + + s->irq_state |= bit; + qemu_irq_raise(s->irq[n]); +} + +static void hercules_esm_set_error(void *opaque, int error, int level) +{ + HerculesESMState *s = opaque; + unsigned int idx; + unsigned int bit; + + if (level) { + switch (error) { + case 0 ... 95: + idx = error / 32; + bit = BIT(error % 32); + + s->esmsr[idx] |= bit; + + if (s->esmie[idx] & bit) { + hercules_esm_irq_raise(s, (s->esmil[idx] & bit) ? + HERCULES_ESM_IRQ_HIGH : + HERCULES_ESM_IRQ_LOW); + } + break; + case 96 ... 127: + /* group 2 */ + s->esmsr[ESM_R2] |= BIT(error - 96); + hercules_esm_irq_raise(s, HERCULES_ESM_IRQ_HIGH); + break; + case 128 ... 159: + s->esmsr[ESM_R3] |= BIT(error - 128); + /* FIXME: Need to abort here */ + /* fall through */ + default: + return; + } + } +} + +enum HerculesESMIOffMask { + ESMIOFF_HIGH = 0x00000000, + ESMIOFF_LOW = 0xFFFFFFFF, +}; + +static unsigned int hercules_esm_interrupt_offset_high(HerculesESMState *s) +{ + const uint32_t pending = s->esmie[ESM_R2] & s->esmsr[ESM_R2]; + + if (pending) { + return 0x21 + ctzl(pending); + } + + return 0; +} + +static unsigned int +hercules_esm_interrupt_offset_low(HerculesESMState *s, + enum HerculesESMIOffMask mask) +{ + uint32_t pending; + + pending = s->esmie[ESM_R1] & (s->esmsr[ESM_R1] ^ mask); + if (pending) { + return 0x01 + ctzl(pending); + } + + pending = s->esmie[ESM_R4] & (s->esmsr[ESM_R4] ^ mask); + if (pending) { + return 0x41 + ctzl(pending); + } + + pending = s->esmie[ESM_R7] & (s->esmsr[ESM_R7] ^ mask); + if (pending) { + return 0x81 + ctzl(pending); + } + + return 0; +} + +static uint64_t hercules_esm_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesESMState *s = opaque; + int irq; + + switch (offset) { + case ESMIESR1: + case ESMIECR1: + return s->esmie[ESM_R1]; + case ESMILSR1: + case ESMILCR1: + return s->esmil[ESM_R1]; + case ESMSR1: + return s->esmsr[ESM_R1]; + case ESMSR2: + return s->esmsr[ESM_R2]; + case ESMSR3: + return s->esmsr[ESM_R3]; + case ESMIOFFHR: + irq = hercules_esm_interrupt_offset_high(s); + if (irq) { + return irq; + } + return hercules_esm_interrupt_offset_low(s, ESMIOFF_HIGH); + case ESMIOFFLR: + return hercules_esm_interrupt_offset_low(s, ESMIOFF_LOW); + case ESMIEPSR4: + case ESMIEPCR4: + return s->esmiep[ESM_R4]; + case ESMIESR4: + case ESMIECR4: + return s->esmie[ESM_R4]; + case ESMILSR4: + case ESMILCR4: + return s->esmil[ESM_R4]; + case ESMSR4: + return s->esmsr[ESM_R4]; + case ESMIEPSR7: + case ESMIEPSC7: + return s->esmiep[ESM_R7]; + case ESMIESR7: + case ESMIECR7: + return s->esmie[ESM_R7]; + case ESMILSR7: + case ESMILCR7: + return s->esmil[ESM_R7]; + case ESMSR7: + return s->esmsr[ESM_R7]; + case ESMDEPAPR1: + case ESMEEPAPR1: + case ESMEPSR: + return 0; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_esm_update_irq_high(HerculesESMState *s) +{ + if (s->esmsr[ESM_R2] || + s->esmsr[ESM_R1] & s->esmie[ESM_R1] & s->esmil[ESM_R1] || + s->esmsr[ESM_R4] & s->esmie[ESM_R4] & s->esmil[ESM_R4] || + s->esmsr[ESM_R7] & s->esmie[ESM_R7] & s->esmil[ESM_R7]) { + return; + } + + hercules_esm_irq_lower(s, HERCULES_ESM_IRQ_HIGH); +} + +static void hercules_esm_update_irq_low(HerculesESMState *s) +{ + if (s->esmsr[ESM_R1] & s->esmie[ESM_R1] & ~s->esmil[ESM_R1] || + s->esmsr[ESM_R4] & s->esmie[ESM_R4] & ~s->esmil[ESM_R4] || + s->esmsr[ESM_R7] & s->esmie[ESM_R7] & ~s->esmil[ESM_R7]) { + return; + } + + hercules_esm_irq_lower(s, HERCULES_ESM_IRQ_LOW); +} + + +static void hercules_esm_update_irqs(HerculesESMState *s) +{ + hercules_esm_update_irq_high(s); + hercules_esm_update_irq_low(s); +} + +static void hercules_esm_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesESMState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case ESMIESR1: + s->esmie[ESM_R1] |= val; + break; + case ESMIECR1: + s->esmie[ESM_R1] &= ~val; + break; + case ESMILSR1: + s->esmil[ESM_R1] |= val; + break; + case ESMILCR1: + s->esmil[ESM_R1] &= ~val; + break; + case ESMSR1: + s->esmsr[ESM_R1] &= ~val; + hercules_esm_update_irqs(s); + break; + case ESMSR2: + s->esmsr[ESM_R2] &= ~val; + hercules_esm_update_irq_high(s); + break; + case ESMSR3: + s->esmsr[ESM_R3] &= ~val; + break; + case ESMIEPSR4: + s->esmiep[ESM_R4] |= val; + break; + case ESMIEPCR4: + s->esmiep[ESM_R4] &= ~val; + break; + case ESMIESR4: + s->esmie[ESM_R4] |= val; + hercules_esm_update_irqs(s); + break; + case ESMIECR4: + s->esmie[ESM_R4] &= ~val; + hercules_esm_update_irqs(s); + break; + case ESMILSR4: + s->esmil[ESM_R4] |= val; + break; + case ESMILCR4: + s->esmil[ESM_R4] &= ~val; + break; + case ESMSR4: + s->esmsr[ESM_R4] &= ~val; + hercules_esm_update_irqs(s); + break; + case ESMIEPSR7: + s->esmiep[ESM_R7] |= val; + break; + case ESMIEPSC7: + s->esmiep[ESM_R7] &= ~val; + break; + case ESMIESR7: + s->esmie[ESM_R7] |= val; + hercules_esm_update_irqs(s); + break; + case ESMIECR7: + s->esmie[ESM_R7] &= ~val; + hercules_esm_update_irqs(s); + break; + case ESMILSR7: + s->esmil[ESM_R7] |= val; + break; + case ESMILCR7: + s->esmil[ESM_R7] &= ~val; + break; + case ESMSR7: + s->esmsr[ESM_R7] &= ~val; + hercules_esm_update_irqs(s); + break; + case ESMIOFFHR: + case ESMIOFFLR: + case ESMDEPAPR1: + case ESMEEPAPR1: + case ESMLTCPR: + case ESMEKR: + case ESMSSR2: + /* No op */ + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_esm_ops = { + .read = hercules_esm_read, + .write = hercules_esm_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_esm_realize(DeviceState *dev, Error **errp) +{ + HerculesESMState *s = HERCULES_ESM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_esm_ops, + s, TYPE_HERCULES_ESM ".io", HERCULES_ESM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + qdev_init_gpio_in(dev, hercules_esm_set_error, HERCULES_NUM_ESM_CHANNELS); + + sysbus_init_irq(sbd, &s->irq[HERCULES_ESM_IRQ_HIGH]); + sysbus_init_irq(sbd, &s->irq[HERCULES_ESM_IRQ_LOW]); +} + +static void hercules_esm_reset(DeviceState *d) +{ + HerculesESMState *s = HERCULES_ESM(d); + + memset(s->esmsr, 0, sizeof(s->esmsr)); + memset(s->esmil, 0, sizeof(s->esmil)); + memset(s->esmie, 0, sizeof(s->esmie)); + memset(s->esmiep, 0, sizeof(s->esmiep)); + + hercules_esm_irq_lower(s, HERCULES_ESM_IRQ_LOW); + hercules_esm_irq_lower(s, HERCULES_ESM_IRQ_HIGH); +} + +static void hercules_esm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_esm_reset; + dc->realize = hercules_esm_realize; +} + +static const TypeInfo hercules_esm_info = { + .name = TYPE_HERCULES_ESM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesESMState), + .class_init = hercules_esm_class_init, +}; + +static void hercules_esm_register_types(void) +{ + type_register_static(&hercules_esm_info); +} + +type_init(hercules_esm_register_types) diff --git a/include/hw/misc/hercules_esm.h b/include/hw/misc/hercules_esm.h new file mode 100644 index 0000000000000..929c48033cb9a --- /dev/null +++ b/include/hw/misc/hercules_esm.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_ESM_H +#define HERCULES_ESM_H + +enum { + HERCULES_ESM_IRQ_HIGH = 0, + HERCULES_ESM_IRQ_LOW = 1, + HERCULES_ESM_NUM_IRQS = 2, + HERCULES_ESM_NUM_GROUP1 = 96, + HERCULES_ESM_NUM_GROUP2 = 32, + + HERCULES_NUM_ESM_CHANNELS = 160, +}; + +#define HERCULES_ESM_GROUP1(n) (n) +#define HERCULES_ESM_GROUP2(n) (HERCULES_ESM_NUM_GROUP1 + (n)) +#define HERCULES_ESM_GROUP3(n) (HERCULES_ESM_NUM_GROUP2 + \ + HERCULES_ESM_GROUP2(n)) + +enum HerculesESMChannels { + HERCULES_MIBADC2_PARITY_ERROR = HERCULES_ESM_GROUP1(1), + HERCULES_EPC_CORRECTABLE_ERROR = HERCULES_ESM_GROUP1(4), + HERCULES_PLL1_SLIP_ERROR = HERCULES_ESM_GROUP1(10), + HERCULES_MIBADC1_PARITY_ERROR = HERCULES_ESM_GROUP1(19), + + HERCULES_CCMR5F_SELF_TEST_ERROR = HERCULES_ESM_GROUP1(31), + HERCULES_PMM_COMPARE_ERROR = HERCULES_ESM_GROUP1(38), + HERCULES_PMM_SELF_TEST_ERROR = HERCULES_ESM_GROUP1(39), + HERCULES_EFUSE_SINGLE_BIT_ERROR = HERCULES_ESM_GROUP1(40), + HERCULES_EFUSE_SELF_TEST_ERROR = HERCULES_ESM_GROUP1(41), + HERCULES_PLL2_SLIP_ERROR = HERCULES_ESM_GROUP1(42), + + HERCULES_MIBSPI1_SINGLE_BIT_ERROR = HERCULES_ESM_GROUP1(77), + HERCULES_MIBSPI2_SINGLE_BIT_ERROR = HERCULES_ESM_GROUP1(78), + HERCULES_MIBSPI3_SINGLE_BIT_ERROR = HERCULES_ESM_GROUP1(79), + HERCULES_MIBSPI4_SINGLE_BIT_ERROR = HERCULES_ESM_GROUP1(80), + HERCULES_MIBSPI5_SINGLE_BIT_ERROR = HERCULES_ESM_GROUP1(81), + + HERCULES_MIBSPI1_UNCORRECTABLE_ERROR = HERCULES_ESM_GROUP1(17), + HERCULES_MIBSPI2_UNCORRECTABLE_ERROR = HERCULES_ESM_GROUP1(49), + HERCULES_MIBSPI3_UNCORRECTABLE_ERROR = HERCULES_ESM_GROUP1(18), + HERCULES_MIBSPI4_UNCORRECTABLE_ERROR = HERCULES_ESM_GROUP1(50), + HERCULES_MIBSPI5_UNCORRECTABLE_ERROR = HERCULES_ESM_GROUP1(24), + + HERCULES_CCMR5F_CPU_COMPARE_ERROR = HERCULES_ESM_GROUP2(2), + HERCULES_CR5F_FATAL_BUS_ERROR = HERCULES_ESM_GROUP2(3), + HERCULES_L2RAMW_TYPE_B_UNCORRECTABLE_ERROR = HERCULES_ESM_GROUP2(7), + HERCULES_CCMR5F_VIM_COMPARE_ERROR = HERCULES_ESM_GROUP2(25), + HERCULES_CPU1_AXIM_BUS_MONITOR_ERROR = HERCULES_ESM_GROUP2(26), + + HERCULES_EFUSE_AUTOLOAD_ERROR = HERCULES_ESM_GROUP3(1), + + HERCULES_L2FMC_UNCORRECTABLE_ERROR = HERCULES_ESM_GROUP3(13), +}; + +typedef struct HerculesESMState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t esmsr[5]; + uint32_t esmil[5]; + uint32_t esmie[5]; + uint32_t esmiep[5]; + + unsigned int irq_state; + qemu_irq irq[HERCULES_ESM_NUM_IRQS]; +} HerculesESMState; + +#define TYPE_HERCULES_ESM "ti-hercules-esm" +#define HERCULES_ESM(obj) OBJECT_CHECK(HerculesESMState, (obj), \ + TYPE_HERCULES_ESM) +#endif From 82cc13a20b9024e0d1d04d7f495c6176ae2079bb Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 5 Jun 2019 14:42:02 -0700 Subject: [PATCH 16/27] hw/misc: Add support for Hercules eFUSE module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 3 +- hw/misc/hercules_efuse.c | 178 +++++++++++++++++++++++++++++++ include/hw/misc/hercules_efuse.h | 28 +++++ 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_efuse.c create mode 100644 include/hw/misc/hercules_efuse.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 706f383860528..7aebf1d29102d 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -86,7 +86,8 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_xdma.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o common-obj-$(CONFIG_MSF2) += msf2-sysreg.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o -common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o hercules_esm.o +common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o \ + hercules_esm.o hercules_efuse.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_efuse.c b/hw/misc/hercules_efuse.c new file mode 100644 index 0000000000000..ef40018abc14b --- /dev/null +++ b/hw/misc/hercules_efuse.c @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_efuse.h" + +enum { + HERCULES_EFUSE_SIZE = 256, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesEFuseRegisters { + EFCBOUND = 0x1C, + EFCBOUND_SELF_TEST_ERR = BIT(21), + EFCBOUND_SINGLE_BIT_ERR = BIT(20), + EFCBOUND_INSTR_ERR = BIT(19), + EFCBOUND_AUTOLOAD_ERR = BIT(18), + EFCBOUND_OUTPUT_EN = (0xf << 14), + EFCBOUND_INPUT_EN = (0xf << 0), + EFCBOUND_SELF_TEST_EN = BIT(13), + + EFCPINS = 0x2C, + EFCPINS_SELF_TEST_DONE = BIT(15), + EFCPINS_SELF_TEST_ERR = BIT(14), + EFCPINS_SINGLE_BIT_ERR = BIT(12), + EFCPINS_INSTR_ERR = BIT(11), + EFCPINS_AUTOLOAD_ERR = BIT(10), + EFCERRSTAT = 0x3C, + + EFCSTCY = 0x48, + EFCSTSIG = 0x4C, +}; + +static uint64_t hercules_efuse_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesEFuseState *s = opaque; + + switch (offset) { + case EFCPINS: + return s->efcpins; + case EFCBOUND: + case EFCERRSTAT: + break; + case EFCSTCY: + return s->efcstcy; + case EFCSTSIG: + return s->efcstsig; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_efuse_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesEFuseState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case EFCBOUND: + if ((val & EFCBOUND_INPUT_EN) == EFCBOUND_INPUT_EN) { + if (val & EFCBOUND_SELF_TEST_EN && + s->efcstcy == 0x00000258 && + s->efcstsig == 0x5362F97F) { + s->efcpins = EFCPINS_SELF_TEST_DONE; + } + } + + if ((val & EFCBOUND_OUTPUT_EN) == EFCBOUND_OUTPUT_EN) { + if (val & EFCBOUND_AUTOLOAD_ERR) { + s->efcpins |= EFCPINS_AUTOLOAD_ERR; + qemu_irq_raise(s->autoload_error); + } + + if (val & EFCBOUND_INSTR_ERR) { + s->efcpins |= EFCPINS_INSTR_ERR; + } + + if (val & EFCBOUND_SINGLE_BIT_ERR) { + s->efcpins |= EFCPINS_SINGLE_BIT_ERR; + } + + if (val & EFCBOUND_SELF_TEST_ERR) { + s->efcpins |= EFCPINS_SELF_TEST_ERR; + qemu_irq_raise(s->self_test_error); + } + } + case EFCPINS: + case EFCERRSTAT: + break; + case EFCSTCY: + s->efcstcy = val; + break; + case EFCSTSIG: + s->efcstsig = val; + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_efuse_ops = { + .read = hercules_efuse_read, + .write = hercules_efuse_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_efuse_realize(DeviceState *dev, Error **errp) +{ + HerculesEFuseState *s = HERCULES_EFUSE(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_efuse_ops, + s, TYPE_HERCULES_EFUSE ".io", HERCULES_EFUSE_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + sysbus_init_irq(sbd, &s->autoload_error); + sysbus_init_irq(sbd, &s->self_test_error); + sysbus_init_irq(sbd, &s->single_bit_error); +} + +static void hercules_efuse_reset(DeviceState *d) +{ + HerculesEFuseState *s = HERCULES_EFUSE(d); + + s->efcpins = 0; +} + +static void hercules_efuse_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_efuse_reset; + dc->realize = hercules_efuse_realize; +} + +static const TypeInfo hercules_efuse_info = { + .name = TYPE_HERCULES_EFUSE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesEFuseState), + .class_init = hercules_efuse_class_init, +}; + +static void hercules_efuse_register_types(void) +{ + type_register_static(&hercules_efuse_info); +} + +type_init(hercules_efuse_register_types) diff --git a/include/hw/misc/hercules_efuse.h b/include/hw/misc/hercules_efuse.h new file mode 100644 index 0000000000000..eb27f7d52daa3 --- /dev/null +++ b/include/hw/misc/hercules_efuse.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_EFUSE_H +#define HERCULES_EFUSE_H + +typedef struct HerculesEFuseState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t efcpins; + uint32_t efcstcy; + uint32_t efcstsig; + + qemu_irq autoload_error; + qemu_irq self_test_error; + qemu_irq single_bit_error; +} HerculesEFuseState; + +#define TYPE_HERCULES_EFUSE "ti-hercules-efuse" +#define HERCULES_EFUSE(obj) OBJECT_CHECK(HerculesEFuseState, (obj), \ + TYPE_HERCULES_EFUSE) +#endif From d544e0ff234880eb50d328e93ba39dcff4899b08 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 5 Jun 2019 14:53:09 -0700 Subject: [PATCH 17/27] hw/misc: Add support for Hercules L2RAMW module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 3 +- hw/misc/hercules_l2ramw.c | 227 ++++++++++++++++++++++++++++++ include/hw/misc/hercules_l2ramw.h | 35 +++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_l2ramw.c create mode 100644 include/hw/misc/hercules_l2ramw.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 7aebf1d29102d..0fb97bf2d24ca 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -87,7 +87,8 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o common-obj-$(CONFIG_MSF2) += msf2-sysreg.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o \ - hercules_esm.o hercules_efuse.o + hercules_esm.o hercules_efuse.o \ + hercules_l2ramw.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_l2ramw.c b/hw/misc/hercules_l2ramw.c new file mode 100644 index 0000000000000..203e5424e7a3c --- /dev/null +++ b/hw/misc/hercules_l2ramw.c @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" + +#include "hw/misc/hercules_l2ramw.h" + +enum { + HERCULES_L2RAMW_CONTAINER_SIZE = 8 * 1024 * 1024, + HERCULES_L2RAMW_SRAM_SIZE = 512 * 1024, + HERCULES_L2RAMW_SRAM_OFFSET = 0, + HERCULES_L2RAMW_ECC_SIZE = HERCULES_L2RAMW_SRAM_SIZE, + HERCULES_L2RAMW_ECC_OFFSET = HERCULES_L2RAMW_CONTAINER_SIZE / 2, + HERCULES_L2RAMW_SIZE = 256, +}; + +enum HerculesL2RAMWRegisters { + RAMCTRL = 0x0000, + RAMERRSTATUS = 0x0010, + DRDE = BIT(22), + DRSE = BIT(21), + DWDE = BIT(20), + DWSE = BIT(19), + ADDE = BIT(4), + ADE = BIT(2), + DIAG_DATA_VECTOR_H = 0x0024, + DIAG_DATA_VECTOR_L = 0x0028, + DIAG_ECC = 0x002C, + RAMTEST = 0x0030, + TRIGGER = BIT(8), + RAMADDRDEC_VECT = 0x0038, + MEMINIT_DOMAIN = 0x003C, + BANK_DOMAIN_MAP0 = 0x0044, + BANK_DOMAIN_MAP1 = 0x0048, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +#define TEST_ENABLE(w) extract32(w, 0, 4) +#define TEST_MODE(w) extract32(w, 6, 2) + +static void hercules_l2ramw_write(void *opaque, hwaddr offset, uint64_t val64, + unsigned size) +{ + HerculesL2RamwState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case RAMCTRL: + s->ramctrl = val; + break; + case RAMTEST: + s->ramtest = val; + if (s->ramtest & TRIGGER) { + s->ramtest &= ~TRIGGER; + + switch (s->diag_ecc) { + case 0x03: + s->ramerrstatus |= DRDE | DWDE; + qemu_irq_raise(s->uncorrectable_error); + break; + case 0xCE: + s->ramerrstatus |= DRSE | DWSE; + qemu_irq_raise(s->uncorrectable_error); + break; + default: + break; + } + } + break; + case RAMERRSTATUS: + s->ramerrstatus &= ~val; + break; + case DIAG_ECC: + s->diag_ecc = val; + break; + case DIAG_DATA_VECTOR_H: + case DIAG_DATA_VECTOR_L: + case RAMADDRDEC_VECT: + case MEMINIT_DOMAIN: + case BANK_DOMAIN_MAP0: + case BANK_DOMAIN_MAP1: + break; + default: + qemu_log_bad_offset(offset); + } +} + +static uint64_t hercules_l2ramw_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesL2RamwState *s = opaque; + + switch (offset) { + case RAMCTRL: + return s->ramctrl; + case RAMTEST: + return s->ramtest; + case RAMERRSTATUS: + return s->ramerrstatus; + case DIAG_ECC: + return s->diag_ecc; + case DIAG_DATA_VECTOR_H: + case DIAG_DATA_VECTOR_L: + case RAMADDRDEC_VECT: + case MEMINIT_DOMAIN: + case BANK_DOMAIN_MAP0: + case BANK_DOMAIN_MAP1: + break; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static const MemoryRegionOps hercules_l2ramw_ops = { + .read = hercules_l2ramw_read, + .write = hercules_l2ramw_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_l2ramw_ecc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + qemu_log_bad_offset(offset); +} + +static uint64_t hercules_l2ramw_ecc_read(void *opaque, hwaddr offset, + unsigned size) +{ + qemu_log_bad_offset(offset); + return 0; +} + +static const MemoryRegionOps hercules_l2ramw_ecc_ops = { + .read = hercules_l2ramw_ecc_read, + .write = hercules_l2ramw_ecc_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_l2ramw_realize(DeviceState *dev, Error **errp) +{ + HerculesL2RamwState *s = HERCULES_L2RAMW(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->io.ecc, OBJECT(dev), &hercules_l2ramw_ecc_ops, s, + TYPE_HERCULES_L2RAMW ".io.ecc", + HERCULES_L2RAMW_ECC_SIZE); + + memory_region_init_ram(&s->io.sram, OBJECT(dev), + TYPE_HERCULES_L2RAMW ".io.sram", + HERCULES_L2RAMW_SRAM_SIZE, &error_fatal); + + memory_region_init(&s->io.container, OBJECT(dev), + TYPE_HERCULES_L2RAMW ".io", + HERCULES_L2RAMW_CONTAINER_SIZE); + + memory_region_add_subregion(&s->io.container, HERCULES_L2RAMW_SRAM_OFFSET, + &s->io.sram); + memory_region_add_subregion(&s->io.container, HERCULES_L2RAMW_ECC_OFFSET, + &s->io.ecc); + + sysbus_init_mmio(sbd, &s->io.container); + + memory_region_init_io(&s->io.regs, OBJECT(dev), &hercules_l2ramw_ops, s, + TYPE_HERCULES_L2RAMW ".io.regs", + HERCULES_L2RAMW_SIZE); + + sysbus_init_mmio(sbd, &s->io.regs); + sysbus_init_irq(sbd, &s->uncorrectable_error); +} + +static void hercules_l2ramw_reset(DeviceState *dev) +{ + HerculesL2RamwState *s = HERCULES_L2RAMW(dev); + + s->ramctrl = 0; + s->ramtest = 0; + s->ramerrstatus = 0; + s->diag_ecc = 0; +} + +static void hercules_l2ramw_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_l2ramw_reset; + dc->realize = hercules_l2ramw_realize; + + dc->desc = "Hercules Level II RAM Module"; +} + +static const TypeInfo hercules_l2ramw_info = { + .name = TYPE_HERCULES_L2RAMW, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesL2RamwState), + .class_init = hercules_l2ramw_class_init, +}; + +static void hercules_l2ramw_register_types(void) +{ + type_register_static(&hercules_l2ramw_info); +} + +type_init(hercules_l2ramw_register_types) diff --git a/include/hw/misc/hercules_l2ramw.h b/include/hw/misc/hercules_l2ramw.h new file mode 100644 index 0000000000000..39b71e5299193 --- /dev/null +++ b/include/hw/misc/hercules_l2ramw.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_L2RAMW_H +#define HERCULES_L2RAMW_H + +#include "hw/sysbus.h" + +typedef struct HerculesL2RamwState { + SysBusDevice parent_obj; + + struct { + MemoryRegion ecc; + MemoryRegion sram; + MemoryRegion container; + + MemoryRegion regs; + } io; + + uint32_t ramctrl; + uint32_t ramtest; + uint32_t ramerrstatus; + uint32_t diag_ecc; + + qemu_irq uncorrectable_error; +} HerculesL2RamwState; + +#define TYPE_HERCULES_L2RAMW "ti-hercules-l2ramw" +#define HERCULES_L2RAMW(obj) OBJECT_CHECK(HerculesL2RamwState, (obj), \ + TYPE_HERCULES_L2RAMW) +#endif From c601c8302fd35625eac654403005cdda48690a04 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 9 Jun 2019 16:24:13 -0700 Subject: [PATCH 18/27] hw/misc: Add support for Hercules PMM module Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 2 +- hw/misc/hercules_pmm.c | 162 +++++++++++++++++++++++++++++++++ include/hw/misc/hercules_pmm.h | 27 ++++++ 3 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_pmm.c create mode 100644 include/hw/misc/hercules_pmm.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 0fb97bf2d24ca..ca8403ba00113 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -88,7 +88,7 @@ common-obj-$(CONFIG_MSF2) += msf2-sysreg.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o \ hercules_esm.o hercules_efuse.o \ - hercules_l2ramw.o + hercules_l2ramw.o hercules_pmm.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_pmm.c b/hw/misc/hercules_pmm.c new file mode 100644 index 0000000000000..900f31d2248da --- /dev/null +++ b/hw/misc/hercules_pmm.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_pmm.h" + +enum { + HERCULES_PMM_SIZE = 256, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesPMMRegisters { + PRCKEYREG = 0xAC, + MKEY_LOCK_STEP = 0x0, + MKEY_SELF_TEST = 0x6, + MKEY_ERROR_FORCING = 0x9, + MKEY_SELF_TEST_ERROR_FORCING = 0xF, + LPDDCSTAT1 = 0xB0, + LPDDCSTAT2 = 0xB4, + + LPDDCSTAT1_LCMPE_ALL = BIT(20) | BIT(19) | BIT(18) | BIT(17) | BIT(16), + LPDDCSTAT1_LSTC_ALL = BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0), +}; + +static uint64_t hercules_pmm_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesPMMState *s = opaque; + + switch (offset) { + case PRCKEYREG: + return s->prckeyreg; + case LPDDCSTAT1: + return s->lpddcstat1; + case LPDDCSTAT2: + return s->lpddcstat2; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_pmm_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesPMMState *s = opaque; + uint32_t val = val64; + qemu_irq error = NULL; + + switch (offset) { + case PRCKEYREG: + s->prckeyreg = val & 0xF; + + switch (s->prckeyreg) { + case MKEY_ERROR_FORCING: + error = s->compare_error; + break; + case MKEY_SELF_TEST_ERROR_FORCING: + error = s->self_test_error; + break; + case MKEY_SELF_TEST: + break; + default: + return; + } + + s->lpddcstat1 |= LPDDCSTAT1_LSTC_ALL; + + if (error) { + qemu_irq_raise(error); + } + + break; + case LPDDCSTAT1: + val &= LPDDCSTAT1_LCMPE_ALL; + s->lpddcstat1 &= ~val; + case LPDDCSTAT2: + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_pmm_ops = { + .read = hercules_pmm_read, + .write = hercules_pmm_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_pmm_realize(DeviceState *dev, Error **errp) +{ + HerculesPMMState *s = HERCULES_PMM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_pmm_ops, + s, TYPE_HERCULES_PMM ".io", HERCULES_PMM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + sysbus_init_irq(sbd, &s->compare_error); + sysbus_init_irq(sbd, &s->self_test_error); +} + +static void hercules_pmm_reset(DeviceState *d) +{ + HerculesPMMState *s = HERCULES_PMM(d); + + s->prckeyreg = 0; + s->lpddcstat1 = 0; + s->lpddcstat2 = 0; + + qemu_irq_lower(s->compare_error); + qemu_irq_lower(s->self_test_error); +} + +static void hercules_pmm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_pmm_reset; + dc->realize = hercules_pmm_realize; +} + +static const TypeInfo hercules_pmm_info = { + .name = TYPE_HERCULES_PMM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesPMMState), + .class_init = hercules_pmm_class_init, +}; + +static void hercules_pmm_register_types(void) +{ + type_register_static(&hercules_pmm_info); +} + +type_init(hercules_pmm_register_types) diff --git a/include/hw/misc/hercules_pmm.h b/include/hw/misc/hercules_pmm.h new file mode 100644 index 0000000000000..d080af60087b0 --- /dev/null +++ b/include/hw/misc/hercules_pmm.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_PMM_H +#define HERCULES_PMM_H + +typedef struct HerculesPMMState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t prckeyreg; + uint32_t lpddcstat1; + uint32_t lpddcstat2; + + qemu_irq compare_error; + qemu_irq self_test_error; +} HerculesPMMState; + +#define TYPE_HERCULES_PMM "ti-hercules-pmm" +#define HERCULES_PMM(obj) OBJECT_CHECK(HerculesPMMState, (obj), \ + TYPE_HERCULES_PMM) +#endif From c79ecfd35f440a5516041dfafb72108c3675908c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 19 Jun 2019 14:40:45 -0700 Subject: [PATCH 19/27] hw/misc: Add emulation for Hercules STC block Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 3 +- hw/misc/hercules_stc.c | 180 +++++++++++++++++++++++++++++++++ include/hw/misc/hercules_stc.h | 29 ++++++ 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_stc.c create mode 100644 include/hw/misc/hercules_stc.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index ca8403ba00113..e528847e4a74f 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -88,7 +88,8 @@ common-obj-$(CONFIG_MSF2) += msf2-sysreg.o common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o \ hercules_esm.o hercules_efuse.o \ - hercules_l2ramw.o hercules_pmm.o + hercules_l2ramw.o hercules_pmm.o \ + hercules_stc.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_stc.c b/hw/misc/hercules_stc.c new file mode 100644 index 0000000000000..a03271c9cd5c4 --- /dev/null +++ b/hw/misc/hercules_stc.c @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_stc.h" + +enum { + HERCULES_STC_SIZE = 256, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesSTCRegisters { + STCGCR0 = 0x0000, + STCGCR1 = 0x0004, + STCTPR = 0x0008, + STCGSTAT = 0x0014, + TEST_DONE = BIT(0), + TEST_FAIL = BIT(1), + STCFSTAT = 0x0018, + STCSCSCR = 0x003C, + FAULT_INS = BIT(4), + STCCLKDIV = 0x0044, +}; + +#define STC_ENA(w) extract32(w, 0, 4) + +static uint64_t hercules_stc_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesSTCState *s = opaque; + + switch (offset) { + case STCGCR0: + return s->stcgcr[0]; + case STCGCR1: + return s->stcgcr[1]; + case STCTPR: + return s->stctpr; + case STCFSTAT: + break; + case STCGSTAT: + return s->stcgstat; + case STCSCSCR: + return s->stcscscr; + case STCCLKDIV: + return s->stcclkdiv; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_stc_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesSTCState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case STCGCR0: + s->stcgcr[0] = val; + break; + case STCGCR1: + s->stcgcr[1] = val; + + if (STC_ENA(val) == 0xA) { + qemu_bh_schedule(s->self_test); + } + break; + case STCTPR: + s->stctpr = val; + break; + case STCGSTAT: + s->stcgstat &= ~val; + break; + case STCFSTAT: + break; + case STCSCSCR: + s->stcscscr = val; + break; + case STCCLKDIV: + s->stcclkdiv = val; + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_stc_ops = { + .read = hercules_stc_read, + .write = hercules_stc_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_stc_self_test(void *opaque) +{ + HerculesSTCState *s = opaque; + CPUState *cpu = qemu_get_cpu(0); + + if (cpu->halted) { + s->stcgstat |= TEST_DONE; + if (s->stcscscr & FAULT_INS) { + s->stcgstat |= TEST_FAIL; + } + + qemu_irq_raise(s->cpurst); + return; + } + + qemu_bh_schedule(s->self_test); +} + +static void hercules_stc_realize(DeviceState *dev, Error **errp) +{ + HerculesSTCState *s = HERCULES_STC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_stc_ops, + s, TYPE_HERCULES_STC ".io", HERCULES_STC_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + s->self_test = qemu_bh_new(hercules_stc_self_test, s); + sysbus_init_irq(sbd, &s->cpurst); +} + +static void hercules_stc_reset(DeviceState *d) +{ + HerculesSTCState *s = HERCULES_STC(d); + (void)s; +} + +static void hercules_stc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_stc_reset; + dc->realize = hercules_stc_realize; +} + +static const TypeInfo hercules_stc_info = { + .name = TYPE_HERCULES_STC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesSTCState), + .class_init = hercules_stc_class_init, +}; + +static void hercules_stc_register_types(void) +{ + type_register_static(&hercules_stc_info); +} + +type_init(hercules_stc_register_types) diff --git a/include/hw/misc/hercules_stc.h b/include/hw/misc/hercules_stc.h new file mode 100644 index 0000000000000..7d2f349c45d6f --- /dev/null +++ b/include/hw/misc/hercules_stc.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_STC_H +#define HERCULES_STC_H + +typedef struct HerculesSTCState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t stcgcr[2]; + uint32_t stctpr; + uint32_t stcscscr; + uint32_t stcclkdiv; + uint32_t stcgstat; + + QEMUBH *self_test; + qemu_irq cpurst; +} HerculesSTCState; + +#define TYPE_HERCULES_STC "ti-hercules-stc" +#define HERCULES_STC(obj) OBJECT_CHECK(HerculesSTCState, (obj), \ + TYPE_HERCULES_STC) +#endif From ef5c1552071b8004b0be0a99a0ff4fddf2e9496e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 19 Jun 2019 16:44:11 -0700 Subject: [PATCH 20/27] hw/misc: Add emulation for Hercules PBIST block Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 2 +- hw/misc/hercules_pbist.c | 180 +++++++++++++++++++++++++++++++ include/hw/misc/hercules_pbist.h | 30 ++++++ 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_pbist.c create mode 100644 include/hw/misc/hercules_pbist.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index e528847e4a74f..5e92c63e89309 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -89,7 +89,7 @@ common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o \ hercules_esm.o hercules_efuse.o \ hercules_l2ramw.o hercules_pmm.o \ - hercules_stc.o + hercules_stc.o hercules_pbist.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_pbist.c b/hw/misc/hercules_pbist.c new file mode 100644 index 0000000000000..ddde5a2060678 --- /dev/null +++ b/hw/misc/hercules_pbist.c @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_pbist.h" + +enum { + HERCULES_PBIST_SIZE = 512, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesPBISTRegisters { + RAMT = 0x160, + DLR = 0x164, + DLR2 = BIT(2), + STC = 0x16C, + PACT = 0x180, + FSRF0 = 0x190, + FSRF1 = 0x194, + FSRFx = BIT(0), + OVER = 0x188, + ROM = 0x1C0, + ALGO = 0x1C4, + RINFOL = 0x1C8, + RINFOU = 0x1CC, +}; + +static uint64_t hercules_pbist_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesPBISTState *s = opaque; + + switch (offset) { + case PACT: + return s->pact; + case FSRF0: + return s->fsrf[0]; + case FSRF1: + return s->fsrf[1]; + case OVER: + return s->over; + case ROM: + return s->rom; + case ALGO: + return s->algo; + case RINFOL: + return s->rinfol; + case RINFOU: + return s->rinfou; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_pbist_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesPBISTState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + /* + * Magic undocumented registers used by PBIST code + */ + case 0x00 ... 0x18: + case 0x40 ... 0x58: + break; + case RAMT: + case STC: + break; + case PACT: + s->pact = val; + break; + case DLR: + /* + * Not how HW works, but good enough to get things running + */ + if (val & DLR2) { + s->fsrf[0] = 0; + s->fsrf[1] = 0; + } else { + s->fsrf[0] = FSRFx; + s->fsrf[1] = FSRFx; + } + qemu_irq_raise(s->mstdone); + break; + case OVER: + s->over = val; + break; + case ROM: + s->rom = val; + break; + case ALGO: + s->algo = val; + break; + case RINFOL: + s->rinfol = val; + break; + case RINFOU: + s->rinfou = val; + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_pbist_ops = { + .read = hercules_pbist_read, + .write = hercules_pbist_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_pbist_realize(DeviceState *dev, Error **errp) +{ + HerculesPBISTState *s = HERCULES_PBIST(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_pbist_ops, + s, TYPE_HERCULES_PBIST ".io", HERCULES_PBIST_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + sysbus_init_irq(sbd, &s->mstdone); +} + +static void hercules_pbist_reset(DeviceState *d) +{ + HerculesPBISTState *s = HERCULES_PBIST(d); + + s->pact = 0; +} + +static void hercules_pbist_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_pbist_reset; + dc->realize = hercules_pbist_realize; +} + +static const TypeInfo hercules_pbist_info = { + .name = TYPE_HERCULES_PBIST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesPBISTState), + .class_init = hercules_pbist_class_init, +}; + +static void hercules_pbist_register_types(void) +{ + type_register_static(&hercules_pbist_info); +} + +type_init(hercules_pbist_register_types) diff --git a/include/hw/misc/hercules_pbist.h b/include/hw/misc/hercules_pbist.h new file mode 100644 index 0000000000000..dac799ab5d42e --- /dev/null +++ b/include/hw/misc/hercules_pbist.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_PBIST_H +#define HERCULES_PBIST_H + +typedef struct HerculesPBISTState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t pact; + uint32_t fsrf[2]; + uint32_t over; + uint32_t rom; + uint32_t algo; + uint32_t rinfol; + uint32_t rinfou; + + qemu_irq mstdone; +} HerculesPBISTState; + +#define TYPE_HERCULES_PBIST "ti-hercules-pbist" +#define HERCULES_PBIST(obj) OBJECT_CHECK(HerculesPBISTState, (obj), \ + TYPE_HERCULES_PBIST) +#endif From f82d2b504cd01d7a5805286cb12254079a90dc80 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 19 Jun 2019 21:12:17 -0700 Subject: [PATCH 21/27] hw/misc: Add support for Hercules CCM IP block Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 3 +- hw/misc/hercules_ccm.c | 169 +++++++++++++++++++++++++++++++++ include/hw/misc/hercules_ccm.h | 26 +++++ 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_ccm.c create mode 100644 include/hw/misc/hercules_ccm.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 5e92c63e89309..006ce50ca0d5a 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -89,7 +89,8 @@ common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o \ hercules_esm.o hercules_efuse.o \ hercules_l2ramw.o hercules_pmm.o \ - hercules_stc.o hercules_pbist.o + hercules_stc.o hercules_pbist.o \ + hercules_ccm.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_ccm.c b/hw/misc/hercules_ccm.c new file mode 100644 index 0000000000000..956f316dd5e52 --- /dev/null +++ b/hw/misc/hercules_ccm.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_ccm.h" + +enum { + HERCULES_CCM_SIZE = 256, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesCCMRegisters { + CCMSR1 = 0x00, + CCMKEYR1 = 0x04, + CCMSR2 = 0x08, + CCMKEYR2 = 0x0C, + CCMSR3 = 0x10, + CCMKEYR3 = 0x14, + MKEYn_SELF_TEST = 0x6, + MKEYn_ERROR_FORCING = 0x9, + MKEYn_SELF_TEST_ERROR_FORCING = 0xF, + STCn = BIT(8), +}; + +static uint64_t hercules_ccm_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesCCMState *s = opaque; + + switch (offset) { + + case CCMSR1: + return s->ccmsr[0]; + case CCMSR2: + return s->ccmsr[1]; + case CCMSR3: + return s->ccmsr[2]; + case CCMKEYR1: + case CCMKEYR2: + case CCMKEYR3: + break; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_ccm_test(HerculesCCMState *s, unsigned int idx, + uint32_t val) +{ + switch (val) { + case MKEYn_SELF_TEST: + s->ccmsr[idx] |= STCn; + break; + case MKEYn_ERROR_FORCING: + qemu_irq_raise(s->error[idx]); + qemu_irq_raise(s->error_self_test); + break; + case MKEYn_SELF_TEST_ERROR_FORCING: + qemu_irq_raise(s->error_self_test); + break; + } +} + +static void hercules_ccm_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesCCMState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case CCMSR1: + s->ccmsr[0] &= ~val; + break; + case CCMSR2: + s->ccmsr[1] &= ~val; + break; + case CCMSR3: + s->ccmsr[2] &= ~val; + break; + case CCMKEYR1: + hercules_ccm_test(s, 0, val); + break; + case CCMKEYR2: + hercules_ccm_test(s, 1, val); + break; + case CCMKEYR3: + hercules_ccm_test(s, 2, val); + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_ccm_ops = { + .read = hercules_ccm_read, + .write = hercules_ccm_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_ccm_realize(DeviceState *dev, Error **errp) +{ + HerculesCCMState *s = HERCULES_CCM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_ccm_ops, + s, TYPE_HERCULES_CCM ".io", HERCULES_CCM_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + sysbus_init_irq(sbd, &s->error[0]); + sysbus_init_irq(sbd, &s->error[1]); + sysbus_init_irq(sbd, &s->error[2]); + sysbus_init_irq(sbd, &s->error_self_test); +} + +static void hercules_ccm_reset(DeviceState *d) +{ + HerculesCCMState *s = HERCULES_CCM(d); + + memset(s->ccmsr, 0, sizeof(s->ccmsr)); +} + +static void hercules_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_ccm_reset; + dc->realize = hercules_ccm_realize; +} + +static const TypeInfo hercules_ccm_info = { + .name = TYPE_HERCULES_CCM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesCCMState), + .class_init = hercules_ccm_class_init, +}; + +static void hercules_ccm_register_types(void) +{ + type_register_static(&hercules_ccm_info); +} +type_init(hercules_ccm_register_types) diff --git a/include/hw/misc/hercules_ccm.h b/include/hw/misc/hercules_ccm.h new file mode 100644 index 0000000000000..86827f80de8d8 --- /dev/null +++ b/include/hw/misc/hercules_ccm.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_CCM_H +#define HERCULES_CCM_H + +#include "hercules_system.h" + +typedef struct HerculesCCMState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint32_t ccmsr[3]; + + qemu_irq error[3]; + qemu_irq error_self_test; +} HerculesCCMState; + +#define TYPE_HERCULES_CCM "ti-hercules-ccm" +#define HERCULES_CCM(obj) OBJECT_CHECK(HerculesCCMState, (obj), \ + TYPE_HERCULES_CCM) +#endif From 6dfa9375eaf203776633e4413a0e230bb2710024 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sat, 22 Jun 2019 20:30:21 -0700 Subject: [PATCH 22/27] hw/misc: Add support for Hercules L2FMC IP block Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 2 +- hw/misc/hercules_l2fmc.c | 323 +++++++++++++++++++++++++++++++ include/hw/misc/hercules_l2fmc.h | 40 ++++ 3 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_l2fmc.c create mode 100644 include/hw/misc/hercules_l2fmc.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 006ce50ca0d5a..2f0f3dd441777 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -90,7 +90,7 @@ common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o \ hercules_esm.o hercules_efuse.o \ hercules_l2ramw.o hercules_pmm.o \ hercules_stc.o hercules_pbist.o \ - hercules_ccm.o + hercules_ccm.o hercules_l2fmc.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_l2fmc.c b/hw/misc/hercules_l2fmc.c new file mode 100644 index 0000000000000..ee345e490f910 --- /dev/null +++ b/hw/misc/hercules_l2fmc.c @@ -0,0 +1,323 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_l2fmc.h" + +enum { + HERCULES_L2FMC_SIZE = 4 * 1024, + HERCULES_EPC_SIZE = 1024, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesL2FMCRegisters { + FRDCNTL = 0x0000, + EE_FEDACCTRL1 = 0x0008, + FEDAC_PASTATUS = 0x0014, + FEDAC_PBSTATUS = 0x0018, + ADD_PAR_ERR = BIT(10), + ADD_TAG_ERR = BIT(11), + FEDAC_GBLSTATUS = 0x001C, + FEDACSDIS = 0x0024, + FPRIM_ADD_TAG = 0x0028, + FDUP_ADD_TAG = 0x002C, + FBPROT = 0x0030, + FBSE = 0x0034, + FBBUSY = 0x0038, + FBAC = 0x003C, + FBPWRMODE = 0x0040, + FBPRDY = 0x0044, + FPAC1 = 0x0048, + FMAC = 0x0050, + FMSTAT = 0x0054, + FEMU_DMSW = 0x0058, + FEMU_DLSW = 0x005C, + FEMU_ECC = 0x0060, + FLOCK = 0x0064, + FDIAGCTRL = 0x006C, + DIAG_TRIG = BIT(24), + FRAW_ADDR = 0x0074, + FPAR_OVR = 0x007C, + RCR_VALID = 0x00B4, + ACC_THRESHOLD = 0x00B8, + FEDACSDIS2 = 0x00C0, + RCR_VALUE0 = 0x00D0, + RCR_VALUE1 = 0x00D4, + FSM_WR_ENA = 0x0288, + EEPROM_CONFIG = 0x02B8, + FSM_SECTOR1 = 0x02C0, + FSM_SECTOR2 = 0x02C4, + FCFG_BANK = 0x02B8, +}; + +#define DIAG_EN_KEY(w) extract32(w, 16, 4) +#define DIAG_BUF_SEL(w) extract32(w, 8, 3) +#define DIAGMODE(w) extract32(w, 0, 3) +#define DIAGMODE_ADDR 0x5 +#define DIAGMODE_ECC 0x7 + +enum HerculesEPCRegisters { + EPCREVnID = 0x0000, + EPCCNTRL = 0x0004, + UERRSTAT = 0x0008, + EPCERRSTAT = 0x000C, + FIFOFULLSTAT = 0x0010, + OVRFLWSTAT = 0x0014, + CAMAVAILSTAT = 0x0018, +}; + +#define UERRADDR(n) (0x0020 + (n) * sizeof(uint32_t)) +#define CAM_CONTENT(n) (0x00A0 + (n) * sizeof(uint32_t)) +#define CAM_INDEX(n) (0x0200 + (n) * sizeof(uint32_t)) + +static uint64_t hercules_epc_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesL2FMCState *s = opaque; + + switch (offset) { + case CAMAVAILSTAT: + if (s->camavailstat) { + return ctzl(s->camavailstat) + 1; + } + break; + case CAM_CONTENT(0) ... CAM_CONTENT(31): + return s->cam_content[(offset - CAM_CONTENT(0)) / sizeof(uint32_t)]; + case CAM_INDEX(0) ... CAM_INDEX(7): + return s->cam_index[(offset - CAM_INDEX(0)) / sizeof(uint32_t)]; + } + + return 0; +} + +static void hercules_epc_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesL2FMCState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case CAMAVAILSTAT: + break; + case CAM_CONTENT(0) ... CAM_CONTENT(31): + s->cam_content[(offset - CAM_CONTENT(0)) / sizeof(uint32_t)] = val; + break; + case CAM_INDEX(0) ... CAM_INDEX(7): + s->cam_index[(offset - CAM_INDEX(0)) / sizeof(uint32_t)] = val; + s->camavailstat = 0; + break; + } +} + +static const MemoryRegionOps hercules_epc_ops = { + .read = hercules_epc_read, + .write = hercules_epc_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static uint64_t hercules_l2fmc_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesL2FMCState *s = opaque; + + switch (offset) { + case FDIAGCTRL: + return s->fdiagctrl; + case FRAW_ADDR: + return s->fraw_addr; + case FPRIM_ADD_TAG: + return s->fprim_add_tag; + case FDUP_ADD_TAG: + return s->fdup_add_tag; + case FEDAC_PASTATUS: + return s->fedac_pastatus; + case FEDAC_PBSTATUS: + return s->fedac_pbstatus; + default: + qemu_log_bad_offset(offset); + } + + return 0; +} + +static void hercules_l2fmc_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesL2FMCState *s = opaque; + const uint32_t val = val64; + + switch (offset) { + case FDIAGCTRL: + s->fdiagctrl = val; + + if (s->fdiagctrl & DIAG_TRIG && + DIAG_EN_KEY(s->fdiagctrl) == 0x5) { + uint32_t err_bit; + qemu_irq error; + + if (DIAGMODE(s->fdiagctrl) == DIAGMODE_ADDR) { + err_bit = ADD_TAG_ERR; + error = s->uncorrectable_error; + } else { + err_bit = ADD_PAR_ERR; + /* + * FIXME, we should calculate those value against + * reading all zeros or all Fs + */ + if (s->femu_ecc == 0xCE) { + error = s->correctable_error; + s->camavailstat |= BIT(0); + s->cam_content[0] = s->ecc_1bit_address; + } else { + error = s->bus_error; + } + } + + switch (DIAG_BUF_SEL(s->fdiagctrl)) { + case 2: + case 3: + return; + case 0: + case 1: + s->fedac_pastatus |= err_bit; + break; + case 4: + case 5: + case 6: + case 7: + s->fedac_pbstatus |= err_bit; + break; + } + + s->fdiagctrl &= ~DIAG_TRIG; + qemu_irq_raise(error); + } + break; + case FRAW_ADDR: + s->fraw_addr = val; + break; + case FPRIM_ADD_TAG: + s->fprim_add_tag = val; + break; + case FDUP_ADD_TAG: + s->fdup_add_tag = val; + break; + case FEDAC_PASTATUS: + s->fedac_pastatus &= ~val; + break; + case FEDAC_PBSTATUS: + s->fedac_pbstatus &= ~val; + break; + case FEMU_ECC: + s->femu_ecc = val; + break; + case FEMU_DMSW: + case FEMU_DLSW: + break; + default: + qemu_log_bad_offset(offset); + } +} + +static const MemoryRegionOps hercules_l2fmc_ops = { + .read = hercules_l2fmc_read, + .write = hercules_l2fmc_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_l2fmc_realize(DeviceState *dev, Error **errp) +{ + HerculesL2FMCState *s = HERCULES_L2FMC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_l2fmc_ops, + s, TYPE_HERCULES_L2FMC ".io", HERCULES_L2FMC_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + sysbus_init_irq(sbd, &s->uncorrectable_error); + sysbus_init_irq(sbd, &s->bus_error); + sysbus_init_irq(sbd, &s->correctable_error); + + /* + * Technically EPC is a separate IP block, but our only use-case + * for it involves flash controller so dealing with it here + * simplifies things + */ + memory_region_init_io(&s->epc, OBJECT(dev), &hercules_epc_ops, + s, TYPE_HERCULES_L2FMC ".epc", HERCULES_EPC_SIZE); + sysbus_init_mmio(sbd, &s->epc); + + s->ecc_1bit_address = 0x00000008; + s->ecc_1bit_femu_ecc = 0xCE; +} + +static void hercules_l2fmc_reset(DeviceState *d) +{ + HerculesL2FMCState *s = HERCULES_L2FMC(d); + + s->fdiagctrl = 0; + s->fraw_addr = 0; + s->fprim_add_tag = 0; + s->fdup_add_tag = 0; + s->fedac_pastatus = 0; + s->fedac_pbstatus = 0; + s->femu_ecc = 0; +} + +static void hercules_l2fmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_l2fmc_reset; + dc->realize = hercules_l2fmc_realize; +} + +static const TypeInfo hercules_l2fmc_info = { + .name = TYPE_HERCULES_L2FMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesL2FMCState), + .class_init = hercules_l2fmc_class_init, +}; + +static void hercules_l2fmc_register_types(void) +{ + type_register_static(&hercules_l2fmc_info); +} +type_init(hercules_l2fmc_register_types) diff --git a/include/hw/misc/hercules_l2fmc.h b/include/hw/misc/hercules_l2fmc.h new file mode 100644 index 0000000000000..645b346ed729e --- /dev/null +++ b/include/hw/misc/hercules_l2fmc.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_L2FMC_H +#define HERCULES_L2FMC_H + +typedef struct HerculesL2FMCState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion epc; + + uint32_t fdiagctrl; + uint32_t fraw_addr; + uint32_t fprim_add_tag; + uint32_t fdup_add_tag; + uint32_t fedac_pastatus; + uint32_t fedac_pbstatus; + uint32_t femu_ecc; + + uint32_t camavailstat; + uint32_t cam_index[7]; + uint32_t cam_content[32]; + + uint32_t ecc_1bit_address; + uint32_t ecc_1bit_femu_ecc; + + qemu_irq uncorrectable_error; + qemu_irq bus_error; + qemu_irq correctable_error; +} HerculesL2FMCState; + +#define TYPE_HERCULES_L2FMC "ti-hercules-l2fmc" +#define HERCULES_L2FMC(obj) OBJECT_CHECK(HerculesL2FMCState, (obj), \ + TYPE_HERCULES_L2FMC) +#endif From 71e7a78f77ac857d2ede77ff1b6863fa4fb22462 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 3 Jul 2019 16:41:58 -0700 Subject: [PATCH 23/27] hw/misc: Add emulation for Hercules ECAP IP block Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- hw/misc/Makefile.objs | 3 +- hw/misc/hercules_ecap.c | 163 ++++++++++++++++++++++++++++++++ include/hw/misc/hercules_ecap.h | 30 ++++++ 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 hw/misc/hercules_ecap.c create mode 100644 include/hw/misc/hercules_ecap.h diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 2f0f3dd441777..3bafdbf921337 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -90,7 +90,8 @@ common-obj-$(CONFIG_HERCULES) += hercules_system.o hercules_scm.o \ hercules_esm.o hercules_efuse.o \ hercules_l2ramw.o hercules_pmm.o \ hercules_stc.o hercules_pbist.o \ - hercules_ccm.o hercules_l2fmc.o + hercules_ccm.o hercules_l2fmc.o \ + hercules_ecap.o obj-$(CONFIG_MAC_VIA) += mac_via.o common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o diff --git a/hw/misc/hercules_ecap.c b/hw/misc/hercules_ecap.c new file mode 100644 index 0000000000000..f1855d4145e9d --- /dev/null +++ b/hw/misc/hercules_ecap.c @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "hw/core/cpu.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" + +#include "hw/misc/hercules_ecap.h" + +enum { + HERCULES_ECAP_SIZE = 256, +}; + +#define qemu_log_bad_offset(offset) \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ + __func__, offset); + +enum HerculesECAPRegisters { + TSCTR = 0x00, + CTRPHS = 0x04, + CAP1 = 0x08, + CAP2 = 0x0C, + CAP3 = 0x10, + CAP4 = 0x14, + + ECCTL2 = 0x28, + ECCTL1 = 0x2A, + ECFLG = 0x2C, + ECEINT = 0x2E, + ECFRC = 0x30, + ECCLR = 0x32, +}; + +static uint64_t hercules_ecap_read(void *opaque, hwaddr offset, + unsigned size) +{ + HerculesECAPState *s = opaque; + + switch (size) { + case sizeof(uint16_t): + switch (offset) { + case ECCTL1: + case ECCTL2: + case ECEINT: + case ECFRC: + case ECCLR: + return 0; + case ECFLG: + return s->ecflg; + } + break; + case sizeof(uint32_t): + switch (offset) { + case TSCTR: + return 0; + case CAP1 ... CAP4: + return s->cap[(offset - CAP1) / sizeof(uint32_t)]; + } + break; + } + + qemu_log_bad_offset(offset); + return 0; +} + +static void hercules_ecap_write(void *opaque, hwaddr offset, + uint64_t val64, unsigned size) +{ + HerculesECAPState *s = opaque; + const uint32_t val = val64; + + switch (size) { + case sizeof(uint16_t): + switch (offset) { + case ECCTL1: + case ECCTL2: + case ECEINT: + case ECFRC: + break; + case ECCLR: + /* + * Currently a no-op on purpose. Once a given capture + * register and corresponding bit in ECFLG is set by an + * external entity, we want it to remain set in order to + * make guest think that we are constantly capturing a + * waveform of given frequency + */ + break; + } + break; + case sizeof(uint32_t): + switch (offset) { + case TSCTR: + break; + case CAP1 ... CAP4: + s->cap[(offset - CAP1) / sizeof(uint32_t)] = val; + break; + default: + qemu_log_bad_offset(offset); + } + break; + + } +} + +static const MemoryRegionOps hercules_ecap_ops = { + .read = hercules_ecap_read, + .write = hercules_ecap_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void hercules_ecap_realize(DeviceState *dev, Error **errp) +{ + HerculesECAPState *s = HERCULES_ECAP(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_ecap_ops, + s, TYPE_HERCULES_ECAP ".io", HERCULES_ECAP_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void hercules_ecap_reset(DeviceState *d) +{ + HerculesECAPState *s = HERCULES_ECAP(d); + + memset(s->cap, 0, sizeof(s->cap)); + s->ecflg = 0; +} + +static void hercules_ecap_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = hercules_ecap_reset; + dc->realize = hercules_ecap_realize; +} + +static const TypeInfo hercules_ecap_info = { + .name = TYPE_HERCULES_ECAP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HerculesECAPState), + .class_init = hercules_ecap_class_init, +}; + +static void hercules_ecap_register_types(void) +{ + type_register_static(&hercules_ecap_info); +} +type_init(hercules_ecap_register_types) diff --git a/include/hw/misc/hercules_ecap.h b/include/hw/misc/hercules_ecap.h new file mode 100644 index 0000000000000..5bfdded5b38e8 --- /dev/null +++ b/include/hw/misc/hercules_ecap.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_ECAP_H +#define HERCULES_ECAP_H + +#include "hercules_system.h" + +enum { + HERCULES_ECAP_NUM_CAPS = 4, +}; + +typedef struct HerculesECAPState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t cap[HERCULES_ECAP_NUM_CAPS]; + uint16_t ecflg; + +} HerculesECAPState; + +#define TYPE_HERCULES_ECAP "ti-hercules-ecap" +#define HERCULES_ECAP(obj) OBJECT_CHECK(HerculesECAPState, (obj), \ + TYPE_HERCULES_ECAP) +#endif From 01eecce27ada7bcb8fcf67b7eab6a46597fa0bfe Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 28 Apr 2019 00:59:08 -0700 Subject: [PATCH 24/27] hw/arm: Add support for Hercules SoC Signed-off-by: Andrey Smirnov Signed-off-by: Benjamin Kamath --- default-configs/arm-softmmu.mak | 1 + hw/arm/Kconfig | 3 + hw/arm/Makefile.objs | 1 + hw/arm/hercules.c | 652 ++++++++++++++++++++++++++++++++ hw/arm/tms570lcxxxx.c | 69 ++++ include/hw/arm/hercules.h | 215 +++++++++++ 6 files changed, 941 insertions(+) create mode 100644 hw/arm/hercules.c create mode 100644 hw/arm/tms570lcxxxx.c create mode 100644 include/hw/arm/hercules.h diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 8fc09a4a51037..9a31947b2f6e5 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -19,6 +19,7 @@ CONFIG_SX1=y CONFIG_NSERIES=y CONFIG_STELLARIS=y CONFIG_REALVIEW=y +CONFIG_HERCULES=y CONFIG_VERSATILE=y CONFIG_VEXPRESS=y CONFIG_ZYNQ=y diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 9afa6eee7991c..9ee1835416908 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -456,6 +456,9 @@ config MSF2 select SSI select UNIMP +config HERCULES + bool + config ZAURUS bool select NAND diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 534a6a119e5d7..49625a2373444 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -54,3 +54,4 @@ obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o obj-$(CONFIG_FSL_IMX6UL) += fsl-imx6ul.o mcimx6ul-evk.o obj-$(CONFIG_NRF51_SOC) += nrf51_soc.o +obj-$(CONFIG_HERCULES) += hercules.o tms570lcxxxx.o diff --git a/hw/arm/hercules.c b/hw/arm/hercules.c new file mode 100644 index 0000000000000..5c5ea5a2fcdd0 --- /dev/null +++ b/hw/arm/hercules.c @@ -0,0 +1,652 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "qemu/error-report.h" +#include "hw/arm/boot.h" +#include "hw/arm/hercules.h" +#include "hw/boards.h" +#include "hw/sysbus.h" +#include "net/net.h" +#include "exec/address-spaces.h" +#include "chardev/char-fe.h" +#include "sysemu/reset.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" +#include "hw/loader.h" +#include "sysemu/block-backend.h" + +typedef struct QEMU_PACKED HerculesOTPSensorCalibrationData { + uint16_t temp1val; + uint16_t temp1; + uint16_t temp2val; + uint16_t temp2; + uint16_t temp3val; + uint16_t temp3; + uint32_t reserved; +} HerculesOTPSensorCalibrationData; + +typedef struct QEMU_PACKED HerculesOTPData { + uint32_t bank0_sector_info; + uint32_t bank0_package_memory_info; + uint32_t __reserved1[21]; + uint32_t lpo_trim_max_gclk; + uint32_t __reserved2[10]; + uint8_t part_number[32]; + uint32_t __reserved3[68]; + HerculesOTPSensorCalibrationData die_temp_sensor[3]; + uint32_t __reserved4[44]; + uint32_t single_bit_ecc[2]; + uint32_t double_bit_ecc[2]; +} HerculesOTPData; + +static const int +HERCULES_MIBSPIn_DMAREQ[HERCULES_NUM_MIBSPIS][HERCULES_SPI_NUM_DMAREQS] = { + { + [0] = 1, [1] = 0, [2] = 4, [3] = 5, + [4] = 8, [5] = 9, [6] = 12, [7] = 13, + [8] = 16, [9] = 17, [10] = 22, [11] = 23, + [12] = 26, [13] = 27, [14] = 30, [15] = 31, + }, + { + [0] = 3, [1] = 2, [2] = 32, [3] = 33, + [4] = 34, [5] = 35, [6] = 36, [7] = 37, + [8] = 38, [9] = 39, [10] = 40, [11] = 41, + [12] = 42, [13] = 43, [14] = 44, [15] = 45, + }, + { + [0] = 15, [1] = 14, [2] = 4, [3] = 5, + [4] = 8, [5] = 9, [6] = 12, [7] = 13, + [8] = 16, [9] = 17, [10] = 22, [11] = 23, + [12] = 26, [13] = 27, [14] = 30, [15] = 31, + }, + { + [0] = 25, [1] = 24, [2] = 32, [3] = 33, + [4] = 34, [5] = 35, [6] = 36, [7] = 37, + [8] = 38, [9] = 39, [10] = 40, [11] = 41, + [12] = 42, [13] = 43, [14] = 44, [15] = 45, + }, + { + [0] = 31, [1] = 30, [2] = 6, [3] = 7, + [4] = 10, [5] = 11, [6] = 14, [7] = 15, + [8] = 18, [9] = 19, [10] = 22, [11] = 23, + [12] = 24, [13] = 25, [14] = 28, [15] = 29, + }, +}; + +static void hercules_initfn(Object *obj) +{ + HerculesState *s = HERCULES_SOC(obj); + Object *cpu_obj = OBJECT(&s->cpu); + int i; + + object_initialize(cpu_obj, sizeof(s->cpu), + ARM_CPU_TYPE_NAME("cortex-r5f")); + + object_property_add_child(obj, "cpu", cpu_obj); + + s->cpu.ctr = 0x1d192992; /* 32K icache 32K dcache */ + + set_feature(&s->cpu.env, ARM_FEATURE_DUMMY_C15_REGS); + + object_property_set_bool(cpu_obj, true, "cfgend", &error_fatal); + object_property_set_bool(cpu_obj, true, "cfgend-instr", &error_fatal); + + sysbus_init_child_obj(obj, "l2ramw", &s->l2ramw, sizeof(s->l2ramw), + TYPE_HERCULES_L2RAMW); + + sysbus_init_child_obj(obj, "rtp", &s->rtp, sizeof(s->rtp), + TYPE_HERCULES_RTP); + + sysbus_init_child_obj(obj, "vim", &s->vim, sizeof(s->vim), + TYPE_HERCULES_VIM); + + sysbus_init_child_obj(obj, "system", &s->system, sizeof(s->system), + TYPE_HERCULES_SYSTEM); + + sysbus_init_child_obj(obj, "gio", &s->gio, sizeof(s->gio), + TYPE_HERCULES_GIO); + + for (i = 0; i < HERCULES_NUM_N2HETS; i++) { + sysbus_init_child_obj(obj, "n2het[*]", &s->n2het[i], + sizeof(s->n2het[i]), TYPE_HERCULES_N2HET); + } + s->n2het[0].gpio.bank = 2; + s->n2het[1].gpio.bank = 3; + + for (i = 0; i < HERCULES_NUM_MIBADCS; i++) { + sysbus_init_child_obj(obj, "mibadc[*]", &s->mibadc[i], + sizeof(s->mibadc[i]), TYPE_HERCULES_MIBADC); + } + + sysbus_init_child_obj(obj, "rti", &s->rti, sizeof(s->rti), + TYPE_HERCULES_RTI); + + sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac), + TYPE_HERCULES_EMAC); + + sysbus_init_child_obj(obj, "dma", &s->dma, sizeof(s->dma), + TYPE_HERCULES_DMA); + + for (i = 0; i < HERCULES_NUM_MIBSPIS; i++) { + sysbus_init_child_obj(obj, "mibspi[*]", &s->mibspi[i], + sizeof(s->mibspi), TYPE_HERCULES_SPI); + } + + sysbus_init_child_obj(obj, "scm", &s->scm, sizeof(s->scm), + TYPE_HERCULES_SCM); + sysbus_init_child_obj(obj, "esm", &s->esm, sizeof(s->esm), + TYPE_HERCULES_ESM); + sysbus_init_child_obj(obj, "efuse", &s->efuse, sizeof(s->efuse), + TYPE_HERCULES_EFUSE); + sysbus_init_child_obj(obj, "pmm", &s->pmm, sizeof(s->pmm), + TYPE_HERCULES_PMM); + + for (i = 0; i < HERCULES_NUM_ECAPS; i++) { + sysbus_init_child_obj(obj, "ecap[*]", &s->ecap[i], + sizeof(s->ecap), TYPE_HERCULES_ECAP); + } + + sysbus_init_child_obj(obj, "stc", &s->stc, sizeof(s->stc), + TYPE_HERCULES_STC); + sysbus_init_child_obj(obj, "pbist", &s->pbist, sizeof(s->pbist), + TYPE_HERCULES_PBIST); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), + TYPE_HERCULES_CCM); + sysbus_init_child_obj(obj, "l2fmc", &s->l2fmc, sizeof(s->l2fmc), + TYPE_HERCULES_L2FMC); +} + +static void hercules_cpu_reset(void *opaque) +{ + ARMCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + +static void hercules_realize(DeviceState *dev, Error **errp) +{ + HerculesState *s = HERCULES_SOC(dev); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *flash = g_new(MemoryRegion, 1); + MemoryRegion *eeprom = g_new(MemoryRegion, 1); + MemoryRegion *otp_bank1 = g_new(MemoryRegion, 1); + uint8_t *otp_bank1_data; + SysBusDevice *sbd; + DeviceState *vim, *dma, *esm; + qemu_irq irq, error; + int i; + + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &error_fatal); + qemu_register_reset(hercules_cpu_reset, ARM_CPU(&s->cpu)); + + memory_region_init_rom(flash, OBJECT(dev), "hercules.flash", + HERCULES_FLASH_SIZE, &error_fatal); + memory_region_add_subregion(system_memory, HERCULES_FLASH_ADDR, flash); + + memory_region_init_rom(eeprom, OBJECT(dev), "hercules.eeprom", + HERCULES_EEPROM_SIZE, &error_fatal); + memory_region_add_subregion(system_memory, HERCULES_EEPROM_ADDR, eeprom); + + if (s->blk_eeprom) { + int64_t size; + + size = MIN(blk_getlength(s->blk_eeprom), HERCULES_EEPROM_SIZE); + if (size <= 0) { + error_setg(errp, "failed to get flash size"); + return; + } + + if (blk_pread(s->blk_eeprom, 0, memory_region_get_ram_ptr(eeprom), + size) < 0) { + error_setg(errp, "failed to read EEPROM content"); + return; + } + } + + qdev_prop_set_chr(DEVICE(&s->rtp), "chardev", serial_hd(0)); + object_property_set_bool(OBJECT(&s->rtp), true, "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtp), 0, HERCULES_RTP_ADDR); + + /* + * ARM Debug peripherals + */ + create_unimplemented_device("debug-rom", HERCULES_DEBUG_ROM_ADDR, + HERCULES_DEBUG_SIZE); + create_unimplemented_device("debug", HERCULES_DEBUG_ADDR, + HERCULES_DEBUG_SIZE); + create_unimplemented_device("etm", HERCULES_ETM_ADDR, + HERCULES_DEBUG_SIZE); + create_unimplemented_device("tpiu", HERCULES_TPIU_ADDR , + HERCULES_DEBUG_SIZE); + create_unimplemented_device("pom", HERCULES_POM_ADDR, + HERCULES_DEBUG_SIZE); + create_unimplemented_device("cti1", HERCULES_CTI1_ADDR, + HERCULES_DEBUG_SIZE); + create_unimplemented_device("cti2", HERCULES_CTI2_ADDR, + HERCULES_DEBUG_SIZE); + create_unimplemented_device("cti3", HERCULES_CTI3_ADDR, + HERCULES_DEBUG_SIZE); + create_unimplemented_device("cti4", HERCULES_CTI4_ADDR, + HERCULES_DEBUG_SIZE); + create_unimplemented_device("ctsf", HERCULES_CTSF_ADDR, + HERCULES_DEBUG_SIZE); + + /* + * VIM + */ + object_property_set_bool(OBJECT(&s->vim), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->vim); + irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ); + sysbus_connect_irq(sbd, 0, irq); + irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ); + sysbus_connect_irq(sbd, 1, irq); + sysbus_mmio_map(sbd, 0, HERCULES_VIM_ECC_ADDR); + sysbus_mmio_map(sbd, 1, HERCULES_VIM_CONTROL_ADDR); + sysbus_mmio_map(sbd, 2, HERCULES_VIM_RAM_ADDR); + + vim = DEVICE(&s->vim); + + object_property_set_bool(OBJECT(&s->esm), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->esm); + sysbus_mmio_map(sbd, 0, HERCULES_ESM_ADDR); + irq = qdev_get_gpio_in(vim, HERCULES_ESM_HIGH_LEVEL_IRQ); + sysbus_connect_irq(sbd, 0, irq); + irq = qdev_get_gpio_in(vim, HERCULES_ESM_LOW_LEVEL_IRQ); + sysbus_connect_irq(sbd, 1, irq); + + esm = DEVICE(&s->esm); + + object_property_set_bool(OBJECT(&s->l2ramw), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->l2ramw); + sysbus_mmio_map(sbd, 0, HERCULES_RAM_ADDR); + sysbus_mmio_map(sbd, 1, HERCULES_L2RAMW_ADDR); + error = qdev_get_gpio_in(esm, HERCULES_L2RAMW_TYPE_B_UNCORRECTABLE_ERROR); + sysbus_connect_irq(sbd, 0, error); + + object_property_set_bool(OBJECT(&s->system), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->system); + sysbus_mmio_map(sbd, 0, HERCULES_SYS_ADDR); + sysbus_mmio_map(sbd, 1, HERCULES_SYS2_ADDR); + sysbus_mmio_map(sbd, 2, HERCULES_PCR_ADDR); + sysbus_mmio_map(sbd, 3, HERCULES_PCR2_ADDR); + sysbus_mmio_map(sbd, 4, HERCULES_PCR3_ADDR); + sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(vim, HERCULES_SSI_IRQ)); + error = qdev_get_gpio_in(esm, HERCULES_PLL1_SLIP_ERROR); + sysbus_connect_irq(sbd, 1, error); + error = qdev_get_gpio_in(esm, HERCULES_PLL2_SLIP_ERROR); + sysbus_connect_irq(sbd, 2, error); + + create_unimplemented_device("pinmux", HERCULES_PINMUX_ADDR, + HERCULES_PINMUX_SIZE); + + object_property_set_bool(OBJECT(&s->l2fmc), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->l2fmc); + sysbus_mmio_map(sbd, 0, HERCULES_L2FMC_ADDR); + sysbus_mmio_map(sbd, 1, HERCULES_EPC_ADDR); + error = qdev_get_gpio_in(esm, HERCULES_L2FMC_UNCORRECTABLE_ERROR); + sysbus_connect_irq(sbd, 0, error); + error = qdev_get_gpio_in(esm, HERCULES_CR5F_FATAL_BUS_ERROR); + sysbus_connect_irq(sbd, 1, error); + error = qdev_get_gpio_in(esm, HERCULES_EPC_CORRECTABLE_ERROR); + sysbus_connect_irq(sbd, 2, error); + + otp_bank1_data = g_new0(uint8_t, HERCULES_OTP_BANK1_SIZE); + if (0) { + /* + * Handle loading OTP1 from a custom file here + */ + } else { + /* + * FIXME: Convenience workaround, should eventually be removed + * once loading from custom file is implemented + */ + const HerculesOTPData dummy_otp = { + .bank0_sector_info = htobe32(0x00108303), + .bank0_package_memory_info = htobe32(0x01511000U), + + .lpo_trim_max_gclk = htobe32(0x0F201611), + + .part_number = { + 0x52, 0x4D, 0x35, 0x37, + 0x4c, 0x38, 0x34, 0x33, + 0x42, 0x5a, 0x57, 0x54, + 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }, + + .die_temp_sensor = { + [0] = { + .temp1 = htobe16(0x012F), + .temp1val = htobe16(0x0762), + .temp2 = htobe16(0x00E9), + .temp2val = htobe16(0x05A5), + .temp3 = htobe16(0x018E), + .temp3val = htobe16(0x09A6), + .reserved = 0xFFFFFFFF, + }, + [1] = { + .temp1 = htobe16(0x012F), + .temp1val = htobe16(0x07A8), + .temp2 = htobe16(0x00E9), + .temp2val = htobe16(0x0613U), + .temp3 = htobe16(0x018E), + .temp3val = htobe16(0x09D4), + .reserved = 0xFFFFFFFFUL, + }, + [2] = { + .temp1 = htobe16(0x012F), + .temp1val = htobe16(0x0750), + .temp2 = htobe16(0x00E9), + .temp2val = htobe16(0x058E), + .temp3 = htobe16(0x018E), + .temp3val = htobe16(0x099D), + .reserved = 0xFFFFFFFFUL, + }, + }, + + .single_bit_ecc = { + htobe32(0x12345678), + htobe32(0x9ABCDEF0) + }, + .double_bit_ecc = { + htobe32(0x123C5478), + htobe32(0x9ABCDEF2) + }, + }; + + memcpy(otp_bank1_data + 0x158, &dummy_otp, sizeof(dummy_otp)); + } + + memory_region_init_ram_ptr(otp_bank1, OBJECT(dev), + TYPE_HERCULES_SOC ".otp.bank1", + HERCULES_OTP_BANK1_SIZE, + otp_bank1_data); + memory_region_add_subregion(system_memory, + HERCULES_OTP_BANK1_ADDR, otp_bank1); + + create_unimplemented_device("emif", HERCULES_EMIF_ADDR, + HERCULES_EMIF_SIZE); + + + object_property_set_bool(OBJECT(&s->gio), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->gio); + sysbus_mmio_map(sbd, 0, HERCULES_GIO_ADDR); + + for (i = 0; i < HERCULES_NUM_N2HETS; i++) { + static const hwaddr HERCULES_N2HETn_ADDR[HERCULES_NUM_N2HETS] = { + HERCULES_N2HET1_ADDR, + HERCULES_N2HET2_ADDR, + }; + + static const hwaddr HERCULES_N2HETn_RAM_ADDR[HERCULES_NUM_N2HETS] = { + HERCULES_N2HET1_RAM_ADDR, + HERCULES_N2HET2_RAM_ADDR, + }; + + object_property_set_bool(OBJECT(&s->n2het[i]), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->n2het[i]); + sysbus_mmio_map(sbd, 0, HERCULES_N2HETn_ADDR[i]); + sysbus_mmio_map(sbd, 1, HERCULES_N2HETn_RAM_ADDR[i]); + } + + create_unimplemented_device("lin1", HERCULES_LIN1_ADDR, + HERCULES_LIN1_SIZE); + + for (i = 0; i < HERCULES_NUM_MIBADCS; i++) { + static const hwaddr HERCULES_MIBADCn_ADDR[HERCULES_NUM_MIBADCS] = { + HERCULES_MIBADC1_ADDR, + HERCULES_MIBADC2_ADDR, + }; + static const hwaddr HERCULES_MIBADCn_RAM_ADDR[HERCULES_NUM_MIBADCS] = { + HERCULES_MIBADC1_RAM_ADDR, + HERCULES_MIBADC2_RAM_ADDR, + }; + static const int + HERCULES_MIBADCn_PARITY_ERRROR[HERCULES_NUM_MIBADCS] = { + HERCULES_MIBADC1_PARITY_ERROR, + HERCULES_MIBADC2_PARITY_ERROR, + }; + + object_property_set_bool(OBJECT(&s->mibadc[i]), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->mibadc[i]); + sysbus_mmio_map(sbd, 0, HERCULES_MIBADCn_ADDR[i]); + sysbus_mmio_map(sbd, 1, HERCULES_MIBADCn_RAM_ADDR[i]); + error = qdev_get_gpio_in(esm, HERCULES_MIBADCn_PARITY_ERRROR[i]); + sysbus_connect_irq(sbd, 0, error); + } + + /* + * RTI + */ + object_property_set_bool(OBJECT(&s->rti), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->rti); + + irq = qdev_get_gpio_in(vim, HERCULES_RTI_COMPARE0_IRQ); + sysbus_connect_irq(sbd, HERCULES_RTI_INT_COMPARE0, irq); + irq = qdev_get_gpio_in(vim, HERCULES_RTI_COMPARE1_IRQ); + sysbus_connect_irq(sbd, HERCULES_RTI_INT_COMPARE1, irq); + irq = qdev_get_gpio_in(vim, HERCULES_RTI_COMPARE2_IRQ); + sysbus_connect_irq(sbd, HERCULES_RTI_INT_COMPARE2, irq); + irq = qdev_get_gpio_in(vim, HERCULES_RTI_COMPARE3_IRQ); + sysbus_connect_irq(sbd, HERCULES_RTI_INT_COMPARE3, irq); + + sysbus_mmio_map(sbd, 0, HERCULES_RTI_ADDR); + + qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); + object_property_set_bool(OBJECT(&s->emac), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->emac); + sysbus_mmio_map(sbd, 0, HERCULES_EMAC_MODULE_ADDR); + sysbus_mmio_map(sbd, 1, HERCULES_EMAC_CTRL_ADDR); + sysbus_mmio_map(sbd, 2, HERCULES_EMAC_MDIO_ADDR); + sysbus_mmio_map(sbd, 3, HERCULES_EMAC_CPPI_ADDR); + + create_unimplemented_device("nmpu", HERCULES_NMPU_ADDR, + HERCULES_NMPU_SIZE); + + create_unimplemented_device("dcc1", HERCULES_DCC1_ADDR, + HERCULES_DCC1_SIZE); + + object_property_set_bool(OBJECT(&s->dma), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->dma); + sysbus_mmio_map(sbd, 0, HERCULES_DMA_ADDR); + sysbus_mmio_map(sbd, 1, HERCULES_DMA_RAM_ADDR); + + dma = DEVICE(&s->dma); + + for (i = 0; i < HERCULES_NUM_MIBSPIS; i++) { + static const hwaddr + HERCULES_MIBSPIn_RAM_ADDR[HERCULES_NUM_MIBSPIS] = { + HERCULES_MIBSPI1_RAM_ADDR, + HERCULES_MIBSPI2_RAM_ADDR, + HERCULES_MIBSPI3_RAM_ADDR, + HERCULES_MIBSPI4_RAM_ADDR, + HERCULES_MIBSPI5_RAM_ADDR, + }; + static const hwaddr + HERCULES_MIBSPIn_CTRL_ADDR[HERCULES_NUM_MIBSPIS] = { + HERCULES_MIBSPI1_CTRL_ADDR, + HERCULES_MIBSPI2_CTRL_ADDR, + HERCULES_MIBSPI3_CTRL_ADDR, + HERCULES_MIBSPI4_CTRL_ADDR, + HERCULES_MIBSPI5_CTRL_ADDR, + }; + static const int HERCULES_MIBSPIn_L0_IRQ[HERCULES_NUM_MIBSPIS] = { + HERCULES_MIBSPI1_L0_IRQ, + HERCULES_MIBSPI2_L0_IRQ, + HERCULES_MIBSPI3_L0_IRQ, + HERCULES_MIBSPI4_L0_IRQ, + HERCULES_MIBSPI5_L0_IRQ, + }; + static const int HERCULES_MIBSPIn_L1_IRQ[HERCULES_NUM_MIBSPIS] = { + HERCULES_MIBSPI1_L1_IRQ, + HERCULES_MIBSPI2_L1_IRQ, + HERCULES_MIBSPI3_L1_IRQ, + HERCULES_MIBSPI4_L1_IRQ, + HERCULES_MIBSPI5_L1_IRQ, + }; + static const int + HERCULES_MIBSPIn_SINGLE_BIT_ERROR[HERCULES_NUM_MIBSPIS] = { + HERCULES_MIBSPI1_SINGLE_BIT_ERROR, + HERCULES_MIBSPI2_SINGLE_BIT_ERROR, + HERCULES_MIBSPI3_SINGLE_BIT_ERROR, + HERCULES_MIBSPI4_SINGLE_BIT_ERROR, + HERCULES_MIBSPI5_SINGLE_BIT_ERROR, + }; + static const int + HERCULES_MIBSPIn_UNCORRECTABLE_ERROR[HERCULES_NUM_MIBSPIS] = { + HERCULES_MIBSPI1_UNCORRECTABLE_ERROR, + HERCULES_MIBSPI2_UNCORRECTABLE_ERROR, + HERCULES_MIBSPI3_UNCORRECTABLE_ERROR, + HERCULES_MIBSPI4_UNCORRECTABLE_ERROR, + HERCULES_MIBSPI5_UNCORRECTABLE_ERROR, + }; + int irq_nr = 0; + int j; + + object_property_set_bool(OBJECT(&s->mibspi[i]), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->mibspi[i]); + sysbus_mmio_map(sbd, 0, HERCULES_MIBSPIn_CTRL_ADDR[i]); + sysbus_mmio_map(sbd, 1, HERCULES_MIBSPIn_RAM_ADDR[i]); + irq = qdev_get_gpio_in(vim, HERCULES_MIBSPIn_L0_IRQ[i]); + sysbus_connect_irq(sbd, irq_nr++, irq); + irq = qdev_get_gpio_in(vim, HERCULES_MIBSPIn_L1_IRQ[i]); + sysbus_connect_irq(sbd, irq_nr++, irq); + + irq_nr += HERCULES_SPI_NUM_CS_LINES; + + for (j = 0; j < HERCULES_SPI_NUM_DMAREQS; j++) { + irq = qdev_get_gpio_in(dma, HERCULES_MIBSPIn_DMAREQ[i][j]); + sysbus_connect_irq(sbd, irq_nr++, irq); + } + + error = qdev_get_gpio_in(esm, HERCULES_MIBSPIn_SINGLE_BIT_ERROR[i]); + sysbus_connect_irq(sbd, irq_nr++, error); + error = qdev_get_gpio_in(esm, HERCULES_MIBSPIn_UNCORRECTABLE_ERROR[i]); + sysbus_connect_irq(sbd, irq_nr++, error); + } + + object_property_set_bool(OBJECT(&s->scm), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->scm); + sysbus_mmio_map(sbd, 0, HERCULES_SCM_ADDR); + sysbus_mmio_map(sbd, 1, HERCULES_SDR_MMR_ADDR); + irq = qdev_get_gpio_in(DEVICE(&s->system), HERCULES_SYSTEM_ICRST); + sysbus_connect_irq(sbd, 0, irq); + + object_property_set_bool(OBJECT(&s->efuse), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->efuse); + sysbus_mmio_map(sbd, 0, HERCULES_EFUSE_ADDR); + error = qdev_get_gpio_in(esm, HERCULES_EFUSE_AUTOLOAD_ERROR); + sysbus_connect_irq(sbd, 0, error); + error = qdev_get_gpio_in(esm, HERCULES_EFUSE_SELF_TEST_ERROR); + sysbus_connect_irq(sbd, 1, error); + error = qdev_get_gpio_in(esm, HERCULES_EFUSE_SINGLE_BIT_ERROR); + sysbus_connect_irq(sbd, 2, error); + + object_property_set_bool(OBJECT(&s->pmm), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->pmm); + sysbus_mmio_map(sbd, 0, HERCULES_PMM_ADDR); + error = qdev_get_gpio_in(esm, HERCULES_PMM_COMPARE_ERROR); + sysbus_connect_irq(sbd, 0, error); + error = qdev_get_gpio_in(esm, HERCULES_PMM_SELF_TEST_ERROR); + sysbus_connect_irq(sbd, 1, error); + + for (i = 0; i < HERCULES_NUM_ECAPS; i++) { + static const hwaddr HERCULES_ECAPn_ADDR[HERCULES_NUM_ECAPS] = { + HERCULES_ECAP1_ADDR, + HERCULES_ECAP2_ADDR, + HERCULES_ECAP3_ADDR, + HERCULES_ECAP4_ADDR, + HERCULES_ECAP5_ADDR, + HERCULES_ECAP6_ADDR, + }; + + object_property_set_bool(OBJECT(&s->ecap[i]), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->ecap[i]); + sysbus_mmio_map(sbd, 0, HERCULES_ECAPn_ADDR[i]); + } + + object_property_set_bool(OBJECT(&s->stc), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->stc); + sysbus_mmio_map(sbd, 0, HERCULES_STC1_ADDR); + irq = qdev_get_gpio_in(DEVICE(&s->system), HERCULES_SYSTEM_CPURST); + sysbus_connect_irq(sbd, 0, irq); + + object_property_set_bool(OBJECT(&s->pbist), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->pbist); + sysbus_mmio_map(sbd, 0, HERCULES_PBIST_ADDR); + irq = qdev_get_gpio_in(DEVICE(&s->system), HERCULES_SYSTEM_MSTDONE); + sysbus_connect_irq(sbd, 0, irq); + + object_property_set_bool(OBJECT(&s->ccm), true, "realized", + &error_abort); + sbd = SYS_BUS_DEVICE(&s->ccm); + sysbus_mmio_map(sbd, 0, HERCULES_CCM_ADDR); + error = qdev_get_gpio_in(esm, HERCULES_CCMR5F_CPU_COMPARE_ERROR); + sysbus_connect_irq(sbd, 0, error); + error = qdev_get_gpio_in(esm, HERCULES_CCMR5F_VIM_COMPARE_ERROR); + sysbus_connect_irq(sbd, 1, error); + error = qdev_get_gpio_in(esm, HERCULES_CPU1_AXIM_BUS_MONITOR_ERROR); + sysbus_connect_irq(sbd, 2, error); + error = qdev_get_gpio_in(esm, HERCULES_CCMR5F_SELF_TEST_ERROR); + sysbus_connect_irq(sbd, 3, error); +} + +static Property hercules_properties[] = { + DEFINE_PROP_DRIVE("eeprom", HerculesState, blk_eeprom), + DEFINE_PROP_END_OF_LIST(), +}; + +static void hercules_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = hercules_realize; + dc->desc = "TI Hercules"; + device_class_set_props(dc, hercules_properties); + + /* Reason: Uses serial_hds in realize() directly */ + dc->user_creatable = false; +} + +static const TypeInfo hercules_type_info = { + .name = TYPE_HERCULES_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(HerculesState), + .instance_init = hercules_initfn, + .class_init = hercules_class_init, +}; + +static void hercules_register_types(void) +{ + type_register_static(&hercules_type_info); +} +type_init(hercules_register_types) diff --git a/hw/arm/tms570lcxxxx.c b/hw/arm/tms570lcxxxx.c new file mode 100644 index 0000000000000..219ea1fae2e4c --- /dev/null +++ b/hw/arm/tms570lcxxxx.c @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "sysemu/qtest.h" +#include "hw/loader.h" +#include "elf.h" + +#include "hw/arm/hercules.h" + +static void tms570lc43_init(MachineState *machine) +{ + Object *dev; + MemoryRegion *sdram = g_new(MemoryRegion, 1); + DriveInfo *eeprom = drive_get(IF_MTD, 0, 0); + const char *file; + + dev = object_new(TYPE_HERCULES_SOC); + qdev_prop_set_drive(DEVICE(dev), "eeprom", + eeprom ? blk_by_legacy_dinfo(eeprom) : NULL, + &error_abort); + object_property_set_bool(dev, true, "realized", &error_fatal); + + memory_region_init_ram(sdram, OBJECT(dev), "hercules.sdram", + 0x00800000, &error_fatal); + memory_region_add_subregion(get_system_memory(), HERCULES_EMIF_CS1_ADDR, + sdram); + + if (qtest_enabled()) { + return; + } + + if (machine->kernel_filename) { + uint64_t e, l; + + file = machine->kernel_filename; + if (load_elf(file, NULL, NULL, NULL, &e, &l, NULL, NULL, + 1, EM_ARM, 1, 0) < 0) { + goto exit; + } + } else if (machine->firmware) { + file = machine->firmware; + if (load_image_targphys(file, HERCULES_FLASH_ADDR, + HERCULES_FLASH_SIZE) < 0) { + goto exit; + } + } + + return; +exit: + error_report("Could not load '%s'", file); + exit(1); +} + +static void tms570lc43_machine_init(MachineClass *mc) +{ + mc->desc = "TMS570LC43"; + mc->init = tms570lc43_init; + mc->max_cpus = 1; +} + +DEFINE_MACHINE("tms570lc43", tms570lc43_machine_init) diff --git a/include/hw/arm/hercules.h b/include/hw/arm/hercules.h new file mode 100644 index 0000000000000..4ae4433c91a15 --- /dev/null +++ b/include/hw/arm/hercules.h @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2019, Blue Origin. + * + * Authors: Andrey Smirnov + * Benjamin Kamath + */ +#ifndef HERCULES_H +#define HERCULES_H + +#include "qemu/osdep.h" +#include "hw/arm/boot.h" +#include "cpu.h" +#include "hw/misc/hercules_l2ramw.h" +#include "hw/char/hercules_rtp.h" +#include "hw/intc/hercules_vim.h" +#include "hw/misc/hercules_system.h" +#include "hw/gpio/hercules_gpio.h" +#include "hw/adc/hercules_mibadc.h" +#include "hw/timer/hercules_rti.h" +#include "hw/net/hercules_emac.h" +#include "hw/ssi/hercules_spi.h" +#include "hw/dma/hercules_dma.h" +#include "hw/misc/hercules_scm.h" +#include "hw/misc/hercules_esm.h" +#include "hw/misc/hercules_efuse.h" +#include "hw/misc/hercules_pmm.h" +#include "hw/misc/hercules_stc.h" +#include "hw/misc/hercules_pbist.h" +#include "hw/misc/hercules_ccm.h" +#include "hw/misc/hercules_l2fmc.h" +#include "hw/misc/hercules_ecap.h" + +#define TYPE_HERCULES_SOC "ti-hercules" + +enum HerculesConfiguration { + HERCULES_NUM_N2HETS = 2, + HERCULES_NUM_MIBADCS = 2, + HERCULES_NUM_MIBSPIS = 5, + HERCULES_NUM_ECAPS = 6, +}; + +typedef struct HerculesState { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + BlockBackend *blk_eeprom; + + ARMCPU cpu; + HerculesL2RamwState l2ramw; + HerculesRTPState rtp; + + HerculesVimState vim; + HerculesSystemState system; + HerculesGioState gio; + HerculesN2HetState n2het[HERCULES_NUM_N2HETS]; + HerculesMibAdcState mibadc[HERCULES_NUM_MIBADCS]; + HerculesRtiState rti; + HerculesEmacState emac; + HerculesDMAState dma; + HerculesMibSpiState mibspi[HERCULES_NUM_MIBSPIS]; + HerculesSCMState scm; + HerculesESMState esm; + HerculesEFuseState efuse; + HerculesPMMState pmm; + HerculesSTCState stc; + HerculesPBISTState pbist; + HerculesCCMState ccm; + HerculesL2FMCState l2fmc; + HerculesECAPState ecap[HERCULES_NUM_ECAPS]; +} HerculesState; + +#define HERCULES_SOC(obj) OBJECT_CHECK(HerculesState, (obj), TYPE_HERCULES_SOC) + +enum HerculesMemoryMap { + HERCULES_FLASH_ADDR = 0x00000000, + HERCULES_FLASH_SIZE = 4 * 1024 * 1024, + + HERCULES_RAM_ADDR = 0x08000000, + + HERCULES_EMIF_CS1_ADDR = 0x80000000, + + HERCULES_OTP_BANK1_ADDR = 0xF0080000, + HERCULES_OTP_BANK1_SIZE = 8 * 1024, + + HERCULES_EEPROM_ADDR = 0xF0200000, + HERCULES_EEPROM_SIZE = 128 * 1024, + + HERCULES_SDR_MMR_ADDR = 0xFA000000, + + HERCULES_EMAC_CPPI_ADDR = 0xFC520000, + + HERCULES_EMAC_MODULE_ADDR = 0xFCF78000, + HERCULES_EMAC_CTRL_ADDR = 0xFCF78800, + HERCULES_EMAC_MDIO_ADDR = 0xFCF78900, + + HERCULES_ECAP1_ADDR = 0xFCF79300, + HERCULES_ECAP2_ADDR = 0xFCF79400, + HERCULES_ECAP3_ADDR = 0xFCF79500, + HERCULES_ECAP4_ADDR = 0xFCF79600, + HERCULES_ECAP5_ADDR = 0xFCF79700, + HERCULES_ECAP6_ADDR = 0xFCF79800, + + HERCULES_NMPU_ADDR = 0xFCFF1800, + HERCULES_NMPU_SIZE = 512, + + HERCULES_PCR2_ADDR = 0xFCFF1000, + + HERCULES_EMIF_ADDR = 0xFCFFE800, + HERCULES_EMIF_SIZE = 256, + + HERCULES_MIBSPI4_RAM_ADDR = 0xFF060000, + + HERCULES_MIBSPI2_RAM_ADDR = 0xFF080000, + + HERCULES_MIBSPI5_RAM_ADDR = 0xFF0A0000, + + HERCULES_MIBSPI3_RAM_ADDR = 0xFF0C0000, + + HERCULES_MIBSPI1_RAM_ADDR = 0xFF0E0000, + + HERCULES_MIBADC2_RAM_ADDR = 0xFF3A0000, + + HERCULES_MIBADC1_RAM_ADDR = 0xFF3E0000, + + HERCULES_N2HET2_RAM_ADDR = 0xFF440000, + + HERCULES_N2HET1_RAM_ADDR = 0xFF460000, + + HERCULES_DEBUG_ROM_ADDR = 0xFFA00000, + HERCULES_DEBUG_ADDR = 0xFFA01000, + HERCULES_ETM_ADDR = 0xFFA02000, + HERCULES_TPIU_ADDR = 0xFFA03000, + HERCULES_POM_ADDR = 0xFFA04000, + HERCULES_CTI1_ADDR = 0xFFA07000, + HERCULES_CTI2_ADDR = 0xFFA07000, + HERCULES_CTI3_ADDR = 0xFFA07000, + HERCULES_CTI4_ADDR = 0xFFA07000, + HERCULES_CTSF_ADDR = 0xFFA07000, + HERCULES_DEBUG_SIZE = 4 * 1024, + + HERCULES_PCR_ADDR = 0xFFFF1000, + + HERCULES_PINMUX_ADDR = 0xFFFF1C00, + HERCULES_PINMUX_SIZE = 1024, + + HERCULES_PCR3_ADDR = 0xFFF78000, + + HERCULES_N2HET1_ADDR = 0xFFF7B800, + + HERCULES_N2HET2_ADDR = 0xFFF7B900, + + HERCULES_GIO_ADDR = 0xFFF7BC00, + + HERCULES_MIBADC1_ADDR = 0xFFF7C000, + + HERCULES_MIBADC2_ADDR = 0xFFF7C200, + + HERCULES_LIN1_ADDR = 0xFFF7E400, + HERCULES_LIN1_SIZE = 256, + + HERCULES_MIBSPI1_CTRL_ADDR = 0xFFF7F400, + + HERCULES_MIBSPI2_CTRL_ADDR = 0xFFF7F600, + + HERCULES_MIBSPI3_CTRL_ADDR = 0xFFF7F800, + + HERCULES_MIBSPI4_CTRL_ADDR = 0xFFF7FA00, + + HERCULES_MIBSPI5_CTRL_ADDR = 0xFFF7FC00, + + HERCULES_DMA_RAM_ADDR = 0xFFF80000, + + HERCULES_VIM_RAM_ADDR = 0xFFF82000, + + HERCULES_L2FMC_ADDR = 0xFFF87000, + + HERCULES_EFUSE_ADDR = 0xFFF8C000, + + HERCULES_PMM_ADDR = 0xFFFF0000, + + HERCULES_SCM_ADDR = 0xFFFF0A00, + + HERCULES_EPC_ADDR = 0xFFFF0C00, + + HERCULES_RTP_ADDR = 0xFFFFFA00, + + HERCULES_DMA_ADDR = 0xFFFFF000, + + HERCULES_SYS2_ADDR = 0xFFFFE100, + + HERCULES_PBIST_ADDR = 0xFFFFE400, + + HERCULES_STC1_ADDR = 0xFFFFE600, + + HERCULES_DCC1_ADDR = 0xFFFFEC00, + HERCULES_DCC1_SIZE = 256, + + HERCULES_ESM_ADDR = 0xFFFFF500, + + HERCULES_CCM_ADDR = 0xFFFFF600, + + HERCULES_L2RAMW_ADDR = 0xFFFFF900, + + HERCULES_RTI_ADDR = 0xFFFFFC00, + + HERCULES_VIM_ECC_ADDR = 0xFFFFFD00, + + HERCULES_VIM_CONTROL_ADDR = 0xFFFFFE00, + + HERCULES_SYS_ADDR = 0xFFFFFF00, +}; + +#endif From 59e237a916bb202fb91fc1ee5f170ddb14b18f98 Mon Sep 17 00:00:00 2001 From: Benjamin Kamath Date: Thu, 4 Jun 2020 23:57:30 -0700 Subject: [PATCH 25/27] fixup! hw/arm: Add support for Hercules SoC --- hw/arm/Makefile.objs | 2 +- hw/arm/hercules.c | 211 ++++++++++++++++++-------------------- hw/arm/tms570lcxxxx.c | 69 ------------- include/hw/arm/hercules.h | 3 + 4 files changed, 105 insertions(+), 180 deletions(-) delete mode 100644 hw/arm/tms570lcxxxx.c diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 49625a2373444..d7d6a7a4283bb 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -54,4 +54,4 @@ obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o obj-$(CONFIG_FSL_IMX6UL) += fsl-imx6ul.o mcimx6ul-evk.o obj-$(CONFIG_NRF51_SOC) += nrf51_soc.o -obj-$(CONFIG_HERCULES) += hercules.o tms570lcxxxx.o +obj-$(CONFIG_HERCULES) += hercules.o diff --git a/hw/arm/hercules.c b/hw/arm/hercules.c index 5c5ea5a2fcdd0..1d733a5c3b2fc 100644 --- a/hw/arm/hercules.c +++ b/hw/arm/hercules.c @@ -8,44 +8,26 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" +#include "chardev/char-fe.h" #include "cpu.h" -#include "qemu/error-report.h" +#include "elf.h" +#include "exec/address-spaces.h" #include "hw/arm/boot.h" #include "hw/arm/hercules.h" #include "hw/boards.h" +#include "hw/loader.h" #include "hw/sysbus.h" #include "net/net.h" -#include "exec/address-spaces.h" -#include "chardev/char-fe.h" +#include "qemu/error-report.h" #include "sysemu/reset.h" #include "sysemu/sysemu.h" -#include "qemu/error-report.h" -#include "hw/loader.h" #include "sysemu/block-backend.h" +#include "sysemu/qtest.h" + -typedef struct QEMU_PACKED HerculesOTPSensorCalibrationData { - uint16_t temp1val; - uint16_t temp1; - uint16_t temp2val; - uint16_t temp2; - uint16_t temp3val; - uint16_t temp3; - uint32_t reserved; -} HerculesOTPSensorCalibrationData; - -typedef struct QEMU_PACKED HerculesOTPData { - uint32_t bank0_sector_info; - uint32_t bank0_package_memory_info; - uint32_t __reserved1[21]; - uint32_t lpo_trim_max_gclk; - uint32_t __reserved2[10]; - uint8_t part_number[32]; - uint32_t __reserved3[68]; - HerculesOTPSensorCalibrationData die_temp_sensor[3]; - uint32_t __reserved4[44]; - uint32_t single_bit_ecc[2]; - uint32_t double_bit_ecc[2]; -} HerculesOTPData; +#ifdef HOST_WORDS_BIGENDIAN +#pragma message "Hercules emulation not tested on Big Endian hosts" +#endif static const int HERCULES_MIBSPIn_DMAREQ[HERCULES_NUM_MIBSPIS][HERCULES_SPI_NUM_DMAREQS] = { @@ -92,13 +74,6 @@ static void hercules_initfn(Object *obj) object_property_add_child(obj, "cpu", cpu_obj); - s->cpu.ctr = 0x1d192992; /* 32K icache 32K dcache */ - - set_feature(&s->cpu.env, ARM_FEATURE_DUMMY_C15_REGS); - - object_property_set_bool(cpu_obj, true, "cfgend", &error_fatal); - object_property_set_bool(cpu_obj, true, "cfgend-instr", &error_fatal); - sysbus_init_child_obj(obj, "l2ramw", &s->l2ramw, sizeof(s->l2ramw), TYPE_HERCULES_L2RAMW); @@ -174,16 +149,24 @@ static void hercules_cpu_reset(void *opaque) static void hercules_realize(DeviceState *dev, Error **errp) { HerculesState *s = HERCULES_SOC(dev); + Object *cpu_obj = OBJECT(&s->cpu); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *flash = g_new(MemoryRegion, 1); MemoryRegion *eeprom = g_new(MemoryRegion, 1); MemoryRegion *otp_bank1 = g_new(MemoryRegion, 1); - uint8_t *otp_bank1_data; SysBusDevice *sbd; DeviceState *vim, *dma, *esm; qemu_irq irq, error; int i; + s->cpu.ctr = 0x1d192992; /* 32K icache 32K dcache */ + set_feature(&s->cpu.env, ARM_FEATURE_DUMMY_C15_REGS); + + if (s->is_tms570) { + object_property_set_bool(cpu_obj, true, "cfgend", &error_fatal); + object_property_set_bool(cpu_obj, true, "cfgend-instr", &error_fatal); + } + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &error_fatal); qemu_register_reset(hercules_cpu_reset, ARM_CPU(&s->cpu)); @@ -303,80 +286,10 @@ static void hercules_realize(DeviceState *dev, Error **errp) error = qdev_get_gpio_in(esm, HERCULES_EPC_CORRECTABLE_ERROR); sysbus_connect_irq(sbd, 2, error); - otp_bank1_data = g_new0(uint8_t, HERCULES_OTP_BANK1_SIZE); - if (0) { - /* - * Handle loading OTP1 from a custom file here - */ - } else { - /* - * FIXME: Convenience workaround, should eventually be removed - * once loading from custom file is implemented - */ - const HerculesOTPData dummy_otp = { - .bank0_sector_info = htobe32(0x00108303), - .bank0_package_memory_info = htobe32(0x01511000U), - - .lpo_trim_max_gclk = htobe32(0x0F201611), - - .part_number = { - 0x52, 0x4D, 0x35, 0x37, - 0x4c, 0x38, 0x34, 0x33, - 0x42, 0x5a, 0x57, 0x54, - 0x54, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - }, - - .die_temp_sensor = { - [0] = { - .temp1 = htobe16(0x012F), - .temp1val = htobe16(0x0762), - .temp2 = htobe16(0x00E9), - .temp2val = htobe16(0x05A5), - .temp3 = htobe16(0x018E), - .temp3val = htobe16(0x09A6), - .reserved = 0xFFFFFFFF, - }, - [1] = { - .temp1 = htobe16(0x012F), - .temp1val = htobe16(0x07A8), - .temp2 = htobe16(0x00E9), - .temp2val = htobe16(0x0613U), - .temp3 = htobe16(0x018E), - .temp3val = htobe16(0x09D4), - .reserved = 0xFFFFFFFFUL, - }, - [2] = { - .temp1 = htobe16(0x012F), - .temp1val = htobe16(0x0750), - .temp2 = htobe16(0x00E9), - .temp2val = htobe16(0x058E), - .temp3 = htobe16(0x018E), - .temp3val = htobe16(0x099D), - .reserved = 0xFFFFFFFFUL, - }, - }, - - .single_bit_ecc = { - htobe32(0x12345678), - htobe32(0x9ABCDEF0) - }, - .double_bit_ecc = { - htobe32(0x123C5478), - htobe32(0x9ABCDEF2) - }, - }; - - memcpy(otp_bank1_data + 0x158, &dummy_otp, sizeof(dummy_otp)); - } - - memory_region_init_ram_ptr(otp_bank1, OBJECT(dev), - TYPE_HERCULES_SOC ".otp.bank1", - HERCULES_OTP_BANK1_SIZE, - otp_bank1_data); + memory_region_init_rom(otp_bank1, OBJECT(dev), + TYPE_HERCULES_SOC ".otp.bank1", + HERCULES_OTP_BANK1_SIZE, + &error_fatal); memory_region_add_subregion(system_memory, HERCULES_OTP_BANK1_ADDR, otp_bank1); @@ -618,10 +531,16 @@ static void hercules_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sbd, 2, error); error = qdev_get_gpio_in(esm, HERCULES_CCMR5F_SELF_TEST_ERROR); sysbus_connect_irq(sbd, 3, error); + +#ifdef HOST_WORDS_BIGENDIAN + error_setg(errp, "failed realize on big endian host"); + return; +#endif } static Property hercules_properties[] = { DEFINE_PROP_DRIVE("eeprom", HerculesState, blk_eeprom), + DEFINE_PROP_BOOL("tms570", HerculesState, is_tms570, true), DEFINE_PROP_END_OF_LIST(), }; @@ -650,3 +569,75 @@ static void hercules_register_types(void) type_register_static(&hercules_type_info); } type_init(hercules_register_types) + +static void hercules_xx57_init(MachineState *machine, bool is_tms570) +{ + Object *dev; + MemoryRegion *sdram = g_new(MemoryRegion, 1); + DriveInfo *eeprom = drive_get(IF_MTD, 0, 0); + const char *file; + + dev = object_new(TYPE_HERCULES_SOC); + qdev_prop_set_drive(DEVICE(dev), "eeprom", + eeprom ? blk_by_legacy_dinfo(eeprom) : NULL, + &error_abort); + object_property_set_bool(dev, is_tms570, "tms570", &error_fatal); + object_property_set_bool(dev, true, "realized", &error_fatal); + + memory_region_init_ram(sdram, OBJECT(dev), "hercules.sdram", + 0x00800000, &error_fatal); + memory_region_add_subregion(get_system_memory(), HERCULES_EMIF_CS1_ADDR, + sdram); + + if (qtest_enabled()) { + return; + } + + if (machine->kernel_filename) { + uint64_t e, l; + + file = machine->kernel_filename; + if (load_elf(file, NULL, NULL, NULL, &e, &l, NULL, NULL, + 1, EM_ARM, 1, 0) < 0) { + goto exit; + } + } else if (machine->firmware) { + file = machine->firmware; + if (load_image_targphys(file, HERCULES_FLASH_ADDR, + HERCULES_FLASH_SIZE) < 0) { + goto exit; + } + } + + return; +exit: + error_report("Could not load '%s'", file); + exit(1); +} + +static void tms570lc43_init(MachineState *machine) +{ + hercules_xx57_init(machine, true); +} + +static void rm57l843_init(MachineState *machine) +{ + hercules_xx57_init(machine, false); +} + +static void tms570lc43_machine_init(MachineClass *mc) +{ + mc->desc = "Texas Instruments Hercules TMS570LC43"; + mc->init = tms570lc43_init; + mc->max_cpus = 1; +} + +static void rm57l843_machine_init(MachineClass *mc) +{ + mc->desc = "Texas Instruments Hercules RM57L843"; + mc->init = rm57l843_init; + mc->max_cpus = 1; +} + +DEFINE_MACHINE("tms570lc43", tms570lc43_machine_init) +DEFINE_MACHINE("rm57l843", rm57l843_machine_init) diff --git a/hw/arm/tms570lcxxxx.c b/hw/arm/tms570lcxxxx.c deleted file mode 100644 index 219ea1fae2e4c..0000000000000 --- a/hw/arm/tms570lcxxxx.c +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright (c) 2019, Blue Origin. - * - * Authors: Andrey Smirnov - * Benjamin Kamath - */ -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" -#include "hw/boards.h" -#include "sysemu/qtest.h" -#include "hw/loader.h" -#include "elf.h" - -#include "hw/arm/hercules.h" - -static void tms570lc43_init(MachineState *machine) -{ - Object *dev; - MemoryRegion *sdram = g_new(MemoryRegion, 1); - DriveInfo *eeprom = drive_get(IF_MTD, 0, 0); - const char *file; - - dev = object_new(TYPE_HERCULES_SOC); - qdev_prop_set_drive(DEVICE(dev), "eeprom", - eeprom ? blk_by_legacy_dinfo(eeprom) : NULL, - &error_abort); - object_property_set_bool(dev, true, "realized", &error_fatal); - - memory_region_init_ram(sdram, OBJECT(dev), "hercules.sdram", - 0x00800000, &error_fatal); - memory_region_add_subregion(get_system_memory(), HERCULES_EMIF_CS1_ADDR, - sdram); - - if (qtest_enabled()) { - return; - } - - if (machine->kernel_filename) { - uint64_t e, l; - - file = machine->kernel_filename; - if (load_elf(file, NULL, NULL, NULL, &e, &l, NULL, NULL, - 1, EM_ARM, 1, 0) < 0) { - goto exit; - } - } else if (machine->firmware) { - file = machine->firmware; - if (load_image_targphys(file, HERCULES_FLASH_ADDR, - HERCULES_FLASH_SIZE) < 0) { - goto exit; - } - } - - return; -exit: - error_report("Could not load '%s'", file); - exit(1); -} - -static void tms570lc43_machine_init(MachineClass *mc) -{ - mc->desc = "TMS570LC43"; - mc->init = tms570lc43_init; - mc->max_cpus = 1; -} - -DEFINE_MACHINE("tms570lc43", tms570lc43_machine_init) diff --git a/include/hw/arm/hercules.h b/include/hw/arm/hercules.h index 4ae4433c91a15..452a70ae479a8 100644 --- a/include/hw/arm/hercules.h +++ b/include/hw/arm/hercules.h @@ -45,7 +45,10 @@ typedef struct HerculesState { DeviceState parent_obj; /*< public >*/ + /* properties */ BlockBackend *blk_eeprom; + bool is_tms570; + /* end properties */ ARMCPU cpu; HerculesL2RamwState l2ramw; From 60fcc0266ba326fd232ea501430b5a3b897c4a93 Mon Sep 17 00:00:00 2001 From: Benjamin Kamath Date: Fri, 5 Jun 2020 09:30:10 -0700 Subject: [PATCH 26/27] fixup! hw/arm: Add support for Hercules SoC Can probably make this a helper function to reduce some boilerplate. What I would really want to do is allocate the MemoryRegionOps and have it return a 'const' version, but that should probably happen in init rather than realize... --- hw/adc/hercules_mibadc.c | 112 ++++++++++++++++++++---------------- hw/arm/hercules.c | 38 ++++++------ hw/char/hercules_rtp.c | 2 +- hw/dma/hercules_dma.c | 110 +++++++++++++++++++---------------- hw/gpio/hercules_gpio.c | 118 ++++++++++++++++++++++---------------- hw/intc/hercules_vim.c | 42 ++++++++------ hw/misc/hercules_ccm.c | 44 ++++++++------ hw/misc/hercules_ecap.c | 32 +++++++---- hw/misc/hercules_efuse.c | 42 ++++++++------ hw/misc/hercules_esm.c | 42 ++++++++------ hw/misc/hercules_l2fmc.c | 81 ++++++++++++++------------ hw/misc/hercules_l2ramw.c | 61 +++++++++++--------- hw/misc/hercules_pbist.c | 42 ++++++++------ hw/misc/hercules_pmm.c | 42 ++++++++------ hw/misc/hercules_scm.c | 82 ++++++++++++++------------ hw/misc/hercules_stc.c | 42 ++++++++------ hw/misc/hercules_system.c | 85 +++++++++++++++------------ hw/net/hercules_emac.c | 85 +++++++++++++++------------ hw/ssi/hercules_spi.c | 74 +++++++++++++----------- hw/timer/hercules_rti.c | 44 ++++++++------ include/hw/arm/hercules.h | 5 +- 21 files changed, 693 insertions(+), 532 deletions(-) diff --git a/hw/adc/hercules_mibadc.c b/hw/adc/hercules_mibadc.c index 0b4f91e7b682b..08cd96d361b17 100644 --- a/hw/adc/hercules_mibadc.c +++ b/hw/adc/hercules_mibadc.c @@ -15,6 +15,7 @@ #include "qapi/error.h" #include "hw/adc/hercules_mibadc.h" +#include "hw/arm/hercules.h" #define qemu_log_bad_offset(offset) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ @@ -269,61 +270,70 @@ static void hercules_mibadc_write(void *opaque, hwaddr offset, #undef IDX -static const MemoryRegionOps hercules_mibadc_ecc_ops = { - .read = hercules_mibadc_ecc_read, - .write = hercules_mibadc_ecc_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static const MemoryRegionOps hercules_mibadc_ram_ops = { - .read = hercules_mibadc_ram_read, - .write = hercules_mibadc_ram_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static const MemoryRegionOps hercules_mibadc_ops = { - .read = hercules_mibadc_read, - .write = hercules_mibadc_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_mibadc_realize(DeviceState *dev, Error **errp) { HerculesMibAdcState *s = HERCULES_MIBADC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_mibadc_ecc_ops = { + .read = hercules_mibadc_ecc_read, + .write = hercules_mibadc_ecc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_mibadc_ram_ops = { + .read = hercules_mibadc_ram_read, + .write = hercules_mibadc_ram_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_mibadc_ops = { + .read = hercules_mibadc_read, + .write = hercules_mibadc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_mibadc_ecc_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_mibadc_ram_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_mibadc_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->regs, OBJECT(dev), &hercules_mibadc_ops, s, TYPE_HERCULES_MIBADC ".regs", diff --git a/hw/arm/hercules.c b/hw/arm/hercules.c index 1d733a5c3b2fc..ce83b13039ad3 100644 --- a/hw/arm/hercules.c +++ b/hw/arm/hercules.c @@ -66,11 +66,17 @@ HERCULES_MIBSPIn_DMAREQ[HERCULES_NUM_MIBSPIS][HERCULES_SPI_NUM_DMAREQS] = { static void hercules_initfn(Object *obj) { HerculesState *s = HERCULES_SOC(obj); - Object *cpu_obj = OBJECT(&s->cpu); + Object *cpu_obj = object_new(ARM_CPU_TYPE_NAME("cortex-r5f")); int i; - object_initialize(cpu_obj, sizeof(s->cpu), - ARM_CPU_TYPE_NAME("cortex-r5f")); + s->cpu = ARM_CPU(cpu_obj); + s->cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ + set_feature(&s->cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + + if (s->is_tms570) { + object_property_set_bool(cpu_obj, true, "cfgend", &error_fatal); + object_property_set_bool(cpu_obj, true, "cfgend-instr", &error_fatal); + } object_property_add_child(obj, "cpu", cpu_obj); @@ -149,7 +155,6 @@ static void hercules_cpu_reset(void *opaque) static void hercules_realize(DeviceState *dev, Error **errp) { HerculesState *s = HERCULES_SOC(dev); - Object *cpu_obj = OBJECT(&s->cpu); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *flash = g_new(MemoryRegion, 1); MemoryRegion *eeprom = g_new(MemoryRegion, 1); @@ -159,16 +164,8 @@ static void hercules_realize(DeviceState *dev, Error **errp) qemu_irq irq, error; int i; - s->cpu.ctr = 0x1d192992; /* 32K icache 32K dcache */ - set_feature(&s->cpu.env, ARM_FEATURE_DUMMY_C15_REGS); - - if (s->is_tms570) { - object_property_set_bool(cpu_obj, true, "cfgend", &error_fatal); - object_property_set_bool(cpu_obj, true, "cfgend-instr", &error_fatal); - } - - object_property_set_bool(OBJECT(&s->cpu), true, "realized", &error_fatal); - qemu_register_reset(hercules_cpu_reset, ARM_CPU(&s->cpu)); + object_property_set_bool(OBJECT(s->cpu), true, "realized", &error_fatal); + qemu_register_reset(hercules_cpu_reset, ARM_CPU(s->cpu)); memory_region_init_rom(flash, OBJECT(dev), "hercules.flash", HERCULES_FLASH_SIZE, &error_fatal); @@ -228,9 +225,9 @@ static void hercules_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&s->vim), true, "realized", &error_abort); sbd = SYS_BUS_DEVICE(&s->vim); - irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ); + irq = qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ); sysbus_connect_irq(sbd, 0, irq); - irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ); + irq = qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ); sysbus_connect_irq(sbd, 1, irq); sysbus_mmio_map(sbd, 0, HERCULES_VIM_ECC_ADDR); sysbus_mmio_map(sbd, 1, HERCULES_VIM_CONTROL_ADDR); @@ -534,7 +531,6 @@ static void hercules_realize(DeviceState *dev, Error **errp) #ifdef HOST_WORDS_BIGENDIAN error_setg(errp, "failed realize on big endian host"); - return; #endif } @@ -577,11 +573,15 @@ static void hercules_xx57_init(MachineState *machine, bool is_tms570) DriveInfo *eeprom = drive_get(IF_MTD, 0, 0); const char *file; - dev = object_new(TYPE_HERCULES_SOC); + dev = object_new_with_props(TYPE_HERCULES_SOC, + object_get_objects_root(), + "hercules0", + &error_fatal, + "tms570", is_tms570, + NULL); qdev_prop_set_drive(DEVICE(dev), "eeprom", eeprom ? blk_by_legacy_dinfo(eeprom) : NULL, &error_abort); - object_property_set_bool(dev, is_tms570, "tms570", &error_fatal); object_property_set_bool(dev, true, "realized", &error_fatal); memory_region_init_ram(sdram, OBJECT(dev), "hercules.sdram", diff --git a/hw/char/hercules_rtp.c b/hw/char/hercules_rtp.c index cf2d1ccc6cab6..38fa525aeb0e5 100644 --- a/hw/char/hercules_rtp.c +++ b/hw/char/hercules_rtp.c @@ -35,7 +35,7 @@ static void hercules_rtp_write(void *opaque, hwaddr offset, uint64_t value, static const MemoryRegionOps hercules_rtp_ops = { .read = hercules_rtp_read, .write = hercules_rtp_write, - .endianness = DEVICE_BIG_ENDIAN, + .endianness = DEVICE_NATIVE_ENDIAN, }; static void hercules_rtp_init(Object *obj) diff --git a/hw/dma/hercules_dma.c b/hw/dma/hercules_dma.c index 0568fb47fd8d8..8506abb5ebbaa 100644 --- a/hw/dma/hercules_dma.c +++ b/hw/dma/hercules_dma.c @@ -14,6 +14,7 @@ #include "trace.h" #include "hw/dma/hercules_dma.h" +#include "hw/arm/hercules.h" #define qemu_log_bad_offset(offset) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ @@ -285,56 +286,6 @@ static uint64_t hercules_dma_ram_wcp_read(void *opaque, hwaddr offset, return 0; } -static const MemoryRegionOps hercules_dma_ops = { - .read = hercules_dma_read, - .write = hercules_dma_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static const MemoryRegionOps hercules_dma_ram_pcp_ops = { - .read = hercules_dma_ram_pcp_read, - .write = hercules_dma_ram_pcp_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static const MemoryRegionOps hercules_dma_ram_wcp_ops = { - .read = hercules_dma_ram_wcp_read, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_dma_reset(DeviceState *d) { HerculesDMAState *s = HERCULES_DMA(d); @@ -377,8 +328,67 @@ static void hercules_dma_realize(DeviceState *dev, Error **errp) { HerculesDMAState *s = HERCULES_DMA(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); int i; + static MemoryRegionOps hercules_dma_ops = { + .read = hercules_dma_read, + .write = hercules_dma_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_dma_ram_pcp_ops = { + .read = hercules_dma_ram_pcp_read, + .write = hercules_dma_ram_pcp_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_dma_ram_wcp_ops = { + .read = hercules_dma_ram_wcp_read, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_dma_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_dma_ram_pcp_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_dma_ram_wcp_ops.endianness = DEVICE_BIG_ENDIAN; + } + memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_dma_ops, s, TYPE_HERCULES_DMA ".io", HERCULES_DMA_SIZE); sysbus_init_mmio(sbd, &s->iomem); diff --git a/hw/gpio/hercules_gpio.c b/hw/gpio/hercules_gpio.c index 1cf8687f762ee..9caa8de61adc9 100644 --- a/hw/gpio/hercules_gpio.c +++ b/hw/gpio/hercules_gpio.c @@ -12,6 +12,7 @@ #include "qapi/error.h" #include "hw/gpio/hercules_gpio.h" +#include "hw/arm/hercules.h" #include "trace.h" @@ -306,44 +307,52 @@ static void hercules_n2het_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_gio_gio_ops = { - .read = hercules_gio_gio_read, - .write = hercules_gio_gio_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static const MemoryRegionOps hercules_gio_regs_ops = { - .read = hercules_gio_reg_read, - .write = hercules_gio_reg_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_gio_realize(DeviceState *dev, Error **errp) { HerculesGioState *s = HERCULES_GIO(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_gio_gio_ops = { + .read = hercules_gio_gio_read, + .write = hercules_gio_gio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_gio_regs_ops = { + .read = hercules_gio_reg_read, + .write = hercules_gio_reg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_gio_gio_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_gio_regs_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->io.regs, OBJECT(dev), &hercules_gio_regs_ops, s, TYPE_HERCULES_GIO ".io.regs", @@ -389,23 +398,6 @@ static void hercules_gio_class_init(ObjectClass *klass, void *data) dc->realize = hercules_gio_realize; } -static const MemoryRegionOps hercules_n2het_ops = { - .read = hercules_n2het_read, - .write = hercules_n2het_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_n2het_initfn(Object *obj) { HerculesN2HetState *s = HERCULES_N2HET(obj); @@ -419,6 +411,30 @@ static void hercules_n2het_realize(DeviceState *dev, Error **errp) { HerculesN2HetState *s = HERCULES_N2HET(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_n2het_ops = { + .read = hercules_n2het_read, + .write = hercules_n2het_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_n2het_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_n2het_ops, s, TYPE_HERCULES_N2HET ".io", HERCULES_N2HET_REG_SIZE); diff --git a/hw/intc/hercules_vim.c b/hw/intc/hercules_vim.c index 79a356336c353..3e74752993ccc 100644 --- a/hw/intc/hercules_vim.c +++ b/hw/intc/hercules_vim.c @@ -13,6 +13,7 @@ #include "cpu.h" #include "hw/intc/hercules_vim.h" +#include "hw/arm/hercules.h" #define qemu_log_bad_offset(offset) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ @@ -220,23 +221,6 @@ static void hercules_vim_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_vim_ops = { - .read = hercules_vim_read, - .write = hercules_vim_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_vim_reset(DeviceState *d) { HerculesVimState *s = HERCULES_VIM(d); @@ -274,6 +258,30 @@ static void hercules_vim_realize(DeviceState *dev, Error **errp) { HerculesVimState *s = HERCULES_VIM(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_vim_ops = { + .read = hercules_vim_read, + .write = hercules_vim_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_vim_ops.endianness = DEVICE_BIG_ENDIAN; + } qdev_prop_set_string(DEVICE(&s->ecc), "name", "ecc-regs"); qdev_prop_set_uint64(DEVICE(&s->ecc), "size", 256); diff --git a/hw/misc/hercules_ccm.c b/hw/misc/hercules_ccm.c index 956f316dd5e52..8464427b0fa35 100644 --- a/hw/misc/hercules_ccm.c +++ b/hw/misc/hercules_ccm.c @@ -15,6 +15,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_ccm.h" +#include "hw/arm/hercules.h" enum { HERCULES_CCM_SIZE = 256, @@ -108,29 +109,36 @@ static void hercules_ccm_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_ccm_ops = { - .read = hercules_ccm_read, - .write = hercules_ccm_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_ccm_realize(DeviceState *dev, Error **errp) { HerculesCCMState *s = HERCULES_CCM(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_ccm_ops = { + .read = hercules_ccm_read, + .write = hercules_ccm_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_ccm_ops.endianness = DEVICE_BIG_ENDIAN; + } - memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_ccm_ops, + memory_region_init_io(&s->iomem, obj, &hercules_ccm_ops, s, TYPE_HERCULES_CCM ".io", HERCULES_CCM_SIZE); sysbus_init_mmio(sbd, &s->iomem); diff --git a/hw/misc/hercules_ecap.c b/hw/misc/hercules_ecap.c index f1855d4145e9d..d0fceea16d47e 100644 --- a/hw/misc/hercules_ecap.c +++ b/hw/misc/hercules_ecap.c @@ -15,6 +15,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_ecap.h" +#include "hw/arm/hercules.h" enum { HERCULES_ECAP_SIZE = 256, @@ -112,23 +113,30 @@ static void hercules_ecap_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_ecap_ops = { - .read = hercules_ecap_read, - .write = hercules_ecap_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 2, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_ecap_realize(DeviceState *dev, Error **errp) { HerculesECAPState *s = HERCULES_ECAP(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_ecap_ops = { + .read = hercules_ecap_read, + .write = hercules_ecap_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_ecap_ops.endianness = DEVICE_BIG_ENDIAN; + } - memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_ecap_ops, + memory_region_init_io(&s->iomem, obj, &hercules_ecap_ops, s, TYPE_HERCULES_ECAP ".io", HERCULES_ECAP_SIZE); sysbus_init_mmio(sbd, &s->iomem); } diff --git a/hw/misc/hercules_efuse.c b/hw/misc/hercules_efuse.c index ef40018abc14b..ae768ec609be1 100644 --- a/hw/misc/hercules_efuse.c +++ b/hw/misc/hercules_efuse.c @@ -15,6 +15,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_efuse.h" +#include "hw/arm/hercules.h" enum { HERCULES_EFUSE_SIZE = 256, @@ -117,27 +118,34 @@ static void hercules_efuse_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_efuse_ops = { - .read = hercules_efuse_read, - .write = hercules_efuse_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_efuse_realize(DeviceState *dev, Error **errp) { HerculesEFuseState *s = HERCULES_EFUSE(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_efuse_ops = { + .read = hercules_efuse_read, + .write = hercules_efuse_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_efuse_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_efuse_ops, s, TYPE_HERCULES_EFUSE ".io", HERCULES_EFUSE_SIZE); diff --git a/hw/misc/hercules_esm.c b/hw/misc/hercules_esm.c index b2dc0ae45e1c4..12e01b6545958 100644 --- a/hw/misc/hercules_esm.c +++ b/hw/misc/hercules_esm.c @@ -15,6 +15,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_esm.h" +#include "hw/arm/hercules.h" enum { HERCULES_ESM_SIZE = 256, @@ -346,27 +347,34 @@ static void hercules_esm_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_esm_ops = { - .read = hercules_esm_read, - .write = hercules_esm_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_esm_realize(DeviceState *dev, Error **errp) { HerculesESMState *s = HERCULES_ESM(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_esm_ops = { + .read = hercules_esm_read, + .write = hercules_esm_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_esm_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_esm_ops, s, TYPE_HERCULES_ESM ".io", HERCULES_ESM_SIZE); diff --git a/hw/misc/hercules_l2fmc.c b/hw/misc/hercules_l2fmc.c index ee345e490f910..4468a07fd5101 100644 --- a/hw/misc/hercules_l2fmc.c +++ b/hw/misc/hercules_l2fmc.c @@ -15,6 +15,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_l2fmc.h" +#include "hw/arm/hercules.h" enum { HERCULES_L2FMC_SIZE = 4 * 1024, @@ -124,23 +125,6 @@ static void hercules_epc_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_epc_ops = { - .read = hercules_epc_read, - .write = hercules_epc_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static uint64_t hercules_l2fmc_read(void *opaque, hwaddr offset, unsigned size) { @@ -245,29 +229,54 @@ static void hercules_l2fmc_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_l2fmc_ops = { - .read = hercules_l2fmc_read, - .write = hercules_l2fmc_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_l2fmc_realize(DeviceState *dev, Error **errp) { HerculesL2FMCState *s = HERCULES_L2FMC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_epc_ops = { + .read = hercules_epc_read, + .write = hercules_epc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_l2fmc_ops = { + .read = hercules_l2fmc_read, + .write = hercules_l2fmc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_epc_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_l2fmc_ops.endianness = DEVICE_BIG_ENDIAN; + } - memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_l2fmc_ops, + memory_region_init_io(&s->iomem, obj, &hercules_l2fmc_ops, s, TYPE_HERCULES_L2FMC ".io", HERCULES_L2FMC_SIZE); sysbus_init_mmio(sbd, &s->iomem); @@ -280,7 +289,7 @@ static void hercules_l2fmc_realize(DeviceState *dev, Error **errp) * for it involves flash controller so dealing with it here * simplifies things */ - memory_region_init_io(&s->epc, OBJECT(dev), &hercules_epc_ops, + memory_region_init_io(&s->epc, obj, &hercules_epc_ops, s, TYPE_HERCULES_L2FMC ".epc", HERCULES_EPC_SIZE); sysbus_init_mmio(sbd, &s->epc); diff --git a/hw/misc/hercules_l2ramw.c b/hw/misc/hercules_l2ramw.c index 203e5424e7a3c..2d6dde21c5756 100644 --- a/hw/misc/hercules_l2ramw.c +++ b/hw/misc/hercules_l2ramw.c @@ -13,6 +13,7 @@ #include "qapi/error.h" #include "hw/misc/hercules_l2ramw.h" +#include "hw/arm/hercules.h" enum { HERCULES_L2RAMW_CONTAINER_SIZE = 8 * 1024 * 1024, @@ -125,17 +126,6 @@ static uint64_t hercules_l2ramw_read(void *opaque, hwaddr offset, return 0; } -static const MemoryRegionOps hercules_l2ramw_ops = { - .read = hercules_l2ramw_read, - .write = hercules_l2ramw_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_l2ramw_ecc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { @@ -149,31 +139,50 @@ static uint64_t hercules_l2ramw_ecc_read(void *opaque, hwaddr offset, return 0; } -static const MemoryRegionOps hercules_l2ramw_ecc_ops = { - .read = hercules_l2ramw_ecc_read, - .write = hercules_l2ramw_ecc_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_l2ramw_realize(DeviceState *dev, Error **errp) { HerculesL2RamwState *s = HERCULES_L2RAMW(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_l2ramw_ops = { + .read = hercules_l2ramw_read, + .write = hercules_l2ramw_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_l2ramw_ecc_ops = { + .read = hercules_l2ramw_ecc_read, + .write = hercules_l2ramw_ecc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_l2ramw_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_l2ramw_ecc_ops.endianness = DEVICE_BIG_ENDIAN; + } - memory_region_init_io(&s->io.ecc, OBJECT(dev), &hercules_l2ramw_ecc_ops, s, + memory_region_init_io(&s->io.ecc, obj, &hercules_l2ramw_ecc_ops, s, TYPE_HERCULES_L2RAMW ".io.ecc", HERCULES_L2RAMW_ECC_SIZE); - memory_region_init_ram(&s->io.sram, OBJECT(dev), + memory_region_init_ram(&s->io.sram, obj, TYPE_HERCULES_L2RAMW ".io.sram", HERCULES_L2RAMW_SRAM_SIZE, &error_fatal); - memory_region_init(&s->io.container, OBJECT(dev), + memory_region_init(&s->io.container, obj, TYPE_HERCULES_L2RAMW ".io", HERCULES_L2RAMW_CONTAINER_SIZE); @@ -184,7 +193,7 @@ static void hercules_l2ramw_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->io.container); - memory_region_init_io(&s->io.regs, OBJECT(dev), &hercules_l2ramw_ops, s, + memory_region_init_io(&s->io.regs, obj, &hercules_l2ramw_ops, s, TYPE_HERCULES_L2RAMW ".io.regs", HERCULES_L2RAMW_SIZE); diff --git a/hw/misc/hercules_pbist.c b/hw/misc/hercules_pbist.c index ddde5a2060678..6e58d6ef540c6 100644 --- a/hw/misc/hercules_pbist.c +++ b/hw/misc/hercules_pbist.c @@ -15,6 +15,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_pbist.h" +#include "hw/arm/hercules.h" enum { HERCULES_PBIST_SIZE = 512, @@ -121,27 +122,34 @@ static void hercules_pbist_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_pbist_ops = { - .read = hercules_pbist_read, - .write = hercules_pbist_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_pbist_realize(DeviceState *dev, Error **errp) { HerculesPBISTState *s = HERCULES_PBIST(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_pbist_ops = { + .read = hercules_pbist_read, + .write = hercules_pbist_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_pbist_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_pbist_ops, s, TYPE_HERCULES_PBIST ".io", HERCULES_PBIST_SIZE); diff --git a/hw/misc/hercules_pmm.c b/hw/misc/hercules_pmm.c index 900f31d2248da..c194ddfe589c8 100644 --- a/hw/misc/hercules_pmm.c +++ b/hw/misc/hercules_pmm.c @@ -15,6 +15,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_pmm.h" +#include "hw/arm/hercules.h" enum { HERCULES_PMM_SIZE = 256, @@ -97,27 +98,34 @@ static void hercules_pmm_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_pmm_ops = { - .read = hercules_pmm_read, - .write = hercules_pmm_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_pmm_realize(DeviceState *dev, Error **errp) { HerculesPMMState *s = HERCULES_PMM(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_pmm_ops = { + .read = hercules_pmm_read, + .write = hercules_pmm_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_pmm_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_pmm_ops, s, TYPE_HERCULES_PMM ".io", HERCULES_PMM_SIZE); diff --git a/hw/misc/hercules_scm.c b/hw/misc/hercules_scm.c index 841ffdbfe8fc3..cf3fe8cf70a45 100644 --- a/hw/misc/hercules_scm.c +++ b/hw/misc/hercules_scm.c @@ -16,6 +16,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_scm.h" +#include "hw/arm/hercules.h" enum { HERCULES_SCM_SIZE = 256, @@ -108,47 +109,54 @@ static void hercules_sdr_mmr_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_scm_ops = { - .read = hercules_scm_read, - .write = hercules_scm_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static const MemoryRegionOps hercules_sdr_mmr_ops = { - .read = hercules_sdr_mmr_read, - .write = hercules_sdr_mmr_write, - /* - * This is not BE on TMS570 as per Device#51 errata - */ - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_scm_realize(DeviceState *dev, Error **errp) { HerculesSCMState *s = HERCULES_SCM(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_scm_ops = { + .read = hercules_scm_read, + .write = hercules_scm_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_sdr_mmr_ops = { + .read = hercules_sdr_mmr_read, + .write = hercules_sdr_mmr_write, + /* + * This is not BE on TMS570 as per Device#51 errata + */ + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_scm_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->io.scm, OBJECT(dev), &hercules_scm_ops, s, TYPE_HERCULES_SCM ".io.scm", HERCULES_SCM_SIZE); diff --git a/hw/misc/hercules_stc.c b/hw/misc/hercules_stc.c index a03271c9cd5c4..c8efbc6e2d038 100644 --- a/hw/misc/hercules_stc.c +++ b/hw/misc/hercules_stc.c @@ -16,6 +16,7 @@ #include "sysemu/sysemu.h" #include "hw/misc/hercules_stc.h" +#include "hw/arm/hercules.h" enum { HERCULES_STC_SIZE = 256, @@ -103,23 +104,6 @@ static void hercules_stc_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_stc_ops = { - .read = hercules_stc_read, - .write = hercules_stc_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_stc_self_test(void *opaque) { HerculesSTCState *s = opaque; @@ -142,6 +126,30 @@ static void hercules_stc_realize(DeviceState *dev, Error **errp) { HerculesSTCState *s = HERCULES_STC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + + static MemoryRegionOps hercules_stc_ops = { + .read = hercules_stc_read, + .write = hercules_stc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_stc_ops.endianness = DEVICE_BIG_ENDIAN; + } memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_stc_ops, s, TYPE_HERCULES_STC ".io", HERCULES_STC_SIZE); diff --git a/hw/misc/hercules_system.c b/hw/misc/hercules_system.c index 7335adccea802..fe44e8f224369 100644 --- a/hw/misc/hercules_system.c +++ b/hw/misc/hercules_system.c @@ -15,7 +15,7 @@ #include "qapi/error.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" - +#include "hw/arm/hercules.h" #include "hw/misc/hercules_system.h" #define NAME_SIZE 20 @@ -180,23 +180,6 @@ static void hercules_sys_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps hercules_system_sys_ops = { - .read = hercules_sys_read, - .write = hercules_sys_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static uint64_t hercules_sys2_read(void *opaque, hwaddr offset, unsigned size) { @@ -232,24 +215,6 @@ static void hercules_sys2_write(void *opaque, hwaddr offset, } } - -static const MemoryRegionOps hercules_system_sys2_ops = { - .read = hercules_sys2_read, - .write = hercules_sys2_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_system_set_signal(void *opaque, int sig, int level) { HerculesSystemState *s = opaque; @@ -274,6 +239,7 @@ static void hercules_system_set_signal(void *opaque, int sig, int level) static void hercules_system_initfn(Object *obj) { HerculesSystemState *s = HERCULES_SYSTEM(obj); + int i; for (i = 0; i < HERCULES_SYSTEM_NUM_PCRS; i++) { @@ -289,16 +255,59 @@ static void hercules_system_realize(DeviceState *dev, Error **errp) { HerculesSystemState *s = HERCULES_SYSTEM(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + char name[NAME_SIZE]; DeviceState *d; int i; - memory_region_init_io(&s->sys, OBJECT(dev), &hercules_system_sys_ops, + static MemoryRegionOps hercules_system_sys_ops = { + .read = hercules_sys_read, + .write = hercules_sys_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_system_sys2_ops = { + .read = hercules_sys2_read, + .write = hercules_sys2_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_system_sys_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_system_sys2_ops.endianness = DEVICE_BIG_ENDIAN; + } + + memory_region_init_io(&s->sys, obj, &hercules_system_sys_ops, s, TYPE_HERCULES_SYSTEM ".io.sys", HERCULES_SYSTEM_SYS_SIZE); sysbus_init_mmio(sbd, &s->sys); - memory_region_init_io(&s->sys2, OBJECT(dev), &hercules_system_sys2_ops, + memory_region_init_io(&s->sys2, obj, &hercules_system_sys2_ops, s, TYPE_HERCULES_SYSTEM ".io.sys2", HERCULES_SYSTEM_SYS2_SIZE); sysbus_init_mmio(sbd, &s->sys2); diff --git a/hw/net/hercules_emac.c b/hw/net/hercules_emac.c index 0884dac69aff8..978688b362236 100644 --- a/hw/net/hercules_emac.c +++ b/hw/net/hercules_emac.c @@ -15,6 +15,7 @@ #include "sysemu/dma.h" #include "hw/net/hercules_emac.h" +#include "hw/arm/hercules.h" typedef struct QEMU_PACKED HerculesCppiDescriptor { uint32_t next; @@ -534,40 +535,6 @@ static void emac_reset(DeviceState *d) memset(s->rxcp, 0, sizeof(s->rxcp)); } -static const MemoryRegionOps hercules_emac_module_ops = { - .read = hercules_emac_module_read, - .write = hercules_emac_module_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - -static const MemoryRegionOps hercules_emac_contrl_ops = { - .read = hercules_emac_control_read, - .write = hercules_emac_control_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static NetClientInfo net_emac_info = { .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), @@ -580,20 +547,62 @@ static void hercules_emac_realize(DeviceState *dev, Error **errp) { HerculesEmacState *s = HERCULES_EMAC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); MemoryRegion *io; + static MemoryRegionOps hercules_emac_module_ops = { + .read = hercules_emac_module_read, + .write = hercules_emac_module_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + static MemoryRegionOps hercules_emac_control_ops = { + .read = hercules_emac_control_read, + .write = hercules_emac_control_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_emac_module_ops.endianness = DEVICE_BIG_ENDIAN; + hercules_emac_control_ops.endianness = DEVICE_BIG_ENDIAN; + } + /* * Init controller mmap'd interface * 0x000 - 0x800 : emac * 0x800 - 0x900 : ctrl * 0x900 - 0xA00 : mdio */ - memory_region_init_io(&s->module, OBJECT(dev), &hercules_emac_module_ops, + memory_region_init_io(&s->module, obj, &hercules_emac_module_ops, s, TYPE_HERCULES_EMAC ".io.module", HERCULES_EMAC_MODULE_SIZE); sysbus_init_mmio(sbd, &s->module); - memory_region_init_io(&s->control, OBJECT(dev), &hercules_emac_contrl_ops, + memory_region_init_io(&s->control, obj, &hercules_emac_control_ops, s, TYPE_HERCULES_EMAC ".io.control", HERCULES_EMAC_CONTROL_SIZE); sysbus_init_mmio(sbd, &s->control); @@ -606,7 +615,7 @@ static void hercules_emac_realize(DeviceState *dev, Error **errp) io = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0); sysbus_init_mmio(sbd, io); - memory_region_init_ram(&s->ram, OBJECT(dev), + memory_region_init_ram(&s->ram, obj, TYPE_HERCULES_EMAC ".cppi-ram", HERCULES_CPPI_RAM_SIZE, &error_fatal); sysbus_init_mmio(sbd, &s->ram); @@ -614,7 +623,7 @@ static void hercules_emac_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_emac_info, &s->conf, - object_get_typename(OBJECT(dev)), + object_get_typename(obj), dev->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/ssi/hercules_spi.c b/hw/ssi/hercules_spi.c index 812b869bf5212..38f4ce01fc232 100644 --- a/hw/ssi/hercules_spi.c +++ b/hw/ssi/hercules_spi.c @@ -11,10 +11,10 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qapi/error.h" - #include "sysemu/dma.h" #include "hw/ssi/hercules_spi.h" +#include "hw/arm/hercules.h" #define qemu_log_bad_offset(offset) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %" HWADDR_PRIx "\n", \ @@ -596,54 +596,62 @@ static void hercules_spi_set_spiena(void *opaque, int req, int level) } } -static const MemoryRegionOps hercules_spi_rxram_ops = { - .read = hercules_spi_rxram_read, - .write = hercules_spi_rxram_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 1, - .max_access_size = 4, - .unaligned = true, - }, -}; - -static const MemoryRegionOps hercules_spi_ops = { - .read = hercules_spi_read, - .write = hercules_spi_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 2, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_spi_realize(DeviceState *dev, Error **errp) { HerculesMibSpiState *s = HERCULES_SPI(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); + int i; - memory_region_init_io(&s->io.regs, OBJECT(dev), &hercules_spi_ops, s, + static MemoryRegionOps hercules_spi_rxram_ops = { + .read = hercules_spi_rxram_read, + .write = hercules_spi_rxram_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 1, + .max_access_size = 4, + .unaligned = true, + }, + }; + + static MemoryRegionOps hercules_spi_ops = { + .read = hercules_spi_read, + .write = hercules_spi_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_spi_ops.endianness = DEVICE_BIG_ENDIAN; + } + + memory_region_init_io(&s->io.regs, obj, &hercules_spi_ops, s, TYPE_HERCULES_SPI ".io", HERCULES_SPI_SIZE); sysbus_init_mmio(sbd, &s->io.regs); - memory_region_init_ram_ptr(&s->io.txram, OBJECT(dev), + memory_region_init_ram_ptr(&s->io.txram, obj, TYPE_HERCULES_SPI ".ram.tx", sizeof(s->txram), s->txram); - memory_region_init_io(&s->io.rxram, OBJECT(dev), + memory_region_init_io(&s->io.rxram, obj, &hercules_spi_rxram_ops, s, TYPE_HERCULES_SPI ".ram.rx", sizeof(s->rxram)); - memory_region_init(&s->io.ram, OBJECT(dev), + memory_region_init(&s->io.ram, obj, TYPE_HERCULES_SPI ".ram", sizeof(s->txram) + sizeof(s->rxram)); diff --git a/hw/timer/hercules_rti.c b/hw/timer/hercules_rti.c index 46ec8f196e50a..d75e2450ec696 100644 --- a/hw/timer/hercules_rti.c +++ b/hw/timer/hercules_rti.c @@ -15,6 +15,7 @@ #include "qapi/error.h" #include "hw/timer/hercules_rti.h" +#include "hw/arm/hercules.h" enum HerculesRtiRegisters { RTIGCTRL = 0x00, @@ -270,23 +271,6 @@ static void hercules_rti_write(void *opaque, hwaddr offset, uint64_t val64, } } -static const MemoryRegionOps hercules_rti_ops = { - .read = hercules_rti_read, - .write = hercules_rti_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - /* - * Our device would not work correctly if the guest was doing - * unaligned access. This might not be a limitation on the real - * device but in practice there is no reason for a guest to access - * this device unaligned. - */ - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false, - }, -}; - static void hercules_rti_init_irq_group(HerculesRtiState *s, int group, int line_num) { @@ -312,9 +296,33 @@ static void hercules_rti_realize(DeviceState *dev, Error **errp) { HerculesRtiState *s = HERCULES_RTI(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Object *obj = OBJECT(dev); + HerculesState *parent = HERCULES_SOC(obj->parent); int i; - memory_region_init_io(&s->iomem, OBJECT(dev), &hercules_rti_ops, s, + static MemoryRegionOps hercules_rti_ops = { + .read = hercules_rti_read, + .write = hercules_rti_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + }; + + if (parent->is_tms570) + { + hercules_rti_ops.endianness = DEVICE_BIG_ENDIAN; + } + + memory_region_init_io(&s->iomem, obj, &hercules_rti_ops, s, TYPE_HERCULES_RTI ".io", HERCULES_RTI_SIZE); sysbus_init_mmio(sbd, &s->iomem); diff --git a/include/hw/arm/hercules.h b/include/hw/arm/hercules.h index 452a70ae479a8..9edc62d4d36e2 100644 --- a/include/hw/arm/hercules.h +++ b/include/hw/arm/hercules.h @@ -10,7 +10,6 @@ #include "qemu/osdep.h" #include "hw/arm/boot.h" -#include "cpu.h" #include "hw/misc/hercules_l2ramw.h" #include "hw/char/hercules_rtp.h" #include "hw/intc/hercules_vim.h" @@ -40,6 +39,8 @@ enum HerculesConfiguration { HERCULES_NUM_ECAPS = 6, }; +struct ARMCPU; + typedef struct HerculesState { /*< private >*/ DeviceState parent_obj; @@ -50,7 +51,7 @@ typedef struct HerculesState { bool is_tms570; /* end properties */ - ARMCPU cpu; + struct ARMCPU* cpu; HerculesL2RamwState l2ramw; HerculesRTPState rtp; From f66e469e67ea29d803cd3559e4fcbe35d25000dc Mon Sep 17 00:00:00 2001 From: Benjamin Kamath Date: Thu, 4 Jun 2020 09:57:03 -0700 Subject: [PATCH 27/27] MAINTAINERS: add entry for TI Hercules chip family Signed-off-by: Benjamin Kamath --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6e7890ce8222e..ee879492349cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -964,6 +964,14 @@ F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c +TI Hercules +M: Benjamin Kamath +M: Andrey Smirnov +L: qemu-arm@nongnu.org +S: Maintained +F: hw/*/hercules_* +F: include/hw/*/hercules_* + CRIS Machines ------------- Axis Dev88