Message ID | e597ae6f686b2defba87d7f24af1351ab267e6e1.1454967766.git.jcd@tribudubois.net (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 8 February 2016 at 22:08, Jean-Christophe Dubois <jcd@tribudubois.net> wrote: > This controller is also present in i.MX5X devices but they are not > yet emulated by QEMU. > > Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net> > @@ -0,0 +1,353 @@ > +/* > + * IMX6 System Reset Controller > + * > + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > + * See the COPYING file in the top-level directory. > + * > + */ > + > +#include "hw/misc/imx6_src.h" > +#include "sysemu/sysemu.h" > +#include "qemu/bitops.h" #include "qemu/osdep.h" as first #include line. > +static void imx6_src_reset(DeviceState *dev) > +{ > + IMX6SRCState *s = IMX6_SRC(dev); > + > + DPRINTF("\n"); > + > + /* > + * We only clear the first registers as all GPR registers are preserved > + * over resets > + */ > + memset(s->regs, 0, SRC_MAX * sizeof(uint32_t)); Comment doesn't seem to match code? > + /* Set reset values */ > + s->regs[SRC_SCR] = 0x521; > + s->regs[SRC_SRSR] = 0x1; > + s->regs[SRC_SIMR] = 0x1F; > +} > + > +static CPUState *imx6_src_get_cpu_by_id(uint32_t id) > +{ > + CPUState *cpu; > + > + DPRINTF("cpu %d\n", id); > + > + CPU_FOREACH(cpu) { > + ARMCPU *armcpu = ARM_CPU(cpu); > + > + if (armcpu->mp_affinity == id) { > + return cpu; > + } > + } > + > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Resquesting unknown CPU %d\n", > + TYPE_IMX6_SRC, __func__, id); > + > + return NULL; > +} > + > +static void imx6_src_cpu_on(uint32_t cpuid, uint32_t entry, uint32_t context_id) > +{ > + CPUState *target_cpu_state; > + ARMCPU *target_cpu; > + CPUClass *target_cpu_class; > + > + DPRINTF("cpu %d @ 0x%08x with R0 = 0x%08x\n", cpuid, entry, context_id); > + > + /* change to the cpu we are powering up */ > + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); > + if (!target_cpu_state) { > + return; > + } > + target_cpu = ARM_CPU(target_cpu_state); > + if (!target_cpu->powered_off) { > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is already running\n", > + TYPE_IMX6_SRC, __func__, cpuid); > + return; > + } > + target_cpu_class = CPU_GET_CLASS(target_cpu); > + > + /* Initialize the cpu we are turning on */ > + cpu_reset(target_cpu_state); > + target_cpu->powered_off = false; > + target_cpu_state->halted = 0; > + > + target_cpu->env.regs[0] = context_id; > + target_cpu->env.thumb = entry & 1; > + > + target_cpu_class->set_pc(target_cpu_state, entry); > +} > + > +static void imx6_src_cpu_off(uint32_t cpuid) > +{ > + CPUState *target_cpu_state; > + ARMCPU *target_cpu; > + > + DPRINTF("cpu %d\n", cpuid); > + > + /* change to the cpu we are powering up */ > + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); > + if (!target_cpu_state) { > + return; > + } > + target_cpu = ARM_CPU(target_cpu_state); > + if (target_cpu->powered_off) { > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is already off\n", > + TYPE_IMX6_SRC, __func__, cpuid); > + return; > + } > + > + target_cpu->powered_off = true; > + target_cpu_state->halted = 1; > + target_cpu_state->exception_index = EXCP_HLT; > + cpu_loop_exit(target_cpu_state); > +} > > +static void imx6_src_cpu_reset(uint32_t cpuid) > +{ > + CPUState *target_cpu_state; > + ARMCPU *target_cpu; > + > + DPRINTF("cpu %d\n", cpuid); > + > + /* change to the cpu we are powering up */ > + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); > + if (!target_cpu_state) { > + return; > + } > + target_cpu = ARM_CPU(target_cpu_state); > + if (target_cpu->powered_off) { > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is off\n", > + TYPE_IMX6_SRC, __func__, cpuid); > + return; > + } > + > + /* Reset the cpu we are turning on */ > + cpu_reset(target_cpu_state); > +} The code that is messing about with target CPUs to power them up and down needs to be abstracted out into a public API in target-arm/, so it can be used both by your device and by target-arm/psci.c. I don't want variations on this code to duplicate into various devices, because chances are good it will need to change for multithreaded TCG. > +static void imx6_src_realize(DeviceState *dev, Error **errp) > +{ > + IMX6SRCState *s = IMX6_SRC(dev); > + > + memory_region_init_io(&s->iomem, OBJECT(dev), &imx6_src_ops, s, > + TYPE_IMX6_SRC, 0x1000); > + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); > +} > + > +static void imx6_src_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = imx6_src_realize; > + dc->reset = imx6_src_reset; > + dc->vmsd = &vmstate_imx6_src; > + dc->desc = "i.MX6 System Reset Controller"; > +} > +/* SRC_SCR */ > +#define CORE3_ENABLE_SHIFT (24) > +#define CORE3_ENABLE_LENGTH (1) > +#define CORE2_ENABLE_SHIFT (23) > +#define CORE2_ENABLE_LENGTH (1) > +#define CORE1_ENABLE_SHIFT (22) > +#define CORE1_ENABLE_LENGTH (1) > +#define CORE3_RST_SHIFT (16) > +#define CORE3_RST_LENGTH (1) > +#define CORE2_RST_SHIFT (15) > +#define CORE2_RST_LENGTH (1) > +#define CORE1_RST_SHIFT (14) > +#define CORE1_RST_LENGTH (1) > +#define CORE0_RST_SHIFT (13) > +#define CORE0_RST_LENGTH (1) > +#define SW_IPU1_RST_SHIFT (3) > +#define SW_IPU1_RST_LENGTH (1) > +#define SW_IPU2_RST_SHIFT (12) > +#define SW_IPU2_RST_LENGTH (1) > +#define WARM_RST_ENABLE_SHIFT (0) > +#define WARM_RST_ENABLE_LENGTH (1) The brackets here are unnecessary. thanks -- PMM
Hi Peter and Peter, I need to test that the changes I did for PSCI (factor out on/off code) do not introduce any regression. On which QEMU target should I test my changes to PSCI to check I didn't mess up anything? Thanks JC Le 16/02/2016 16:35, Peter Maydell a écrit : > On 8 February 2016 at 22:08, Jean-Christophe Dubois <jcd@tribudubois.net> wrote: >> This controller is also present in i.MX5X devices but they are not >> yet emulated by QEMU. >> >> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net> >> @@ -0,0 +1,353 @@ >> +/* >> + * IMX6 System Reset Controller >> + * >> + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> >> + * >> + * This work is licensed under the terms of the GNU GPL, version 2 or later. >> + * See the COPYING file in the top-level directory. >> + * >> + */ >> + >> +#include "hw/misc/imx6_src.h" >> +#include "sysemu/sysemu.h" >> +#include "qemu/bitops.h" > #include "qemu/osdep.h" as first #include line. > >> +static void imx6_src_reset(DeviceState *dev) >> +{ >> + IMX6SRCState *s = IMX6_SRC(dev); >> + >> + DPRINTF("\n"); >> + >> + /* >> + * We only clear the first registers as all GPR registers are preserved >> + * over resets >> + */ >> + memset(s->regs, 0, SRC_MAX * sizeof(uint32_t)); > Comment doesn't seem to match code? > >> + /* Set reset values */ >> + s->regs[SRC_SCR] = 0x521; >> + s->regs[SRC_SRSR] = 0x1; >> + s->regs[SRC_SIMR] = 0x1F; >> +} >> + >> +static CPUState *imx6_src_get_cpu_by_id(uint32_t id) >> +{ >> + CPUState *cpu; >> + >> + DPRINTF("cpu %d\n", id); >> + >> + CPU_FOREACH(cpu) { >> + ARMCPU *armcpu = ARM_CPU(cpu); >> + >> + if (armcpu->mp_affinity == id) { >> + return cpu; >> + } >> + } >> + >> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Resquesting unknown CPU %d\n", >> + TYPE_IMX6_SRC, __func__, id); >> + >> + return NULL; >> +} >> + >> +static void imx6_src_cpu_on(uint32_t cpuid, uint32_t entry, uint32_t context_id) >> +{ >> + CPUState *target_cpu_state; >> + ARMCPU *target_cpu; >> + CPUClass *target_cpu_class; >> + >> + DPRINTF("cpu %d @ 0x%08x with R0 = 0x%08x\n", cpuid, entry, context_id); >> + >> + /* change to the cpu we are powering up */ >> + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); >> + if (!target_cpu_state) { >> + return; >> + } >> + target_cpu = ARM_CPU(target_cpu_state); >> + if (!target_cpu->powered_off) { >> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is already running\n", >> + TYPE_IMX6_SRC, __func__, cpuid); >> + return; >> + } >> + target_cpu_class = CPU_GET_CLASS(target_cpu); >> + >> + /* Initialize the cpu we are turning on */ >> + cpu_reset(target_cpu_state); >> + target_cpu->powered_off = false; >> + target_cpu_state->halted = 0; >> + >> + target_cpu->env.regs[0] = context_id; >> + target_cpu->env.thumb = entry & 1; >> + >> + target_cpu_class->set_pc(target_cpu_state, entry); >> +} >> + >> +static void imx6_src_cpu_off(uint32_t cpuid) >> +{ >> + CPUState *target_cpu_state; >> + ARMCPU *target_cpu; >> + >> + DPRINTF("cpu %d\n", cpuid); >> + >> + /* change to the cpu we are powering up */ >> + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); >> + if (!target_cpu_state) { >> + return; >> + } >> + target_cpu = ARM_CPU(target_cpu_state); >> + if (target_cpu->powered_off) { >> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is already off\n", >> + TYPE_IMX6_SRC, __func__, cpuid); >> + return; >> + } >> + >> + target_cpu->powered_off = true; >> + target_cpu_state->halted = 1; >> + target_cpu_state->exception_index = EXCP_HLT; >> + cpu_loop_exit(target_cpu_state); >> +} >> >> +static void imx6_src_cpu_reset(uint32_t cpuid) >> +{ >> + CPUState *target_cpu_state; >> + ARMCPU *target_cpu; >> + >> + DPRINTF("cpu %d\n", cpuid); >> + >> + /* change to the cpu we are powering up */ >> + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); >> + if (!target_cpu_state) { >> + return; >> + } >> + target_cpu = ARM_CPU(target_cpu_state); >> + if (target_cpu->powered_off) { >> + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is off\n", >> + TYPE_IMX6_SRC, __func__, cpuid); >> + return; >> + } >> + >> + /* Reset the cpu we are turning on */ >> + cpu_reset(target_cpu_state); >> +} > The code that is messing about with target CPUs to power them up > and down needs to be abstracted out into a public API in target-arm/, > so it can be used both by your device and by target-arm/psci.c. > I don't want variations on this code to duplicate into various > devices, because chances are good it will need to change for > multithreaded TCG. > >> +static void imx6_src_realize(DeviceState *dev, Error **errp) >> +{ >> + IMX6SRCState *s = IMX6_SRC(dev); >> + >> + memory_region_init_io(&s->iomem, OBJECT(dev), &imx6_src_ops, s, >> + TYPE_IMX6_SRC, 0x1000); >> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); >> +} >> + >> +static void imx6_src_class_init(ObjectClass *klass, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + dc->realize = imx6_src_realize; >> + dc->reset = imx6_src_reset; >> + dc->vmsd = &vmstate_imx6_src; >> + dc->desc = "i.MX6 System Reset Controller"; >> +} >> +/* SRC_SCR */ >> +#define CORE3_ENABLE_SHIFT (24) >> +#define CORE3_ENABLE_LENGTH (1) >> +#define CORE2_ENABLE_SHIFT (23) >> +#define CORE2_ENABLE_LENGTH (1) >> +#define CORE1_ENABLE_SHIFT (22) >> +#define CORE1_ENABLE_LENGTH (1) >> +#define CORE3_RST_SHIFT (16) >> +#define CORE3_RST_LENGTH (1) >> +#define CORE2_RST_SHIFT (15) >> +#define CORE2_RST_LENGTH (1) >> +#define CORE1_RST_SHIFT (14) >> +#define CORE1_RST_LENGTH (1) >> +#define CORE0_RST_SHIFT (13) >> +#define CORE0_RST_LENGTH (1) >> +#define SW_IPU1_RST_SHIFT (3) >> +#define SW_IPU1_RST_LENGTH (1) >> +#define SW_IPU2_RST_SHIFT (12) >> +#define SW_IPU2_RST_LENGTH (1) >> +#define WARM_RST_ENABLE_SHIFT (0) >> +#define WARM_RST_ENABLE_LENGTH (1) > The brackets here are unnecessary. > > thanks > -- PMM >
On 27 February 2016 at 16:57, Jean-Christophe DUBOIS <jcd@tribudubois.net> wrote: > Hi Peter and Peter, > > I need to test that the changes I did for PSCI (factor out on/off code) do > not introduce any regression. > > On which QEMU target should I test my changes to PSCI to check I didn't mess > up anything? The 'virt' board uses PSCI. thanks -- PMM
Le 27/02/2016 18:43, Peter Maydell a écrit : > On 27 February 2016 at 16:57, Jean-Christophe DUBOIS > <jcd@tribudubois.net> wrote: >> Hi Peter and Peter, >> >> I need to test that the changes I did for PSCI (factor out on/off code) do >> not introduce any regression. >> >> On which QEMU target should I test my changes to PSCI to check I didn't mess >> up anything? > The 'virt' board uses PSCI. Is there some quick howto somewhere on the 'virt' plateform (which linux defconfig to use, which DT tree to use, which command line t use, ...) ? Thanks JC > > thanks > -- PMM >
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index a2a8e91..6bac654 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -29,6 +29,7 @@ obj-$(CONFIG_IMX) += imx_ccm.o obj-$(CONFIG_IMX) += imx31_ccm.o obj-$(CONFIG_IMX) += imx25_ccm.o obj-$(CONFIG_IMX) += imx6_ccm.o +obj-$(CONFIG_IMX) += imx6_src.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c new file mode 100644 index 0000000..2cf97ef --- /dev/null +++ b/hw/misc/imx6_src.c @@ -0,0 +1,353 @@ +/* + * IMX6 System Reset Controller + * + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "hw/misc/imx6_src.h" +#include "sysemu/sysemu.h" +#include "qemu/bitops.h" + +#ifndef DEBUG_IMX6_SRC +#define DEBUG_IMX6_SRC 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX6_SRC) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_SRC, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx6_src_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case SRC_SCR: + return "SRC_SCR"; + case SRC_SBMR1: + return "SRC_SBMR1"; + case SRC_SRSR: + return "SRC_SRSR"; + case SRC_SISR: + return "SRC_SISR"; + case SRC_SIMR: + return "SRC_SIMR"; + case SRC_SBMR2: + return "SRC_SBMR2"; + case SRC_GPR1: + return "SRC_GPR1"; + case SRC_GPR2: + return "SRC_GPR2"; + case SRC_GPR3: + return "SRC_GPR3"; + case SRC_GPR4: + return "SRC_GPR4"; + case SRC_GPR5: + return "SRC_GPR5"; + case SRC_GPR6: + return "SRC_GPR6"; + case SRC_GPR7: + return "SRC_GPR7"; + case SRC_GPR8: + return "SRC_GPR8"; + case SRC_GPR9: + return "SRC_GPR9"; + case SRC_GPR10: + return "SRC_GPR10"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +static const VMStateDescription vmstate_imx6_src = { + .name = TYPE_IMX6_SRC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IMX6SRCState, SRC_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx6_src_reset(DeviceState *dev) +{ + IMX6SRCState *s = IMX6_SRC(dev); + + DPRINTF("\n"); + + /* + * We only clear the first registers as all GPR registers are preserved + * over resets + */ + memset(s->regs, 0, SRC_MAX * sizeof(uint32_t)); + + /* Set reset values */ + s->regs[SRC_SCR] = 0x521; + s->regs[SRC_SRSR] = 0x1; + s->regs[SRC_SIMR] = 0x1F; +} + +static CPUState *imx6_src_get_cpu_by_id(uint32_t id) +{ + CPUState *cpu; + + DPRINTF("cpu %d\n", id); + + CPU_FOREACH(cpu) { + ARMCPU *armcpu = ARM_CPU(cpu); + + if (armcpu->mp_affinity == id) { + return cpu; + } + } + + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Resquesting unknown CPU %d\n", + TYPE_IMX6_SRC, __func__, id); + + return NULL; +} + +static void imx6_src_cpu_on(uint32_t cpuid, uint32_t entry, uint32_t context_id) +{ + CPUState *target_cpu_state; + ARMCPU *target_cpu; + CPUClass *target_cpu_class; + + DPRINTF("cpu %d @ 0x%08x with R0 = 0x%08x\n", cpuid, entry, context_id); + + /* change to the cpu we are powering up */ + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); + if (!target_cpu_state) { + return; + } + target_cpu = ARM_CPU(target_cpu_state); + if (!target_cpu->powered_off) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is already running\n", + TYPE_IMX6_SRC, __func__, cpuid); + return; + } + target_cpu_class = CPU_GET_CLASS(target_cpu); + + /* Initialize the cpu we are turning on */ + cpu_reset(target_cpu_state); + target_cpu->powered_off = false; + target_cpu_state->halted = 0; + + target_cpu->env.regs[0] = context_id; + target_cpu->env.thumb = entry & 1; + + target_cpu_class->set_pc(target_cpu_state, entry); +} + +static void imx6_src_cpu_off(uint32_t cpuid) +{ + CPUState *target_cpu_state; + ARMCPU *target_cpu; + + DPRINTF("cpu %d\n", cpuid); + + /* change to the cpu we are powering up */ + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); + if (!target_cpu_state) { + return; + } + target_cpu = ARM_CPU(target_cpu_state); + if (target_cpu->powered_off) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is already off\n", + TYPE_IMX6_SRC, __func__, cpuid); + return; + } + + target_cpu->powered_off = true; + target_cpu_state->halted = 1; + target_cpu_state->exception_index = EXCP_HLT; + cpu_loop_exit(target_cpu_state); +} + +static void imx6_src_cpu_reset(uint32_t cpuid) +{ + CPUState *target_cpu_state; + ARMCPU *target_cpu; + + DPRINTF("cpu %d\n", cpuid); + + /* change to the cpu we are powering up */ + target_cpu_state = imx6_src_get_cpu_by_id(cpuid); + if (!target_cpu_state) { + return; + } + target_cpu = ARM_CPU(target_cpu_state); + if (target_cpu->powered_off) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: CPU %d is off\n", + TYPE_IMX6_SRC, __func__, cpuid); + return; + } + + /* Reset the cpu we are turning on */ + cpu_reset(target_cpu_state); +} + +static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32 value = 0; + IMX6SRCState *s = (IMX6SRCState *)opaque; + uint32_t index = offset >> 2; + + if (index < SRC_MAX) { + value = s->regs[index]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX6_SRC, __func__, offset); + + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_src_reg_name(index), value); + + return (uint64_t)value; +} + +static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMX6SRCState *s = (IMX6SRCState *)opaque; + uint32_t index = offset >> 2; + uint64_t change_mask; + + if (index >= SRC_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX6_SRC, __func__, offset); + return; + } + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_src_reg_name(index), + (uint32_t)value); + + change_mask = s->regs[index] ^ (uint32_t)value; + + switch (index) { + case SRC_SCR: + if (EXTRACT(change_mask, CORE3_ENABLE)) { + if (EXTRACT(value, CORE3_ENABLE)) { + /* CORE 3 is brought up */ + imx6_src_cpu_on(3, s->regs[SRC_GPR7], s->regs[SRC_GPR8]); + } else { + /* CORE 3 is shut down */ + imx6_src_cpu_off(3); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE3_RST_SHIFT, &value); + clear_bit(CORE3_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE2_ENABLE)) { + if (EXTRACT(value, CORE2_ENABLE)) { + /* CORE 2 is brought up */ + imx6_src_cpu_on(2, s->regs[SRC_GPR5], s->regs[SRC_GPR6]); + } else { + /* CORE 3 is shut down */ + imx6_src_cpu_off(2); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE2_RST_SHIFT, &value); + clear_bit(CORE2_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE1_ENABLE)) { + if (EXTRACT(value, CORE1_ENABLE)) { + /* CORE 1 is brought up */ + imx6_src_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4]); + } else { + /* CORE 3 is shut down */ + imx6_src_cpu_off(1); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE1_RST_SHIFT, &value); + clear_bit(CORE1_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE0_RST)) { + imx6_src_cpu_reset(0); + clear_bit(CORE0_RST_SHIFT, &value); + } + if (EXTRACT(change_mask, CORE1_RST)) { + imx6_src_cpu_reset(1); + clear_bit(CORE1_RST_SHIFT, &value); + } + if (EXTRACT(change_mask, CORE2_RST)) { + imx6_src_cpu_reset(2); + clear_bit(CORE2_RST_SHIFT, &value); + } + if (EXTRACT(change_mask, CORE3_RST)) { + imx6_src_cpu_reset(3); + clear_bit(CORE3_RST_SHIFT, &value); + } + if (EXTRACT(change_mask, SW_IPU2_RST)) { + /* We pretend the IPU2 is reseted */ + clear_bit(SW_IPU2_RST_SHIFT, &value); + } + if (EXTRACT(change_mask, SW_IPU1_RST)) { + /* We pretend the IPU1 is reseted */ + clear_bit(SW_IPU1_RST_SHIFT, &value); + } + s->regs[index] = value; + break; + default: + s->regs[index] = value; + break; + } +} + +static const struct MemoryRegionOps imx6_src_ops = { + .read = imx6_src_read, + .write = imx6_src_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * 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 imx6_src_realize(DeviceState *dev, Error **errp) +{ + IMX6SRCState *s = IMX6_SRC(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx6_src_ops, s, + TYPE_IMX6_SRC, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); +} + +static void imx6_src_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx6_src_realize; + dc->reset = imx6_src_reset; + dc->vmsd = &vmstate_imx6_src; + dc->desc = "i.MX6 System Reset Controller"; +} + +static const TypeInfo imx6_src_info = { + .name = TYPE_IMX6_SRC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX6SRCState), + .class_init = imx6_src_class_init, +}; + +static void imx6_src_register_types(void) +{ + type_register_static(&imx6_src_info); +} + +type_init(imx6_src_register_types) diff --git a/include/hw/misc/imx6_src.h b/include/hw/misc/imx6_src.h new file mode 100644 index 0000000..c669488 --- /dev/null +++ b/include/hw/misc/imx6_src.h @@ -0,0 +1,73 @@ +/* + * IMX6 System Reset Controller + * + * Copyright (C) 2012 NICTA + * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX6_SRC_H +#define IMX6_SRC_H + +#include "hw/sysbus.h" +#include "qemu/bitops.h" + +#define SRC_SCR 0 +#define SRC_SBMR1 1 +#define SRC_SRSR 2 +#define SRC_SISR 5 +#define SRC_SIMR 6 +#define SRC_SBMR2 7 +#define SRC_GPR1 8 +#define SRC_GPR2 9 +#define SRC_GPR3 10 +#define SRC_GPR4 11 +#define SRC_GPR5 12 +#define SRC_GPR6 13 +#define SRC_GPR7 14 +#define SRC_GPR8 15 +#define SRC_GPR9 16 +#define SRC_GPR10 17 +#define SRC_MAX 18 + +/* SRC_SCR */ +#define CORE3_ENABLE_SHIFT (24) +#define CORE3_ENABLE_LENGTH (1) +#define CORE2_ENABLE_SHIFT (23) +#define CORE2_ENABLE_LENGTH (1) +#define CORE1_ENABLE_SHIFT (22) +#define CORE1_ENABLE_LENGTH (1) +#define CORE3_RST_SHIFT (16) +#define CORE3_RST_LENGTH (1) +#define CORE2_RST_SHIFT (15) +#define CORE2_RST_LENGTH (1) +#define CORE1_RST_SHIFT (14) +#define CORE1_RST_LENGTH (1) +#define CORE0_RST_SHIFT (13) +#define CORE0_RST_LENGTH (1) +#define SW_IPU1_RST_SHIFT (3) +#define SW_IPU1_RST_LENGTH (1) +#define SW_IPU2_RST_SHIFT (12) +#define SW_IPU2_RST_LENGTH (1) +#define WARM_RST_ENABLE_SHIFT (0) +#define WARM_RST_ENABLE_LENGTH (1) + +#define EXTRACT(value, name) extract32(value, name##_SHIFT, name##_LENGTH) + +#define TYPE_IMX6_SRC "imx6.src" +#define IMX6_SRC(obj) OBJECT_CHECK(IMX6SRCState, (obj), TYPE_IMX6_SRC) + +typedef struct IMX6SRCState { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + + uint32_t regs[SRC_MAX]; + +} IMX6SRCState; + +#endif /* IMX6_SRC_H */
This controller is also present in i.MX5X devices but they are not yet emulated by QEMU. Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net> --- Changes since V1: * Change "reset" sematic to mean full power cyvle. hw/misc/Makefile.objs | 1 + hw/misc/imx6_src.c | 353 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/misc/imx6_src.h | 73 ++++++++++ 3 files changed, 427 insertions(+) create mode 100644 hw/misc/imx6_src.c create mode 100644 include/hw/misc/imx6_src.h