112517bc9SJean-Christophe Dubois /* 212517bc9SJean-Christophe Dubois * IMX7 System Reset Controller 312517bc9SJean-Christophe Dubois * 412517bc9SJean-Christophe Dubois * Copyright (c) 2023 Jean-Christophe Dubois <jcd@tribudubois.net> 512517bc9SJean-Christophe Dubois * 612517bc9SJean-Christophe Dubois * This work is licensed under the terms of the GNU GPL, version 2 or later. 712517bc9SJean-Christophe Dubois * See the COPYING file in the top-level directory. 812517bc9SJean-Christophe Dubois * 912517bc9SJean-Christophe Dubois */ 1012517bc9SJean-Christophe Dubois 1112517bc9SJean-Christophe Dubois #include "qemu/osdep.h" 1212517bc9SJean-Christophe Dubois #include "hw/misc/imx7_src.h" 1312517bc9SJean-Christophe Dubois #include "migration/vmstate.h" 1412517bc9SJean-Christophe Dubois #include "qemu/bitops.h" 1512517bc9SJean-Christophe Dubois #include "qemu/log.h" 1612517bc9SJean-Christophe Dubois #include "qemu/main-loop.h" 1712517bc9SJean-Christophe Dubois #include "qemu/module.h" 1812517bc9SJean-Christophe Dubois #include "target/arm/arm-powerctl.h" 1912517bc9SJean-Christophe Dubois #include "hw/core/cpu.h" 2012517bc9SJean-Christophe Dubois #include "hw/registerfields.h" 2112517bc9SJean-Christophe Dubois 2212517bc9SJean-Christophe Dubois #include "trace.h" 2312517bc9SJean-Christophe Dubois 2412517bc9SJean-Christophe Dubois static const char *imx7_src_reg_name(uint32_t reg) 2512517bc9SJean-Christophe Dubois { 2612517bc9SJean-Christophe Dubois static char unknown[20]; 2712517bc9SJean-Christophe Dubois 2812517bc9SJean-Christophe Dubois switch (reg) { 2912517bc9SJean-Christophe Dubois case SRC_SCR: 3012517bc9SJean-Christophe Dubois return "SRC_SCR"; 3112517bc9SJean-Christophe Dubois case SRC_A7RCR0: 3212517bc9SJean-Christophe Dubois return "SRC_A7RCR0"; 3312517bc9SJean-Christophe Dubois case SRC_A7RCR1: 3412517bc9SJean-Christophe Dubois return "SRC_A7RCR1"; 3512517bc9SJean-Christophe Dubois case SRC_M4RCR: 3612517bc9SJean-Christophe Dubois return "SRC_M4RCR"; 3712517bc9SJean-Christophe Dubois case SRC_ERCR: 3812517bc9SJean-Christophe Dubois return "SRC_ERCR"; 3912517bc9SJean-Christophe Dubois case SRC_HSICPHY_RCR: 4012517bc9SJean-Christophe Dubois return "SRC_HSICPHY_RCR"; 4112517bc9SJean-Christophe Dubois case SRC_USBOPHY1_RCR: 4212517bc9SJean-Christophe Dubois return "SRC_USBOPHY1_RCR"; 4312517bc9SJean-Christophe Dubois case SRC_USBOPHY2_RCR: 4412517bc9SJean-Christophe Dubois return "SRC_USBOPHY2_RCR"; 4512517bc9SJean-Christophe Dubois case SRC_PCIEPHY_RCR: 4612517bc9SJean-Christophe Dubois return "SRC_PCIEPHY_RCR"; 4712517bc9SJean-Christophe Dubois case SRC_SBMR1: 4812517bc9SJean-Christophe Dubois return "SRC_SBMR1"; 4912517bc9SJean-Christophe Dubois case SRC_SRSR: 5012517bc9SJean-Christophe Dubois return "SRC_SRSR"; 5112517bc9SJean-Christophe Dubois case SRC_SISR: 5212517bc9SJean-Christophe Dubois return "SRC_SISR"; 5312517bc9SJean-Christophe Dubois case SRC_SIMR: 5412517bc9SJean-Christophe Dubois return "SRC_SIMR"; 5512517bc9SJean-Christophe Dubois case SRC_SBMR2: 5612517bc9SJean-Christophe Dubois return "SRC_SBMR2"; 5712517bc9SJean-Christophe Dubois case SRC_GPR1: 5812517bc9SJean-Christophe Dubois return "SRC_GPR1"; 5912517bc9SJean-Christophe Dubois case SRC_GPR2: 6012517bc9SJean-Christophe Dubois return "SRC_GPR2"; 6112517bc9SJean-Christophe Dubois case SRC_GPR3: 6212517bc9SJean-Christophe Dubois return "SRC_GPR3"; 6312517bc9SJean-Christophe Dubois case SRC_GPR4: 6412517bc9SJean-Christophe Dubois return "SRC_GPR4"; 6512517bc9SJean-Christophe Dubois case SRC_GPR5: 6612517bc9SJean-Christophe Dubois return "SRC_GPR5"; 6712517bc9SJean-Christophe Dubois case SRC_GPR6: 6812517bc9SJean-Christophe Dubois return "SRC_GPR6"; 6912517bc9SJean-Christophe Dubois case SRC_GPR7: 7012517bc9SJean-Christophe Dubois return "SRC_GPR7"; 7112517bc9SJean-Christophe Dubois case SRC_GPR8: 7212517bc9SJean-Christophe Dubois return "SRC_GPR8"; 7312517bc9SJean-Christophe Dubois case SRC_GPR9: 7412517bc9SJean-Christophe Dubois return "SRC_GPR9"; 7512517bc9SJean-Christophe Dubois case SRC_GPR10: 7612517bc9SJean-Christophe Dubois return "SRC_GPR10"; 7712517bc9SJean-Christophe Dubois default: 7812517bc9SJean-Christophe Dubois sprintf(unknown, "%u ?", reg); 7912517bc9SJean-Christophe Dubois return unknown; 8012517bc9SJean-Christophe Dubois } 8112517bc9SJean-Christophe Dubois } 8212517bc9SJean-Christophe Dubois 8312517bc9SJean-Christophe Dubois static const VMStateDescription vmstate_imx7_src = { 8412517bc9SJean-Christophe Dubois .name = TYPE_IMX7_SRC, 8512517bc9SJean-Christophe Dubois .version_id = 1, 8612517bc9SJean-Christophe Dubois .minimum_version_id = 1, 87*e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 8812517bc9SJean-Christophe Dubois VMSTATE_UINT32_ARRAY(regs, IMX7SRCState, SRC_MAX), 8912517bc9SJean-Christophe Dubois VMSTATE_END_OF_LIST() 9012517bc9SJean-Christophe Dubois }, 9112517bc9SJean-Christophe Dubois }; 9212517bc9SJean-Christophe Dubois 9312517bc9SJean-Christophe Dubois static void imx7_src_reset(DeviceState *dev) 9412517bc9SJean-Christophe Dubois { 9512517bc9SJean-Christophe Dubois IMX7SRCState *s = IMX7_SRC(dev); 9612517bc9SJean-Christophe Dubois 9712517bc9SJean-Christophe Dubois memset(s->regs, 0, sizeof(s->regs)); 9812517bc9SJean-Christophe Dubois 9912517bc9SJean-Christophe Dubois /* Set reset values */ 10012517bc9SJean-Christophe Dubois s->regs[SRC_SCR] = 0xA0; 10112517bc9SJean-Christophe Dubois s->regs[SRC_SRSR] = 0x1; 10212517bc9SJean-Christophe Dubois s->regs[SRC_SIMR] = 0x1F; 10312517bc9SJean-Christophe Dubois } 10412517bc9SJean-Christophe Dubois 10512517bc9SJean-Christophe Dubois static uint64_t imx7_src_read(void *opaque, hwaddr offset, unsigned size) 10612517bc9SJean-Christophe Dubois { 10712517bc9SJean-Christophe Dubois uint32_t value = 0; 10812517bc9SJean-Christophe Dubois IMX7SRCState *s = (IMX7SRCState *)opaque; 10912517bc9SJean-Christophe Dubois uint32_t index = offset >> 2; 11012517bc9SJean-Christophe Dubois 11112517bc9SJean-Christophe Dubois if (index < SRC_MAX) { 11212517bc9SJean-Christophe Dubois value = s->regs[index]; 11312517bc9SJean-Christophe Dubois } else { 11412517bc9SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" 11512517bc9SJean-Christophe Dubois HWADDR_PRIx "\n", TYPE_IMX7_SRC, __func__, offset); 11612517bc9SJean-Christophe Dubois } 11712517bc9SJean-Christophe Dubois 11812517bc9SJean-Christophe Dubois trace_imx7_src_read(imx7_src_reg_name(index), value); 11912517bc9SJean-Christophe Dubois 12012517bc9SJean-Christophe Dubois return value; 12112517bc9SJean-Christophe Dubois } 12212517bc9SJean-Christophe Dubois 12312517bc9SJean-Christophe Dubois 12412517bc9SJean-Christophe Dubois /* 12512517bc9SJean-Christophe Dubois * The reset is asynchronous so we need to defer clearing the reset 12612517bc9SJean-Christophe Dubois * bit until the work is completed. 12712517bc9SJean-Christophe Dubois */ 12812517bc9SJean-Christophe Dubois 12912517bc9SJean-Christophe Dubois struct SRCSCRResetInfo { 13012517bc9SJean-Christophe Dubois IMX7SRCState *s; 13112517bc9SJean-Christophe Dubois uint32_t reset_bit; 13212517bc9SJean-Christophe Dubois }; 13312517bc9SJean-Christophe Dubois 13412517bc9SJean-Christophe Dubois static void imx7_clear_reset_bit(CPUState *cpu, run_on_cpu_data data) 13512517bc9SJean-Christophe Dubois { 13612517bc9SJean-Christophe Dubois struct SRCSCRResetInfo *ri = data.host_ptr; 13712517bc9SJean-Christophe Dubois IMX7SRCState *s = ri->s; 13812517bc9SJean-Christophe Dubois 13912517bc9SJean-Christophe Dubois assert(qemu_mutex_iothread_locked()); 14012517bc9SJean-Christophe Dubois 14112517bc9SJean-Christophe Dubois s->regs[SRC_A7RCR0] = deposit32(s->regs[SRC_A7RCR0], ri->reset_bit, 1, 0); 14212517bc9SJean-Christophe Dubois 14312517bc9SJean-Christophe Dubois trace_imx7_src_write(imx7_src_reg_name(SRC_A7RCR0), s->regs[SRC_A7RCR0]); 14412517bc9SJean-Christophe Dubois 14512517bc9SJean-Christophe Dubois g_free(ri); 14612517bc9SJean-Christophe Dubois } 14712517bc9SJean-Christophe Dubois 14812517bc9SJean-Christophe Dubois static void imx7_defer_clear_reset_bit(uint32_t cpuid, 14912517bc9SJean-Christophe Dubois IMX7SRCState *s, 15012517bc9SJean-Christophe Dubois uint32_t reset_shift) 15112517bc9SJean-Christophe Dubois { 15212517bc9SJean-Christophe Dubois struct SRCSCRResetInfo *ri; 15312517bc9SJean-Christophe Dubois CPUState *cpu = arm_get_cpu_by_id(cpuid); 15412517bc9SJean-Christophe Dubois 15512517bc9SJean-Christophe Dubois if (!cpu) { 15612517bc9SJean-Christophe Dubois return; 15712517bc9SJean-Christophe Dubois } 15812517bc9SJean-Christophe Dubois 15912517bc9SJean-Christophe Dubois ri = g_new(struct SRCSCRResetInfo, 1); 16012517bc9SJean-Christophe Dubois ri->s = s; 16112517bc9SJean-Christophe Dubois ri->reset_bit = reset_shift; 16212517bc9SJean-Christophe Dubois 16312517bc9SJean-Christophe Dubois async_run_on_cpu(cpu, imx7_clear_reset_bit, RUN_ON_CPU_HOST_PTR(ri)); 16412517bc9SJean-Christophe Dubois } 16512517bc9SJean-Christophe Dubois 16612517bc9SJean-Christophe Dubois 16712517bc9SJean-Christophe Dubois static void imx7_src_write(void *opaque, hwaddr offset, uint64_t value, 16812517bc9SJean-Christophe Dubois unsigned size) 16912517bc9SJean-Christophe Dubois { 17012517bc9SJean-Christophe Dubois IMX7SRCState *s = (IMX7SRCState *)opaque; 17112517bc9SJean-Christophe Dubois uint32_t index = offset >> 2; 17212517bc9SJean-Christophe Dubois long unsigned int change_mask; 17312517bc9SJean-Christophe Dubois uint32_t current_value = value; 17412517bc9SJean-Christophe Dubois 17512517bc9SJean-Christophe Dubois if (index >= SRC_MAX) { 17612517bc9SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" 17712517bc9SJean-Christophe Dubois HWADDR_PRIx "\n", TYPE_IMX7_SRC, __func__, offset); 17812517bc9SJean-Christophe Dubois return; 17912517bc9SJean-Christophe Dubois } 18012517bc9SJean-Christophe Dubois 18112517bc9SJean-Christophe Dubois trace_imx7_src_write(imx7_src_reg_name(SRC_A7RCR0), s->regs[SRC_A7RCR0]); 18212517bc9SJean-Christophe Dubois 18312517bc9SJean-Christophe Dubois change_mask = s->regs[index] ^ (uint32_t)current_value; 18412517bc9SJean-Christophe Dubois 18512517bc9SJean-Christophe Dubois switch (index) { 18612517bc9SJean-Christophe Dubois case SRC_A7RCR0: 18712517bc9SJean-Christophe Dubois if (FIELD_EX32(change_mask, CORE0, RST)) { 18812517bc9SJean-Christophe Dubois arm_reset_cpu(0); 18912517bc9SJean-Christophe Dubois imx7_defer_clear_reset_bit(0, s, R_CORE0_RST_SHIFT); 19012517bc9SJean-Christophe Dubois } 19112517bc9SJean-Christophe Dubois if (FIELD_EX32(change_mask, CORE1, RST)) { 19212517bc9SJean-Christophe Dubois arm_reset_cpu(1); 19312517bc9SJean-Christophe Dubois imx7_defer_clear_reset_bit(1, s, R_CORE1_RST_SHIFT); 19412517bc9SJean-Christophe Dubois } 19512517bc9SJean-Christophe Dubois s->regs[index] = current_value; 19612517bc9SJean-Christophe Dubois break; 19712517bc9SJean-Christophe Dubois case SRC_A7RCR1: 19812517bc9SJean-Christophe Dubois /* 19912517bc9SJean-Christophe Dubois * On real hardware when the system reset controller starts a 20012517bc9SJean-Christophe Dubois * secondary CPU it runs through some boot ROM code which reads 20112517bc9SJean-Christophe Dubois * the SRC_GPRX registers controlling the start address and branches 20212517bc9SJean-Christophe Dubois * to it. 20312517bc9SJean-Christophe Dubois * Here we are taking a short cut and branching directly to the 20412517bc9SJean-Christophe Dubois * requested address (we don't want to run the boot ROM code inside 20512517bc9SJean-Christophe Dubois * QEMU) 20612517bc9SJean-Christophe Dubois */ 20712517bc9SJean-Christophe Dubois if (FIELD_EX32(change_mask, CORE1, ENABLE)) { 20812517bc9SJean-Christophe Dubois if (FIELD_EX32(current_value, CORE1, ENABLE)) { 20912517bc9SJean-Christophe Dubois /* CORE 1 is brought up */ 21012517bc9SJean-Christophe Dubois arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4], 21112517bc9SJean-Christophe Dubois 3, false); 21212517bc9SJean-Christophe Dubois } else { 21312517bc9SJean-Christophe Dubois /* CORE 1 is shut down */ 21412517bc9SJean-Christophe Dubois arm_set_cpu_off(1); 21512517bc9SJean-Christophe Dubois } 21612517bc9SJean-Christophe Dubois /* We clear the reset bits as the processor changed state */ 21712517bc9SJean-Christophe Dubois imx7_defer_clear_reset_bit(1, s, R_CORE1_RST_SHIFT); 21812517bc9SJean-Christophe Dubois clear_bit(R_CORE1_RST_SHIFT, &change_mask); 21912517bc9SJean-Christophe Dubois } 22012517bc9SJean-Christophe Dubois s->regs[index] = current_value; 22112517bc9SJean-Christophe Dubois break; 22212517bc9SJean-Christophe Dubois default: 22312517bc9SJean-Christophe Dubois s->regs[index] = current_value; 22412517bc9SJean-Christophe Dubois break; 22512517bc9SJean-Christophe Dubois } 22612517bc9SJean-Christophe Dubois } 22712517bc9SJean-Christophe Dubois 22812517bc9SJean-Christophe Dubois static const struct MemoryRegionOps imx7_src_ops = { 22912517bc9SJean-Christophe Dubois .read = imx7_src_read, 23012517bc9SJean-Christophe Dubois .write = imx7_src_write, 23112517bc9SJean-Christophe Dubois .endianness = DEVICE_NATIVE_ENDIAN, 23212517bc9SJean-Christophe Dubois .valid = { 23312517bc9SJean-Christophe Dubois /* 23412517bc9SJean-Christophe Dubois * Our device would not work correctly if the guest was doing 23512517bc9SJean-Christophe Dubois * unaligned access. This might not be a limitation on the real 23612517bc9SJean-Christophe Dubois * device but in practice there is no reason for a guest to access 23712517bc9SJean-Christophe Dubois * this device unaligned. 23812517bc9SJean-Christophe Dubois */ 23912517bc9SJean-Christophe Dubois .min_access_size = 4, 24012517bc9SJean-Christophe Dubois .max_access_size = 4, 24112517bc9SJean-Christophe Dubois .unaligned = false, 24212517bc9SJean-Christophe Dubois }, 24312517bc9SJean-Christophe Dubois }; 24412517bc9SJean-Christophe Dubois 24512517bc9SJean-Christophe Dubois static void imx7_src_realize(DeviceState *dev, Error **errp) 24612517bc9SJean-Christophe Dubois { 24712517bc9SJean-Christophe Dubois IMX7SRCState *s = IMX7_SRC(dev); 24812517bc9SJean-Christophe Dubois 24912517bc9SJean-Christophe Dubois memory_region_init_io(&s->iomem, OBJECT(dev), &imx7_src_ops, s, 25012517bc9SJean-Christophe Dubois TYPE_IMX7_SRC, 0x1000); 25112517bc9SJean-Christophe Dubois sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); 25212517bc9SJean-Christophe Dubois } 25312517bc9SJean-Christophe Dubois 25412517bc9SJean-Christophe Dubois static void imx7_src_class_init(ObjectClass *klass, void *data) 25512517bc9SJean-Christophe Dubois { 25612517bc9SJean-Christophe Dubois DeviceClass *dc = DEVICE_CLASS(klass); 25712517bc9SJean-Christophe Dubois 25812517bc9SJean-Christophe Dubois dc->realize = imx7_src_realize; 25912517bc9SJean-Christophe Dubois dc->reset = imx7_src_reset; 26012517bc9SJean-Christophe Dubois dc->vmsd = &vmstate_imx7_src; 26112517bc9SJean-Christophe Dubois dc->desc = "i.MX6 System Reset Controller"; 26212517bc9SJean-Christophe Dubois } 26312517bc9SJean-Christophe Dubois 26412517bc9SJean-Christophe Dubois static const TypeInfo imx7_src_info = { 26512517bc9SJean-Christophe Dubois .name = TYPE_IMX7_SRC, 26612517bc9SJean-Christophe Dubois .parent = TYPE_SYS_BUS_DEVICE, 26712517bc9SJean-Christophe Dubois .instance_size = sizeof(IMX7SRCState), 26812517bc9SJean-Christophe Dubois .class_init = imx7_src_class_init, 26912517bc9SJean-Christophe Dubois }; 27012517bc9SJean-Christophe Dubois 27112517bc9SJean-Christophe Dubois static void imx7_src_register_types(void) 27212517bc9SJean-Christophe Dubois { 27312517bc9SJean-Christophe Dubois type_register_static(&imx7_src_info); 27412517bc9SJean-Christophe Dubois } 27512517bc9SJean-Christophe Dubois 27612517bc9SJean-Christophe Dubois type_init(imx7_src_register_types) 277