Signed-off-by: Nikita Ostrenkov <n.ostren...@gmail.com> --- hw/misc/imx7_snvs.c | 59 ++++++++++++++++++++++++++++++++----- hw/misc/trace-events | 4 +-- include/hw/misc/imx7_snvs.h | 14 ++++++++- 3 files changed, 67 insertions(+), 10 deletions(-)
diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c index a245f96cd4..7ef3e4901a 100644 --- a/hw/misc/imx7_snvs.c +++ b/hw/misc/imx7_snvs.c @@ -13,29 +13,74 @@ */ #include "qemu/osdep.h" +#include "qemu/timer.h" #include "hw/misc/imx7_snvs.h" #include "qemu/module.h" +#include "sysemu/sysemu.h" #include "sysemu/runstate.h" #include "trace.h" +#define RTC_FREQ 32768ULL + +static uint64_t imx7_snvs_get_count(IMX7SNVSState *s) +{ + int64_t now_ms = qemu_clock_get_ns(rtc_clock) / 1000000; + return s->tick_offset + now_ms * RTC_FREQ / 1000; +} + static uint64_t imx7_snvs_read(void *opaque, hwaddr offset, unsigned size) { - trace_imx7_snvs_read(offset, 0); + IMX7SNVSState *s = opaque; + uint64_t ret = 0; + + switch (offset) { + case SNVS_LPSRTCMR: + ret = (imx7_snvs_get_count(s) >> 32) & 0x7fffU; + break; + case SNVS_LPSRTCLR: + ret = imx7_snvs_get_count(s) & 0xffffffffU; + break; + case SNVS_LPCR: + ret = s->lpcr; + break; + } + + trace_imx7_snvs_read(offset, ret, size); - return 0; + return ret; } static void imx7_snvs_write(void *opaque, hwaddr offset, uint64_t v, unsigned size) { - const uint32_t value = v; - const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN; + trace_imx7_snvs_write(offset, v, size); - trace_imx7_snvs_write(offset, value); + IMX7SNVSState *s = opaque; - if (offset == SNVS_LPCR && ((value & mask) == mask)) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + uint64_t new_value = 0; + + switch (offset) { + case SNVS_LPSRTCMR: + new_value = (imx7_snvs_get_count(s) & 0xffffffffU) | (v << 32); + break; + case SNVS_LPSRTCLR: + new_value = (imx7_snvs_get_count(s) & 0x7fff00000000ULL) | v; + break; + case SNVS_LPCR: { + s->lpcr = v; + + const uint32_t value = v; + const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN; + + if ((value & mask) == mask) + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + + break; + } } + + if (offset == SNVS_LPSRTCMR || offset == SNVS_LPSRTCLR) + s->tick_offset += new_value - imx7_snvs_get_count(s); } static const struct MemoryRegionOps imx7_snvs_ops = { diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 05ff692441..85725506bf 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -116,8 +116,8 @@ imx7_gpr_read(uint64_t offset) "addr 0x%08" PRIx64 imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx64 # imx7_snvs.c -imx7_snvs_read(uint64_t offset, uint32_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx32 -imx7_snvs_write(uint64_t offset, uint32_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx32 +imx7_snvs_read(uint64_t offset, uint64_t value, unsigned size) "i.MX SNVS read: offset 0x%08" PRIx64 " value 0x%08" PRIx64 " size %u" +imx7_snvs_write(uint64_t offset, uint64_t value, unsigned size) "i.MX SNVS write: offset 0x%08" PRIx64 " value 0x%08" PRIx64 " size %u" # mos6522.c mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d" diff --git a/include/hw/misc/imx7_snvs.h b/include/hw/misc/imx7_snvs.h index 14a1d6fe6b..406c1fe97f 100644 --- a/include/hw/misc/imx7_snvs.h +++ b/include/hw/misc/imx7_snvs.h @@ -20,7 +20,9 @@ enum IMX7SNVSRegisters { SNVS_LPCR = 0x38, SNVS_LPCR_TOP = BIT(6), - SNVS_LPCR_DP_EN = BIT(5) + SNVS_LPCR_DP_EN = BIT(5), + SNVS_LPSRTCMR = 0x050, /* Secure Real Time Counter MSB Register */ + SNVS_LPSRTCLR = 0x054, /* Secure Real Time Counter LSB Register */ }; #define TYPE_IMX7_SNVS "imx7.snvs" @@ -31,6 +33,16 @@ struct IMX7SNVSState { SysBusDevice parent_obj; MemoryRegion mmio; + + /* + * Needed to preserve the tick_count across migration, even if the + * absolute value of the rtc_clock is different on the source and + * destination. + */ + int64_t tick_offset_vmstate; + int64_t tick_offset; + + uint64_t lpcr; }; #endif /* IMX7_SNVS_H */ -- 2.34.1