2 * IMX7 Secure Non-Volatile Storage
4 * Copyright (c) 2018, Impinj, Inc.
6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 * See the COPYING file in the top-level directory.
11 * Bare minimum emulation code needed to support being able to shut
12 * down linux guest gracefully.
15 #include "qemu/osdep.h"
16 #include "qemu/bitops.h"
17 #include "qemu/timer.h"
18 #include "migration/vmstate.h"
19 #include "hw/misc/imx7_snvs.h"
20 #include "qemu/cutils.h"
21 #include "qemu/module.h"
22 #include "sysemu/sysemu.h"
23 #include "sysemu/rtc.h"
24 #include "sysemu/runstate.h"
27 #define RTC_FREQ 32768ULL
29 static const VMStateDescription vmstate_imx7_snvs
= {
30 .name
= TYPE_IMX7_SNVS
,
32 .minimum_version_id
= 1,
33 .fields
= (const VMStateField
[]) {
34 VMSTATE_UINT64(tick_offset
, IMX7SNVSState
),
35 VMSTATE_UINT64(lpcr
, IMX7SNVSState
),
40 static uint64_t imx7_snvs_get_count(IMX7SNVSState
*s
)
42 uint64_t ticks
= muldiv64(qemu_clock_get_ns(rtc_clock
), RTC_FREQ
,
43 NANOSECONDS_PER_SECOND
);
44 return s
->tick_offset
+ ticks
;
47 static uint64_t imx7_snvs_read(void *opaque
, hwaddr offset
, unsigned size
)
49 IMX7SNVSState
*s
= IMX7_SNVS(opaque
);
54 ret
= extract64(imx7_snvs_get_count(s
), 32, 15);
57 ret
= extract64(imx7_snvs_get_count(s
), 0, 32);
64 trace_imx7_snvs_read(offset
, ret
, size
);
69 static void imx7_snvs_reset(DeviceState
*dev
)
71 IMX7SNVSState
*s
= IMX7_SNVS(dev
);
76 static void imx7_snvs_write(void *opaque
, hwaddr offset
,
77 uint64_t v
, unsigned size
)
79 trace_imx7_snvs_write(offset
, v
, size
);
81 IMX7SNVSState
*s
= IMX7_SNVS(opaque
);
83 uint64_t new_value
= 0, snvs_count
= 0;
85 if (offset
== SNVS_LPSRTCMR
|| offset
== SNVS_LPSRTCLR
) {
86 snvs_count
= imx7_snvs_get_count(s
);
91 new_value
= deposit64(snvs_count
, 32, 32, v
);
94 new_value
= deposit64(snvs_count
, 0, 32, v
);
99 const uint32_t mask
= SNVS_LPCR_TOP
| SNVS_LPCR_DP_EN
;
101 if ((v
& mask
) == mask
) {
102 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN
);
108 if (offset
== SNVS_LPSRTCMR
|| offset
== SNVS_LPSRTCLR
) {
109 s
->tick_offset
+= new_value
- snvs_count
;
113 static const struct MemoryRegionOps imx7_snvs_ops
= {
114 .read
= imx7_snvs_read
,
115 .write
= imx7_snvs_write
,
116 .endianness
= DEVICE_NATIVE_ENDIAN
,
119 * Our device would not work correctly if the guest was doing
120 * unaligned access. This might not be a limitation on the real
121 * device but in practice there is no reason for a guest to access
122 * this device unaligned.
124 .min_access_size
= 4,
125 .max_access_size
= 4,
130 static void imx7_snvs_init(Object
*obj
)
132 SysBusDevice
*sd
= SYS_BUS_DEVICE(obj
);
133 IMX7SNVSState
*s
= IMX7_SNVS(obj
);
136 memory_region_init_io(&s
->mmio
, obj
, &imx7_snvs_ops
, s
,
137 TYPE_IMX7_SNVS
, 0x1000);
139 sysbus_init_mmio(sd
, &s
->mmio
);
141 qemu_get_timedate(&tm
, 0);
142 s
->tick_offset
= mktimegm(&tm
) -
143 qemu_clock_get_ns(rtc_clock
) / NANOSECONDS_PER_SECOND
;
146 static void imx7_snvs_class_init(ObjectClass
*klass
, void *data
)
148 DeviceClass
*dc
= DEVICE_CLASS(klass
);
150 dc
->reset
= imx7_snvs_reset
;
151 dc
->vmsd
= &vmstate_imx7_snvs
;
152 dc
->desc
= "i.MX7 Secure Non-Volatile Storage Module";
155 static const TypeInfo imx7_snvs_info
= {
156 .name
= TYPE_IMX7_SNVS
,
157 .parent
= TYPE_SYS_BUS_DEVICE
,
158 .instance_size
= sizeof(IMX7SNVSState
),
159 .instance_init
= imx7_snvs_init
,
160 .class_init
= imx7_snvs_class_init
,
163 static void imx7_snvs_register_type(void)
165 type_register_static(&imx7_snvs_info
);
167 type_init(imx7_snvs_register_type
)