mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 03:59:52 +00:00
i.MX: Add i.MX6 System Reset Controller device.
This controller is also present in i.MX5X devices but they are not yet emulated by QEMU. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
825482adde
commit
1983057470
@ -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
|
||||
|
264
hw/misc/imx6_src.c
Normal file
264
hw/misc/imx6_src.c
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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 "qemu/osdep.h"
|
||||
#include "hw/misc/imx6_src.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "arm-powerctl.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");
|
||||
|
||||
memset(s->regs, 0, sizeof(s->regs));
|
||||
|
||||
/* Set reset values */
|
||||
s->regs[SRC_SCR] = 0x521;
|
||||
s->regs[SRC_SRSR] = 0x1;
|
||||
s->regs[SRC_SIMR] = 0x1F;
|
||||
}
|
||||
|
||||
static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
uint32_t 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 value;
|
||||
}
|
||||
|
||||
static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
IMX6SRCState *s = (IMX6SRCState *)opaque;
|
||||
uint32_t index = offset >> 2;
|
||||
unsigned long change_mask;
|
||||
unsigned long current_value = value;
|
||||
|
||||
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)current_value);
|
||||
|
||||
change_mask = s->regs[index] ^ (uint32_t)current_value;
|
||||
|
||||
switch (index) {
|
||||
case SRC_SCR:
|
||||
/*
|
||||
* On real hardware when the system reset controller starts a
|
||||
* secondary CPU it runs through some boot ROM code which reads
|
||||
* the SRC_GPRX registers controlling the start address and branches
|
||||
* to it.
|
||||
* Here we are taking a short cut and branching directly to the
|
||||
* requested address (we don't want to run the boot ROM code inside
|
||||
* QEMU)
|
||||
*/
|
||||
if (EXTRACT(change_mask, CORE3_ENABLE)) {
|
||||
if (EXTRACT(current_value, CORE3_ENABLE)) {
|
||||
/* CORE 3 is brought up */
|
||||
arm_set_cpu_on(3, s->regs[SRC_GPR7], s->regs[SRC_GPR8],
|
||||
3, false);
|
||||
} else {
|
||||
/* CORE 3 is shut down */
|
||||
arm_set_cpu_off(3);
|
||||
}
|
||||
/* We clear the reset bits as the processor changed state */
|
||||
clear_bit(CORE3_RST_SHIFT, ¤t_value);
|
||||
clear_bit(CORE3_RST_SHIFT, &change_mask);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE2_ENABLE)) {
|
||||
if (EXTRACT(current_value, CORE2_ENABLE)) {
|
||||
/* CORE 2 is brought up */
|
||||
arm_set_cpu_on(2, s->regs[SRC_GPR5], s->regs[SRC_GPR6],
|
||||
3, false);
|
||||
} else {
|
||||
/* CORE 3 is shut down */
|
||||
arm_set_cpu_off(2);
|
||||
}
|
||||
/* We clear the reset bits as the processor changed state */
|
||||
clear_bit(CORE2_RST_SHIFT, ¤t_value);
|
||||
clear_bit(CORE2_RST_SHIFT, &change_mask);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE1_ENABLE)) {
|
||||
if (EXTRACT(current_value, CORE1_ENABLE)) {
|
||||
/* CORE 1 is brought up */
|
||||
arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4],
|
||||
3, false);
|
||||
} else {
|
||||
/* CORE 3 is shut down */
|
||||
arm_set_cpu_off(1);
|
||||
}
|
||||
/* We clear the reset bits as the processor changed state */
|
||||
clear_bit(CORE1_RST_SHIFT, ¤t_value);
|
||||
clear_bit(CORE1_RST_SHIFT, &change_mask);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE0_RST)) {
|
||||
arm_reset_cpu(0);
|
||||
clear_bit(CORE0_RST_SHIFT, ¤t_value);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE1_RST)) {
|
||||
arm_reset_cpu(1);
|
||||
clear_bit(CORE1_RST_SHIFT, ¤t_value);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE2_RST)) {
|
||||
arm_reset_cpu(2);
|
||||
clear_bit(CORE2_RST_SHIFT, ¤t_value);
|
||||
}
|
||||
if (EXTRACT(change_mask, CORE3_RST)) {
|
||||
arm_reset_cpu(3);
|
||||
clear_bit(CORE3_RST_SHIFT, ¤t_value);
|
||||
}
|
||||
if (EXTRACT(change_mask, SW_IPU2_RST)) {
|
||||
/* We pretend the IPU2 is reset */
|
||||
clear_bit(SW_IPU2_RST_SHIFT, ¤t_value);
|
||||
}
|
||||
if (EXTRACT(change_mask, SW_IPU1_RST)) {
|
||||
/* We pretend the IPU1 is reset */
|
||||
clear_bit(SW_IPU1_RST_SHIFT, ¤t_value);
|
||||
}
|
||||
s->regs[index] = current_value;
|
||||
break;
|
||||
default:
|
||||
s->regs[index] = current_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)
|
73
include/hw/misc/imx6_src.h
Normal file
73
include/hw/misc/imx6_src.h
Normal file
@ -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 */
|
Loading…
Reference in New Issue
Block a user