xref: /openbmc/qemu/hw/rtc/pl031.c (revision 4d28d57c)
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*4d28d57cSJessica 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,
294a80cc662SRichard Henderson     .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,
307a80cc662SRichard Henderson     .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é     },
316a80cc662SRichard Henderson     .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