diff options
Diffstat (limited to 'hw/misc')
-rw-r--r-- | hw/misc/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/misc/bcm2835_property.c | 33 | ||||
-rw-r--r-- | hw/misc/imx6_src.c | 264 |
3 files changed, 289 insertions, 9 deletions
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 93f952880a..bc0dd2cc75 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/bcm2835_property.c b/hw/misc/bcm2835_property.c index 530411f841..34473469d4 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -21,6 +21,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) int n; uint32_t offset, length, color; uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; + uint32_t tmp_xres, tmp_yres, tmp_xoffset, tmp_yoffset; + uint32_t tmp_bpp, tmp_pixo, tmp_alpha; uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; @@ -139,7 +141,11 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00040001: /* Allocate buffer */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->size); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 16, + tmp_xres * tmp_yres * tmp_bpp / 8); resplen = 8; break; case 0x00048001: /* Release buffer */ @@ -150,8 +156,10 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; case 0x00040003: /* Get display width/height */ case 0x00040004: - stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; + stl_le_phys(&s->dma_as, value + 12, tmp_xres); + stl_le_phys(&s->dma_as, value + 16, tmp_yres); resplen = 8; break; case 0x00044003: /* Test display width/height */ @@ -167,7 +175,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; case 0x00040005: /* Get depth */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp); + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, tmp_bpp); resplen = 4; break; case 0x00044005: /* Test depth */ @@ -179,7 +188,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040006: /* Get pixel order */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo); + tmp_pixo = newpixo != NULL ? *newpixo : s->fbdev->pixo; + stl_le_phys(&s->dma_as, value + 12, tmp_pixo); resplen = 4; break; case 0x00044006: /* Test pixel order */ @@ -191,7 +201,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040007: /* Get alpha */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha); + tmp_alpha = newalpha != NULL ? *newalpha : s->fbdev->alpha; + stl_le_phys(&s->dma_as, value + 12, tmp_alpha); resplen = 4; break; case 0x00044007: /* Test pixel alpha */ @@ -203,12 +214,16 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040008: /* Get pitch */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, tmp_xres * tmp_bpp / 8); resplen = 4; break; case 0x00040009: /* Get virtual offset */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset); + tmp_xoffset = newxoffset != NULL ? *newxoffset : s->fbdev->xoffset; + tmp_yoffset = newyoffset != NULL ? *newyoffset : s->fbdev->yoffset; + stl_le_phys(&s->dma_as, value + 12, tmp_xoffset); + stl_le_phys(&s->dma_as, value + 16, tmp_yoffset); resplen = 8; break; case 0x00044009: /* Test virtual offset */ diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c new file mode 100644 index 0000000000..6b026b459f --- /dev/null +++ b/hw/misc/imx6_src.c @@ -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) |