1877c181cSPhilippe Mathieu-Daudé /*
2877c181cSPhilippe Mathieu-Daudé * ARM AMBA PrimeCell PL031 RTC
3877c181cSPhilippe Mathieu-Daudé *
4877c181cSPhilippe Mathieu-Daudé * Copyright (c) 2007 CodeSourcery
5877c181cSPhilippe Mathieu-Daudé *
6877c181cSPhilippe Mathieu-Daudé * This file is free software; you can redistribute it and/or modify
7877c181cSPhilippe Mathieu-Daudé * it under the terms of the GNU General Public License version 2 as
8877c181cSPhilippe Mathieu-Daudé * published by the Free Software Foundation.
9877c181cSPhilippe Mathieu-Daudé *
10877c181cSPhilippe Mathieu-Daudé * Contributions after 2012-01-13 are licensed under the terms of the
11877c181cSPhilippe Mathieu-Daudé * GNU GPL, version 2 or (at your option) any later version.
12877c181cSPhilippe Mathieu-Daudé */
13877c181cSPhilippe Mathieu-Daudé
14877c181cSPhilippe Mathieu-Daudé #include "qemu/osdep.h"
15877c181cSPhilippe Mathieu-Daudé #include "hw/rtc/pl031.h"
16877c181cSPhilippe Mathieu-Daudé #include "migration/vmstate.h"
17877c181cSPhilippe Mathieu-Daudé #include "hw/irq.h"
18877c181cSPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
19877c181cSPhilippe Mathieu-Daudé #include "hw/sysbus.h"
20877c181cSPhilippe Mathieu-Daudé #include "qemu/timer.h"
21877c181cSPhilippe Mathieu-Daudé #include "sysemu/sysemu.h"
222f93d8b0SPeter Maydell #include "sysemu/rtc.h"
23877c181cSPhilippe Mathieu-Daudé #include "qemu/cutils.h"
24877c181cSPhilippe Mathieu-Daudé #include "qemu/log.h"
25877c181cSPhilippe Mathieu-Daudé #include "qemu/module.h"
26877c181cSPhilippe Mathieu-Daudé #include "trace.h"
271f216b8cSPeter Maydell #include "qapi/qapi-events-misc.h"
28877c181cSPhilippe Mathieu-Daudé
29877c181cSPhilippe Mathieu-Daudé #define RTC_DR 0x00 /* Data read register */
30877c181cSPhilippe Mathieu-Daudé #define RTC_MR 0x04 /* Match register */
31877c181cSPhilippe Mathieu-Daudé #define RTC_LR 0x08 /* Data load register */
32877c181cSPhilippe Mathieu-Daudé #define RTC_CR 0x0c /* Control register */
33877c181cSPhilippe Mathieu-Daudé #define RTC_IMSC 0x10 /* Interrupt mask and set register */
34877c181cSPhilippe Mathieu-Daudé #define RTC_RIS 0x14 /* Raw interrupt status register */
35877c181cSPhilippe Mathieu-Daudé #define RTC_MIS 0x18 /* Masked interrupt status register */
36877c181cSPhilippe Mathieu-Daudé #define RTC_ICR 0x1c /* Interrupt clear register */
37877c181cSPhilippe Mathieu-Daudé
38877c181cSPhilippe Mathieu-Daudé static const unsigned char pl031_id[] = {
39877c181cSPhilippe Mathieu-Daudé 0x31, 0x10, 0x14, 0x00, /* Device ID */
40877c181cSPhilippe Mathieu-Daudé 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */
41877c181cSPhilippe Mathieu-Daudé };
42877c181cSPhilippe Mathieu-Daudé
pl031_update(PL031State * s)43877c181cSPhilippe Mathieu-Daudé static void pl031_update(PL031State *s)
44877c181cSPhilippe Mathieu-Daudé {
45877c181cSPhilippe Mathieu-Daudé uint32_t flags = s->is & s->im;
46877c181cSPhilippe Mathieu-Daudé
47877c181cSPhilippe Mathieu-Daudé trace_pl031_irq_state(flags);
48877c181cSPhilippe Mathieu-Daudé qemu_set_irq(s->irq, flags);
49877c181cSPhilippe Mathieu-Daudé }
50877c181cSPhilippe Mathieu-Daudé
pl031_interrupt(void * opaque)51877c181cSPhilippe Mathieu-Daudé static void pl031_interrupt(void * opaque)
52877c181cSPhilippe Mathieu-Daudé {
53877c181cSPhilippe Mathieu-Daudé PL031State *s = (PL031State *)opaque;
54877c181cSPhilippe Mathieu-Daudé
55877c181cSPhilippe Mathieu-Daudé s->is = 1;
56877c181cSPhilippe Mathieu-Daudé trace_pl031_alarm_raised();
57877c181cSPhilippe Mathieu-Daudé pl031_update(s);
58877c181cSPhilippe Mathieu-Daudé }
59877c181cSPhilippe Mathieu-Daudé
pl031_get_count(PL031State * s)60877c181cSPhilippe Mathieu-Daudé static uint32_t pl031_get_count(PL031State *s)
61877c181cSPhilippe Mathieu-Daudé {
62877c181cSPhilippe Mathieu-Daudé int64_t now = qemu_clock_get_ns(rtc_clock);
63877c181cSPhilippe Mathieu-Daudé return s->tick_offset + now / NANOSECONDS_PER_SECOND;
64877c181cSPhilippe Mathieu-Daudé }
65877c181cSPhilippe Mathieu-Daudé
pl031_set_alarm(PL031State * s)66877c181cSPhilippe Mathieu-Daudé static void pl031_set_alarm(PL031State *s)
67877c181cSPhilippe Mathieu-Daudé {
68877c181cSPhilippe Mathieu-Daudé uint32_t ticks;
69877c181cSPhilippe Mathieu-Daudé
70877c181cSPhilippe Mathieu-Daudé /* The timer wraps around. This subtraction also wraps in the same way,
71877c181cSPhilippe Mathieu-Daudé and gives correct results when alarm < now_ticks. */
72877c181cSPhilippe Mathieu-Daudé ticks = s->mr - pl031_get_count(s);
73877c181cSPhilippe Mathieu-Daudé trace_pl031_set_alarm(ticks);
74877c181cSPhilippe Mathieu-Daudé if (ticks == 0) {
75877c181cSPhilippe Mathieu-Daudé timer_del(s->timer);
76877c181cSPhilippe Mathieu-Daudé pl031_interrupt(s);
77877c181cSPhilippe Mathieu-Daudé } else {
78877c181cSPhilippe Mathieu-Daudé int64_t now = qemu_clock_get_ns(rtc_clock);
79877c181cSPhilippe Mathieu-Daudé timer_mod(s->timer, now + (int64_t)ticks * NANOSECONDS_PER_SECOND);
80877c181cSPhilippe Mathieu-Daudé }
81877c181cSPhilippe Mathieu-Daudé }
82877c181cSPhilippe Mathieu-Daudé
pl031_read(void * opaque,hwaddr offset,unsigned size)83877c181cSPhilippe Mathieu-Daudé static uint64_t pl031_read(void *opaque, hwaddr offset,
84877c181cSPhilippe Mathieu-Daudé unsigned size)
85877c181cSPhilippe Mathieu-Daudé {
86877c181cSPhilippe Mathieu-Daudé PL031State *s = (PL031State *)opaque;
87877c181cSPhilippe Mathieu-Daudé uint64_t r;
88877c181cSPhilippe Mathieu-Daudé
89877c181cSPhilippe Mathieu-Daudé switch (offset) {
90877c181cSPhilippe Mathieu-Daudé case RTC_DR:
91877c181cSPhilippe Mathieu-Daudé r = pl031_get_count(s);
92877c181cSPhilippe Mathieu-Daudé break;
93877c181cSPhilippe Mathieu-Daudé case RTC_MR:
94877c181cSPhilippe Mathieu-Daudé r = s->mr;
95877c181cSPhilippe Mathieu-Daudé break;
96877c181cSPhilippe Mathieu-Daudé case RTC_IMSC:
97877c181cSPhilippe Mathieu-Daudé r = s->im;
98877c181cSPhilippe Mathieu-Daudé break;
99877c181cSPhilippe Mathieu-Daudé case RTC_RIS:
100877c181cSPhilippe Mathieu-Daudé r = s->is;
101877c181cSPhilippe Mathieu-Daudé break;
102877c181cSPhilippe Mathieu-Daudé case RTC_LR:
103877c181cSPhilippe Mathieu-Daudé r = s->lr;
104877c181cSPhilippe Mathieu-Daudé break;
105877c181cSPhilippe Mathieu-Daudé case RTC_CR:
106877c181cSPhilippe Mathieu-Daudé /* RTC is permanently enabled. */
107877c181cSPhilippe Mathieu-Daudé r = 1;
108877c181cSPhilippe Mathieu-Daudé break;
109877c181cSPhilippe Mathieu-Daudé case RTC_MIS:
110877c181cSPhilippe Mathieu-Daudé r = s->is & s->im;
111877c181cSPhilippe Mathieu-Daudé break;
112877c181cSPhilippe Mathieu-Daudé case 0xfe0 ... 0xfff:
113877c181cSPhilippe Mathieu-Daudé r = pl031_id[(offset - 0xfe0) >> 2];
114877c181cSPhilippe Mathieu-Daudé break;
115877c181cSPhilippe Mathieu-Daudé case RTC_ICR:
116877c181cSPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR,
117877c181cSPhilippe Mathieu-Daudé "pl031: read of write-only register at offset 0x%x\n",
118877c181cSPhilippe Mathieu-Daudé (int)offset);
119877c181cSPhilippe Mathieu-Daudé r = 0;
120877c181cSPhilippe Mathieu-Daudé break;
121877c181cSPhilippe Mathieu-Daudé default:
122877c181cSPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR,
123877c181cSPhilippe Mathieu-Daudé "pl031_read: Bad offset 0x%x\n", (int)offset);
124877c181cSPhilippe Mathieu-Daudé r = 0;
125877c181cSPhilippe Mathieu-Daudé break;
126877c181cSPhilippe Mathieu-Daudé }
127877c181cSPhilippe Mathieu-Daudé
128877c181cSPhilippe Mathieu-Daudé trace_pl031_read(offset, r);
129877c181cSPhilippe Mathieu-Daudé return r;
130877c181cSPhilippe Mathieu-Daudé }
131877c181cSPhilippe Mathieu-Daudé
pl031_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)132877c181cSPhilippe Mathieu-Daudé static void pl031_write(void * opaque, hwaddr offset,
133877c181cSPhilippe Mathieu-Daudé uint64_t value, unsigned size)
134877c181cSPhilippe Mathieu-Daudé {
135877c181cSPhilippe Mathieu-Daudé PL031State *s = (PL031State *)opaque;
136877c181cSPhilippe Mathieu-Daudé
137877c181cSPhilippe Mathieu-Daudé trace_pl031_write(offset, value);
138877c181cSPhilippe Mathieu-Daudé
139877c181cSPhilippe Mathieu-Daudé switch (offset) {
1401adf528eSEric Auger case RTC_LR: {
1412beb1e5fSMarkus Armbruster g_autofree const char *qom_path = object_get_canonical_path(opaque);
1421adf528eSEric Auger struct tm tm;
1431adf528eSEric Auger
144*a0fb839dSJessica Clarke s->lr = value;
145877c181cSPhilippe Mathieu-Daudé s->tick_offset += value - pl031_get_count(s);
1461adf528eSEric Auger
1471adf528eSEric Auger qemu_get_timedate(&tm, s->tick_offset);
1482beb1e5fSMarkus Armbruster qapi_event_send_rtc_change(qemu_timedate_diff(&tm), qom_path);
1491adf528eSEric Auger
150877c181cSPhilippe Mathieu-Daudé pl031_set_alarm(s);
151877c181cSPhilippe Mathieu-Daudé break;
1521adf528eSEric Auger }
153877c181cSPhilippe Mathieu-Daudé case RTC_MR:
154877c181cSPhilippe Mathieu-Daudé s->mr = value;
155877c181cSPhilippe Mathieu-Daudé pl031_set_alarm(s);
156877c181cSPhilippe Mathieu-Daudé break;
157877c181cSPhilippe Mathieu-Daudé case RTC_IMSC:
158877c181cSPhilippe Mathieu-Daudé s->im = value & 1;
159877c181cSPhilippe Mathieu-Daudé pl031_update(s);
160877c181cSPhilippe Mathieu-Daudé break;
161877c181cSPhilippe Mathieu-Daudé case RTC_ICR:
16283ad9595SAlexander Graf s->is &= ~value;
163877c181cSPhilippe Mathieu-Daudé pl031_update(s);
164877c181cSPhilippe Mathieu-Daudé break;
165877c181cSPhilippe Mathieu-Daudé case RTC_CR:
166877c181cSPhilippe Mathieu-Daudé /* Written value is ignored. */
167877c181cSPhilippe Mathieu-Daudé break;
168877c181cSPhilippe Mathieu-Daudé
169877c181cSPhilippe Mathieu-Daudé case RTC_DR:
170877c181cSPhilippe Mathieu-Daudé case RTC_MIS:
171877c181cSPhilippe Mathieu-Daudé case RTC_RIS:
172877c181cSPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR,
173877c181cSPhilippe Mathieu-Daudé "pl031: write to read-only register at offset 0x%x\n",
174877c181cSPhilippe Mathieu-Daudé (int)offset);
175877c181cSPhilippe Mathieu-Daudé break;
176877c181cSPhilippe Mathieu-Daudé
177877c181cSPhilippe Mathieu-Daudé default:
178877c181cSPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR,
179877c181cSPhilippe Mathieu-Daudé "pl031_write: Bad offset 0x%x\n", (int)offset);
180877c181cSPhilippe Mathieu-Daudé break;
181877c181cSPhilippe Mathieu-Daudé }
182877c181cSPhilippe Mathieu-Daudé }
183877c181cSPhilippe Mathieu-Daudé
184877c181cSPhilippe Mathieu-Daudé static const MemoryRegionOps pl031_ops = {
185877c181cSPhilippe Mathieu-Daudé .read = pl031_read,
186877c181cSPhilippe Mathieu-Daudé .write = pl031_write,
187877c181cSPhilippe Mathieu-Daudé .endianness = DEVICE_NATIVE_ENDIAN,
188877c181cSPhilippe Mathieu-Daudé };
189877c181cSPhilippe Mathieu-Daudé
pl031_init(Object * obj)190877c181cSPhilippe Mathieu-Daudé static void pl031_init(Object *obj)
191877c181cSPhilippe Mathieu-Daudé {
192877c181cSPhilippe Mathieu-Daudé PL031State *s = PL031(obj);
193877c181cSPhilippe Mathieu-Daudé SysBusDevice *dev = SYS_BUS_DEVICE(obj);
194877c181cSPhilippe Mathieu-Daudé struct tm tm;
195877c181cSPhilippe Mathieu-Daudé
196877c181cSPhilippe Mathieu-Daudé memory_region_init_io(&s->iomem, obj, &pl031_ops, s, "pl031", 0x1000);
197877c181cSPhilippe Mathieu-Daudé sysbus_init_mmio(dev, &s->iomem);
198877c181cSPhilippe Mathieu-Daudé
199877c181cSPhilippe Mathieu-Daudé sysbus_init_irq(dev, &s->irq);
200877c181cSPhilippe Mathieu-Daudé qemu_get_timedate(&tm, 0);
201877c181cSPhilippe Mathieu-Daudé s->tick_offset = mktimegm(&tm) -
202877c181cSPhilippe Mathieu-Daudé qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND;
203877c181cSPhilippe Mathieu-Daudé
204877c181cSPhilippe Mathieu-Daudé s->timer = timer_new_ns(rtc_clock, pl031_interrupt, s);
205877c181cSPhilippe Mathieu-Daudé }
206877c181cSPhilippe Mathieu-Daudé
pl031_finalize(Object * obj)207b026d4a6SGan Qixin static void pl031_finalize(Object *obj)
208b026d4a6SGan Qixin {
209b026d4a6SGan Qixin PL031State *s = PL031(obj);
210b026d4a6SGan Qixin
211b026d4a6SGan Qixin timer_free(s->timer);
212b026d4a6SGan Qixin }
213b026d4a6SGan Qixin
pl031_pre_save(void * opaque)214877c181cSPhilippe Mathieu-Daudé static int pl031_pre_save(void *opaque)
215877c181cSPhilippe Mathieu-Daudé {
216877c181cSPhilippe Mathieu-Daudé PL031State *s = opaque;
217877c181cSPhilippe Mathieu-Daudé
218877c181cSPhilippe Mathieu-Daudé /*
219877c181cSPhilippe Mathieu-Daudé * The PL031 device model code uses the tick_offset field, which is
220877c181cSPhilippe Mathieu-Daudé * the offset between what the guest RTC should read and what the
221877c181cSPhilippe Mathieu-Daudé * QEMU rtc_clock reads:
222877c181cSPhilippe Mathieu-Daudé * guest_rtc = rtc_clock + tick_offset
223877c181cSPhilippe Mathieu-Daudé * and so
224877c181cSPhilippe Mathieu-Daudé * tick_offset = guest_rtc - rtc_clock
225877c181cSPhilippe Mathieu-Daudé *
226877c181cSPhilippe Mathieu-Daudé * We want to migrate this offset, which sounds straightforward.
227877c181cSPhilippe Mathieu-Daudé * Unfortunately older versions of QEMU migrated a conversion of this
228877c181cSPhilippe Mathieu-Daudé * offset into an offset from the vm_clock. (This was in turn an
229877c181cSPhilippe Mathieu-Daudé * attempt to be compatible with even older QEMU versions, but it
230877c181cSPhilippe Mathieu-Daudé * has incorrect behaviour if the rtc_clock is not the same as the
231877c181cSPhilippe Mathieu-Daudé * vm_clock.) So we put the actual tick_offset into a migration
232877c181cSPhilippe Mathieu-Daudé * subsection, and the backwards-compatible time-relative-to-vm_clock
233877c181cSPhilippe Mathieu-Daudé * in the main migration state.
234877c181cSPhilippe Mathieu-Daudé *
235877c181cSPhilippe Mathieu-Daudé * Calculate base time relative to QEMU_CLOCK_VIRTUAL:
236877c181cSPhilippe Mathieu-Daudé */
237877c181cSPhilippe Mathieu-Daudé int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
238877c181cSPhilippe Mathieu-Daudé s->tick_offset_vmstate = s->tick_offset + delta / NANOSECONDS_PER_SECOND;
239877c181cSPhilippe Mathieu-Daudé
240877c181cSPhilippe Mathieu-Daudé return 0;
241877c181cSPhilippe Mathieu-Daudé }
242877c181cSPhilippe Mathieu-Daudé
pl031_pre_load(void * opaque)243877c181cSPhilippe Mathieu-Daudé static int pl031_pre_load(void *opaque)
244877c181cSPhilippe Mathieu-Daudé {
245877c181cSPhilippe Mathieu-Daudé PL031State *s = opaque;
246877c181cSPhilippe Mathieu-Daudé
247877c181cSPhilippe Mathieu-Daudé s->tick_offset_migrated = false;
248877c181cSPhilippe Mathieu-Daudé return 0;
249877c181cSPhilippe Mathieu-Daudé }
250877c181cSPhilippe Mathieu-Daudé
pl031_post_load(void * opaque,int version_id)251877c181cSPhilippe Mathieu-Daudé static int pl031_post_load(void *opaque, int version_id)
252877c181cSPhilippe Mathieu-Daudé {
253877c181cSPhilippe Mathieu-Daudé PL031State *s = opaque;
254877c181cSPhilippe Mathieu-Daudé
255877c181cSPhilippe Mathieu-Daudé /*
256877c181cSPhilippe Mathieu-Daudé * If we got the tick_offset subsection, then we can just use
257877c181cSPhilippe Mathieu-Daudé * the value in that. Otherwise the source is an older QEMU and
258877c181cSPhilippe Mathieu-Daudé * has given us the offset from the vm_clock; convert it back to
259877c181cSPhilippe Mathieu-Daudé * an offset from the rtc_clock. This will cause time to incorrectly
260877c181cSPhilippe Mathieu-Daudé * go backwards compared to the host RTC, but this is unavoidable.
261877c181cSPhilippe Mathieu-Daudé */
262877c181cSPhilippe Mathieu-Daudé
263877c181cSPhilippe Mathieu-Daudé if (!s->tick_offset_migrated) {
264877c181cSPhilippe Mathieu-Daudé int64_t delta = qemu_clock_get_ns(rtc_clock) -
265877c181cSPhilippe Mathieu-Daudé qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
266877c181cSPhilippe Mathieu-Daudé s->tick_offset = s->tick_offset_vmstate -
267877c181cSPhilippe Mathieu-Daudé delta / NANOSECONDS_PER_SECOND;
268877c181cSPhilippe Mathieu-Daudé }
269877c181cSPhilippe Mathieu-Daudé pl031_set_alarm(s);
270877c181cSPhilippe Mathieu-Daudé return 0;
271877c181cSPhilippe Mathieu-Daudé }
272877c181cSPhilippe Mathieu-Daudé
pl031_tick_offset_post_load(void * opaque,int version_id)273877c181cSPhilippe Mathieu-Daudé static int pl031_tick_offset_post_load(void *opaque, int version_id)
274877c181cSPhilippe Mathieu-Daudé {
275877c181cSPhilippe Mathieu-Daudé PL031State *s = opaque;
276877c181cSPhilippe Mathieu-Daudé
277877c181cSPhilippe Mathieu-Daudé s->tick_offset_migrated = true;
278877c181cSPhilippe Mathieu-Daudé return 0;
279877c181cSPhilippe Mathieu-Daudé }
280877c181cSPhilippe Mathieu-Daudé
pl031_tick_offset_needed(void * opaque)281877c181cSPhilippe Mathieu-Daudé static bool pl031_tick_offset_needed(void *opaque)
282877c181cSPhilippe Mathieu-Daudé {
283877c181cSPhilippe Mathieu-Daudé PL031State *s = opaque;
284877c181cSPhilippe Mathieu-Daudé
285877c181cSPhilippe Mathieu-Daudé return s->migrate_tick_offset;
286877c181cSPhilippe Mathieu-Daudé }
287877c181cSPhilippe Mathieu-Daudé
288877c181cSPhilippe Mathieu-Daudé static const VMStateDescription vmstate_pl031_tick_offset = {
289877c181cSPhilippe Mathieu-Daudé .name = "pl031/tick-offset",
290877c181cSPhilippe Mathieu-Daudé .version_id = 1,
291877c181cSPhilippe Mathieu-Daudé .minimum_version_id = 1,
292877c181cSPhilippe Mathieu-Daudé .needed = pl031_tick_offset_needed,
293877c181cSPhilippe Mathieu-Daudé .post_load = pl031_tick_offset_post_load,
294877c181cSPhilippe Mathieu-Daudé .fields = (const VMStateField[]) {
295877c181cSPhilippe Mathieu-Daudé VMSTATE_UINT32(tick_offset, PL031State),
296877c181cSPhilippe Mathieu-Daudé VMSTATE_END_OF_LIST()
297877c181cSPhilippe Mathieu-Daudé }
298877c181cSPhilippe Mathieu-Daudé };
299877c181cSPhilippe Mathieu-Daudé
300877c181cSPhilippe Mathieu-Daudé static const VMStateDescription vmstate_pl031 = {
301877c181cSPhilippe Mathieu-Daudé .name = "pl031",
302877c181cSPhilippe Mathieu-Daudé .version_id = 1,
303877c181cSPhilippe Mathieu-Daudé .minimum_version_id = 1,
304877c181cSPhilippe Mathieu-Daudé .pre_save = pl031_pre_save,
305877c181cSPhilippe Mathieu-Daudé .pre_load = pl031_pre_load,
306877c181cSPhilippe Mathieu-Daudé .post_load = pl031_post_load,
307877c181cSPhilippe Mathieu-Daudé .fields = (const VMStateField[]) {
308877c181cSPhilippe Mathieu-Daudé VMSTATE_UINT32(tick_offset_vmstate, PL031State),
309877c181cSPhilippe Mathieu-Daudé VMSTATE_UINT32(mr, PL031State),
310877c181cSPhilippe Mathieu-Daudé VMSTATE_UINT32(lr, PL031State),
311877c181cSPhilippe Mathieu-Daudé VMSTATE_UINT32(cr, PL031State),
312877c181cSPhilippe Mathieu-Daudé VMSTATE_UINT32(im, PL031State),
313877c181cSPhilippe Mathieu-Daudé VMSTATE_UINT32(is, PL031State),
314877c181cSPhilippe Mathieu-Daudé VMSTATE_END_OF_LIST()
315877c181cSPhilippe Mathieu-Daudé },
316877c181cSPhilippe Mathieu-Daudé .subsections = (const VMStateDescription * const []) {
317877c181cSPhilippe Mathieu-Daudé &vmstate_pl031_tick_offset,
318877c181cSPhilippe Mathieu-Daudé NULL
319877c181cSPhilippe Mathieu-Daudé }
320877c181cSPhilippe Mathieu-Daudé };
321877c181cSPhilippe Mathieu-Daudé
322877c181cSPhilippe Mathieu-Daudé static Property pl031_properties[] = {
323877c181cSPhilippe Mathieu-Daudé /*
324877c181cSPhilippe Mathieu-Daudé * True to correctly migrate the tick offset of the RTC. False to
325877c181cSPhilippe Mathieu-Daudé * obtain backward migration compatibility with older QEMU versions,
326877c181cSPhilippe Mathieu-Daudé * at the expense of the guest RTC going backwards compared with the
327877c181cSPhilippe Mathieu-Daudé * host RTC when the VM is saved/restored if using -rtc host.
328877c181cSPhilippe Mathieu-Daudé * (Even if set to 'true' older QEMU can migrate forward to newer QEMU;
329877c181cSPhilippe Mathieu-Daudé * 'false' also permits newer QEMU to migrate to older QEMU.)
330877c181cSPhilippe Mathieu-Daudé */
331877c181cSPhilippe Mathieu-Daudé DEFINE_PROP_BOOL("migrate-tick-offset",
332877c181cSPhilippe Mathieu-Daudé PL031State, migrate_tick_offset, true),
333877c181cSPhilippe Mathieu-Daudé DEFINE_PROP_END_OF_LIST()
334877c181cSPhilippe Mathieu-Daudé };
335877c181cSPhilippe Mathieu-Daudé
pl031_class_init(ObjectClass * klass,void * data)336877c181cSPhilippe Mathieu-Daudé static void pl031_class_init(ObjectClass *klass, void *data)
337877c181cSPhilippe Mathieu-Daudé {
338877c181cSPhilippe Mathieu-Daudé DeviceClass *dc = DEVICE_CLASS(klass);
339877c181cSPhilippe Mathieu-Daudé
340877c181cSPhilippe Mathieu-Daudé dc->vmsd = &vmstate_pl031;
3414f67d30bSMarc-André Lureau device_class_set_props(dc, pl031_properties);
342877c181cSPhilippe Mathieu-Daudé }
343877c181cSPhilippe Mathieu-Daudé
344877c181cSPhilippe Mathieu-Daudé static const TypeInfo pl031_info = {
345877c181cSPhilippe Mathieu-Daudé .name = TYPE_PL031,
346877c181cSPhilippe Mathieu-Daudé .parent = TYPE_SYS_BUS_DEVICE,
347877c181cSPhilippe Mathieu-Daudé .instance_size = sizeof(PL031State),
348877c181cSPhilippe Mathieu-Daudé .instance_init = pl031_init,
349b026d4a6SGan Qixin .instance_finalize = pl031_finalize,
350877c181cSPhilippe Mathieu-Daudé .class_init = pl031_class_init,
351877c181cSPhilippe Mathieu-Daudé };
352877c181cSPhilippe Mathieu-Daudé
pl031_register_types(void)353877c181cSPhilippe Mathieu-Daudé static void pl031_register_types(void)
354877c181cSPhilippe Mathieu-Daudé {
355877c181cSPhilippe Mathieu-Daudé type_register_static(&pl031_info);
356877c181cSPhilippe Mathieu-Daudé }
357877c181cSPhilippe Mathieu-Daudé
358877c181cSPhilippe Mathieu-Daudé type_init(pl031_register_types)
359