1bcdb9064SPhilippe Mathieu-Daudé /*
2bcdb9064SPhilippe Mathieu-Daudé * QEMU MC146818 RTC emulation
3bcdb9064SPhilippe Mathieu-Daudé *
4bcdb9064SPhilippe Mathieu-Daudé * Copyright (c) 2003-2004 Fabrice Bellard
5bcdb9064SPhilippe Mathieu-Daudé *
6bcdb9064SPhilippe Mathieu-Daudé * Permission is hereby granted, free of charge, to any person obtaining a copy
7bcdb9064SPhilippe Mathieu-Daudé * of this software and associated documentation files (the "Software"), to deal
8bcdb9064SPhilippe Mathieu-Daudé * in the Software without restriction, including without limitation the rights
9bcdb9064SPhilippe Mathieu-Daudé * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10bcdb9064SPhilippe Mathieu-Daudé * copies of the Software, and to permit persons to whom the Software is
11bcdb9064SPhilippe Mathieu-Daudé * furnished to do so, subject to the following conditions:
12bcdb9064SPhilippe Mathieu-Daudé *
13bcdb9064SPhilippe Mathieu-Daudé * The above copyright notice and this permission notice shall be included in
14bcdb9064SPhilippe Mathieu-Daudé * all copies or substantial portions of the Software.
15bcdb9064SPhilippe Mathieu-Daudé *
16bcdb9064SPhilippe Mathieu-Daudé * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17bcdb9064SPhilippe Mathieu-Daudé * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18bcdb9064SPhilippe Mathieu-Daudé * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19bcdb9064SPhilippe Mathieu-Daudé * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20bcdb9064SPhilippe Mathieu-Daudé * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21bcdb9064SPhilippe Mathieu-Daudé * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22bcdb9064SPhilippe Mathieu-Daudé * THE SOFTWARE.
23bcdb9064SPhilippe Mathieu-Daudé */
24bcdb9064SPhilippe Mathieu-Daudé
25bcdb9064SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
26bcdb9064SPhilippe Mathieu-Daudé #include "qemu/cutils.h"
27bcdb9064SPhilippe Mathieu-Daudé #include "qemu/module.h"
28bcdb9064SPhilippe Mathieu-Daudé #include "qemu/bcd.h"
29d9cf178cSIgor Mammedov #include "hw/acpi/acpi_aml_interface.h"
302b85e0cdSThomas Huth #include "hw/intc/kvm_irqcount.h"
31bcdb9064SPhilippe Mathieu-Daudé #include "hw/irq.h"
32bcdb9064SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
33ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
34bcdb9064SPhilippe Mathieu-Daudé #include "qemu/timer.h"
35bcdb9064SPhilippe Mathieu-Daudé #include "sysemu/sysemu.h"
36bcdb9064SPhilippe Mathieu-Daudé #include "sysemu/replay.h"
37bcdb9064SPhilippe Mathieu-Daudé #include "sysemu/reset.h"
38bcdb9064SPhilippe Mathieu-Daudé #include "sysemu/runstate.h"
392f93d8b0SPeter Maydell #include "sysemu/rtc.h"
40bcdb9064SPhilippe Mathieu-Daudé #include "hw/rtc/mc146818rtc.h"
417ffcb73dSPhilippe Mathieu-Daudé #include "hw/rtc/mc146818rtc_regs.h"
42bcdb9064SPhilippe Mathieu-Daudé #include "migration/vmstate.h"
43bcdb9064SPhilippe Mathieu-Daudé #include "qapi/error.h"
441f216b8cSPeter Maydell #include "qapi/qapi-events-misc.h"
45bcdb9064SPhilippe Mathieu-Daudé #include "qapi/visitor.h"
46bcdb9064SPhilippe Mathieu-Daudé
47bcdb9064SPhilippe Mathieu-Daudé //#define DEBUG_CMOS
48bcdb9064SPhilippe Mathieu-Daudé //#define DEBUG_COALESCED
49bcdb9064SPhilippe Mathieu-Daudé
50bcdb9064SPhilippe Mathieu-Daudé #ifdef DEBUG_CMOS
51bcdb9064SPhilippe Mathieu-Daudé # define CMOS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
52bcdb9064SPhilippe Mathieu-Daudé #else
53bcdb9064SPhilippe Mathieu-Daudé # define CMOS_DPRINTF(format, ...) do { } while (0)
54bcdb9064SPhilippe Mathieu-Daudé #endif
55bcdb9064SPhilippe Mathieu-Daudé
56bcdb9064SPhilippe Mathieu-Daudé #ifdef DEBUG_COALESCED
57bcdb9064SPhilippe Mathieu-Daudé # define DPRINTF_C(format, ...) printf(format, ## __VA_ARGS__)
58bcdb9064SPhilippe Mathieu-Daudé #else
59bcdb9064SPhilippe Mathieu-Daudé # define DPRINTF_C(format, ...) do { } while (0)
60bcdb9064SPhilippe Mathieu-Daudé #endif
61bcdb9064SPhilippe Mathieu-Daudé
62bcdb9064SPhilippe Mathieu-Daudé #define SEC_PER_MIN 60
63bcdb9064SPhilippe Mathieu-Daudé #define MIN_PER_HOUR 60
64bcdb9064SPhilippe Mathieu-Daudé #define SEC_PER_HOUR 3600
65bcdb9064SPhilippe Mathieu-Daudé #define HOUR_PER_DAY 24
66bcdb9064SPhilippe Mathieu-Daudé #define SEC_PER_DAY 86400
67bcdb9064SPhilippe Mathieu-Daudé
68bcdb9064SPhilippe Mathieu-Daudé #define RTC_REINJECT_ON_ACK_COUNT 20
69bcdb9064SPhilippe Mathieu-Daudé #define RTC_CLOCK_RATE 32768
70bcdb9064SPhilippe Mathieu-Daudé #define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768)
71bcdb9064SPhilippe Mathieu-Daudé
725b21b331SBernhard Beschow #define RTC_ISA_BASE 0x70
735b21b331SBernhard Beschow
748df71297SPhilippe Mathieu-Daudé static void rtc_set_time(MC146818RtcState *s);
758df71297SPhilippe Mathieu-Daudé static void rtc_update_time(MC146818RtcState *s);
768df71297SPhilippe Mathieu-Daudé static void rtc_set_cmos(MC146818RtcState *s, const struct tm *tm);
778df71297SPhilippe Mathieu-Daudé static inline int rtc_from_bcd(MC146818RtcState *s, int a);
788df71297SPhilippe Mathieu-Daudé static uint64_t get_next_alarm(MC146818RtcState *s);
79bcdb9064SPhilippe Mathieu-Daudé
rtc_running(MC146818RtcState * s)808df71297SPhilippe Mathieu-Daudé static inline bool rtc_running(MC146818RtcState *s)
81bcdb9064SPhilippe Mathieu-Daudé {
82bcdb9064SPhilippe Mathieu-Daudé return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) &&
83bcdb9064SPhilippe Mathieu-Daudé (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20);
84bcdb9064SPhilippe Mathieu-Daudé }
85bcdb9064SPhilippe Mathieu-Daudé
get_guest_rtc_ns(MC146818RtcState * s)868df71297SPhilippe Mathieu-Daudé static uint64_t get_guest_rtc_ns(MC146818RtcState *s)
87bcdb9064SPhilippe Mathieu-Daudé {
88bcdb9064SPhilippe Mathieu-Daudé uint64_t guest_clock = qemu_clock_get_ns(rtc_clock);
89bcdb9064SPhilippe Mathieu-Daudé
90bcdb9064SPhilippe Mathieu-Daudé return s->base_rtc * NANOSECONDS_PER_SECOND +
91bcdb9064SPhilippe Mathieu-Daudé guest_clock - s->last_update + s->offset;
92bcdb9064SPhilippe Mathieu-Daudé }
93bcdb9064SPhilippe Mathieu-Daudé
rtc_coalesced_timer_update(MC146818RtcState * s)948df71297SPhilippe Mathieu-Daudé static void rtc_coalesced_timer_update(MC146818RtcState *s)
95bcdb9064SPhilippe Mathieu-Daudé {
96bcdb9064SPhilippe Mathieu-Daudé if (s->irq_coalesced == 0) {
97bcdb9064SPhilippe Mathieu-Daudé timer_del(s->coalesced_timer);
98bcdb9064SPhilippe Mathieu-Daudé } else {
99bcdb9064SPhilippe Mathieu-Daudé /* divide each RTC interval to 2 - 8 smaller intervals */
100bcdb9064SPhilippe Mathieu-Daudé int c = MIN(s->irq_coalesced, 7) + 1;
101bcdb9064SPhilippe Mathieu-Daudé int64_t next_clock = qemu_clock_get_ns(rtc_clock) +
102bcdb9064SPhilippe Mathieu-Daudé periodic_clock_to_ns(s->period / c);
103bcdb9064SPhilippe Mathieu-Daudé timer_mod(s->coalesced_timer, next_clock);
104bcdb9064SPhilippe Mathieu-Daudé }
105bcdb9064SPhilippe Mathieu-Daudé }
106bcdb9064SPhilippe Mathieu-Daudé
rtc_reset_reinjection(MC146818RtcState * rtc)107*d0be0ac2SPaolo Bonzini void rtc_reset_reinjection(MC146818RtcState *rtc)
108bcdb9064SPhilippe Mathieu-Daudé {
109*d0be0ac2SPaolo Bonzini rtc->irq_coalesced = 0;
110bcdb9064SPhilippe Mathieu-Daudé }
111bcdb9064SPhilippe Mathieu-Daudé
rtc_policy_slew_deliver_irq(MC146818RtcState * s)1128df71297SPhilippe Mathieu-Daudé static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s)
113bcdb9064SPhilippe Mathieu-Daudé {
1142b85e0cdSThomas Huth kvm_reset_irq_delivered();
115bcdb9064SPhilippe Mathieu-Daudé qemu_irq_raise(s->irq);
1162b85e0cdSThomas Huth return kvm_get_irq_delivered();
117bcdb9064SPhilippe Mathieu-Daudé }
118bcdb9064SPhilippe Mathieu-Daudé
rtc_coalesced_timer(void * opaque)119bcdb9064SPhilippe Mathieu-Daudé static void rtc_coalesced_timer(void *opaque)
120bcdb9064SPhilippe Mathieu-Daudé {
1218df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = opaque;
122bcdb9064SPhilippe Mathieu-Daudé
123bcdb9064SPhilippe Mathieu-Daudé if (s->irq_coalesced != 0) {
124bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] |= 0xc0;
125bcdb9064SPhilippe Mathieu-Daudé DPRINTF_C("cmos: injecting from timer\n");
126bcdb9064SPhilippe Mathieu-Daudé if (rtc_policy_slew_deliver_irq(s)) {
127bcdb9064SPhilippe Mathieu-Daudé s->irq_coalesced--;
128bcdb9064SPhilippe Mathieu-Daudé DPRINTF_C("cmos: coalesced irqs decreased to %d\n",
129bcdb9064SPhilippe Mathieu-Daudé s->irq_coalesced);
130bcdb9064SPhilippe Mathieu-Daudé }
131bcdb9064SPhilippe Mathieu-Daudé }
132bcdb9064SPhilippe Mathieu-Daudé
133bcdb9064SPhilippe Mathieu-Daudé rtc_coalesced_timer_update(s);
134bcdb9064SPhilippe Mathieu-Daudé }
135bcdb9064SPhilippe Mathieu-Daudé
rtc_periodic_clock_ticks(MC146818RtcState * s)1368df71297SPhilippe Mathieu-Daudé static uint32_t rtc_periodic_clock_ticks(MC146818RtcState *s)
137bcdb9064SPhilippe Mathieu-Daudé {
138bcdb9064SPhilippe Mathieu-Daudé int period_code;
139bcdb9064SPhilippe Mathieu-Daudé
140bcdb9064SPhilippe Mathieu-Daudé if (!(s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
141bcdb9064SPhilippe Mathieu-Daudé return 0;
142bcdb9064SPhilippe Mathieu-Daudé }
143bcdb9064SPhilippe Mathieu-Daudé
144bcdb9064SPhilippe Mathieu-Daudé period_code = s->cmos_data[RTC_REG_A] & 0x0f;
145bcdb9064SPhilippe Mathieu-Daudé
146bcdb9064SPhilippe Mathieu-Daudé return periodic_period_to_clock(period_code);
147bcdb9064SPhilippe Mathieu-Daudé }
148bcdb9064SPhilippe Mathieu-Daudé
149bcdb9064SPhilippe Mathieu-Daudé /*
150bcdb9064SPhilippe Mathieu-Daudé * handle periodic timer. @old_period indicates the periodic timer update
151bcdb9064SPhilippe Mathieu-Daudé * is just due to period adjustment.
152bcdb9064SPhilippe Mathieu-Daudé */
periodic_timer_update(MC146818RtcState * s,int64_t current_time,uint32_t old_period,bool period_change)1538df71297SPhilippe Mathieu-Daudé static void periodic_timer_update(MC146818RtcState *s, int64_t current_time,
1548df71297SPhilippe Mathieu-Daudé uint32_t old_period, bool period_change)
155bcdb9064SPhilippe Mathieu-Daudé {
156bcdb9064SPhilippe Mathieu-Daudé uint32_t period;
157bcdb9064SPhilippe Mathieu-Daudé int64_t cur_clock, next_irq_clock, lost_clock = 0;
158bcdb9064SPhilippe Mathieu-Daudé
159bcdb9064SPhilippe Mathieu-Daudé period = rtc_periodic_clock_ticks(s);
1607a3e29b1SPaolo Bonzini s->period = period;
1617a3e29b1SPaolo Bonzini
162673652a7SPaolo Bonzini if (!period) {
163673652a7SPaolo Bonzini s->irq_coalesced = 0;
164673652a7SPaolo Bonzini timer_del(s->periodic_timer);
165673652a7SPaolo Bonzini return;
166673652a7SPaolo Bonzini }
167673652a7SPaolo Bonzini
168bcdb9064SPhilippe Mathieu-Daudé /* compute 32 khz clock */
169bcdb9064SPhilippe Mathieu-Daudé cur_clock =
170bcdb9064SPhilippe Mathieu-Daudé muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND);
171bcdb9064SPhilippe Mathieu-Daudé
172bcdb9064SPhilippe Mathieu-Daudé /*
173bcdb9064SPhilippe Mathieu-Daudé * if the periodic timer's update is due to period re-configuration,
174bcdb9064SPhilippe Mathieu-Daudé * we should count the clock since last interrupt.
175bcdb9064SPhilippe Mathieu-Daudé */
1767a3e29b1SPaolo Bonzini if (old_period && period_change) {
177bcdb9064SPhilippe Mathieu-Daudé int64_t last_periodic_clock, next_periodic_clock;
178bcdb9064SPhilippe Mathieu-Daudé
179bcdb9064SPhilippe Mathieu-Daudé next_periodic_clock = muldiv64(s->next_periodic_time,
180bcdb9064SPhilippe Mathieu-Daudé RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND);
181bcdb9064SPhilippe Mathieu-Daudé last_periodic_clock = next_periodic_clock - old_period;
182bcdb9064SPhilippe Mathieu-Daudé lost_clock = cur_clock - last_periodic_clock;
183bcdb9064SPhilippe Mathieu-Daudé assert(lost_clock >= 0);
1843ae32adfSPaolo Bonzini }
185bcdb9064SPhilippe Mathieu-Daudé
186bcdb9064SPhilippe Mathieu-Daudé /*
187bcdb9064SPhilippe Mathieu-Daudé * s->irq_coalesced can change for two reasons:
188bcdb9064SPhilippe Mathieu-Daudé *
189bcdb9064SPhilippe Mathieu-Daudé * a) if one or more periodic timer interrupts have been lost,
190bcdb9064SPhilippe Mathieu-Daudé * lost_clock will be more that a period.
191bcdb9064SPhilippe Mathieu-Daudé *
192bcdb9064SPhilippe Mathieu-Daudé * b) when the period may be reconfigured, we expect the OS to
193bcdb9064SPhilippe Mathieu-Daudé * treat delayed tick as the new period. So, when switching
194bcdb9064SPhilippe Mathieu-Daudé * from a shorter to a longer period, scale down the missing,
195bcdb9064SPhilippe Mathieu-Daudé * because the OS will treat past delayed ticks as longer
196bcdb9064SPhilippe Mathieu-Daudé * (leftovers are put back into lost_clock). When switching
197bcdb9064SPhilippe Mathieu-Daudé * to a shorter period, scale up the missing ticks since the
198bcdb9064SPhilippe Mathieu-Daudé * OS handler will treat past delayed ticks as shorter.
199bcdb9064SPhilippe Mathieu-Daudé */
200bcdb9064SPhilippe Mathieu-Daudé if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) {
201bcdb9064SPhilippe Mathieu-Daudé uint32_t old_irq_coalesced = s->irq_coalesced;
202bcdb9064SPhilippe Mathieu-Daudé
203bcdb9064SPhilippe Mathieu-Daudé lost_clock += old_irq_coalesced * old_period;
204bcdb9064SPhilippe Mathieu-Daudé s->irq_coalesced = lost_clock / s->period;
205bcdb9064SPhilippe Mathieu-Daudé lost_clock %= s->period;
206bcdb9064SPhilippe Mathieu-Daudé if (old_irq_coalesced != s->irq_coalesced ||
207bcdb9064SPhilippe Mathieu-Daudé old_period != s->period) {
208bcdb9064SPhilippe Mathieu-Daudé DPRINTF_C("cmos: coalesced irqs scaled from %d to %d, "
209bcdb9064SPhilippe Mathieu-Daudé "period scaled from %d to %d\n", old_irq_coalesced,
210bcdb9064SPhilippe Mathieu-Daudé s->irq_coalesced, old_period, s->period);
211bcdb9064SPhilippe Mathieu-Daudé rtc_coalesced_timer_update(s);
212bcdb9064SPhilippe Mathieu-Daudé }
213bcdb9064SPhilippe Mathieu-Daudé } else {
214bcdb9064SPhilippe Mathieu-Daudé /*
215bcdb9064SPhilippe Mathieu-Daudé * no way to compensate the interrupt if LOST_TICK_POLICY_SLEW
216bcdb9064SPhilippe Mathieu-Daudé * is not used, we should make the time progress anyway.
217bcdb9064SPhilippe Mathieu-Daudé */
218bcdb9064SPhilippe Mathieu-Daudé lost_clock = MIN(lost_clock, period);
219bcdb9064SPhilippe Mathieu-Daudé }
220bcdb9064SPhilippe Mathieu-Daudé
221bcdb9064SPhilippe Mathieu-Daudé assert(lost_clock >= 0 && lost_clock <= period);
222bcdb9064SPhilippe Mathieu-Daudé
223bcdb9064SPhilippe Mathieu-Daudé next_irq_clock = cur_clock + period - lost_clock;
224bcdb9064SPhilippe Mathieu-Daudé s->next_periodic_time = periodic_clock_to_ns(next_irq_clock) + 1;
225bcdb9064SPhilippe Mathieu-Daudé timer_mod(s->periodic_timer, s->next_periodic_time);
226bcdb9064SPhilippe Mathieu-Daudé }
227bcdb9064SPhilippe Mathieu-Daudé
rtc_periodic_timer(void * opaque)228bcdb9064SPhilippe Mathieu-Daudé static void rtc_periodic_timer(void *opaque)
229bcdb9064SPhilippe Mathieu-Daudé {
2308df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = opaque;
231bcdb9064SPhilippe Mathieu-Daudé
2327a3e29b1SPaolo Bonzini periodic_timer_update(s, s->next_periodic_time, s->period, false);
233bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] |= REG_C_PF;
234bcdb9064SPhilippe Mathieu-Daudé if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
235bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
236bcdb9064SPhilippe Mathieu-Daudé if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) {
237bcdb9064SPhilippe Mathieu-Daudé if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT)
238bcdb9064SPhilippe Mathieu-Daudé s->irq_reinject_on_ack_count = 0;
239bcdb9064SPhilippe Mathieu-Daudé if (!rtc_policy_slew_deliver_irq(s)) {
240bcdb9064SPhilippe Mathieu-Daudé s->irq_coalesced++;
241bcdb9064SPhilippe Mathieu-Daudé rtc_coalesced_timer_update(s);
242bcdb9064SPhilippe Mathieu-Daudé DPRINTF_C("cmos: coalesced irqs increased to %d\n",
243bcdb9064SPhilippe Mathieu-Daudé s->irq_coalesced);
244bcdb9064SPhilippe Mathieu-Daudé }
245bcdb9064SPhilippe Mathieu-Daudé } else
246bcdb9064SPhilippe Mathieu-Daudé qemu_irq_raise(s->irq);
247bcdb9064SPhilippe Mathieu-Daudé }
248bcdb9064SPhilippe Mathieu-Daudé }
249bcdb9064SPhilippe Mathieu-Daudé
250bcdb9064SPhilippe Mathieu-Daudé /* handle update-ended timer */
check_update_timer(MC146818RtcState * s)2518df71297SPhilippe Mathieu-Daudé static void check_update_timer(MC146818RtcState *s)
252bcdb9064SPhilippe Mathieu-Daudé {
253bcdb9064SPhilippe Mathieu-Daudé uint64_t next_update_time;
254bcdb9064SPhilippe Mathieu-Daudé uint64_t guest_nsec;
255bcdb9064SPhilippe Mathieu-Daudé int next_alarm_sec;
256bcdb9064SPhilippe Mathieu-Daudé
257bcdb9064SPhilippe Mathieu-Daudé /* From the data sheet: "Holding the dividers in reset prevents
258bcdb9064SPhilippe Mathieu-Daudé * interrupts from operating, while setting the SET bit allows"
259bcdb9064SPhilippe Mathieu-Daudé * them to occur.
260bcdb9064SPhilippe Mathieu-Daudé */
261bcdb9064SPhilippe Mathieu-Daudé if ((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) {
262bcdb9064SPhilippe Mathieu-Daudé assert((s->cmos_data[RTC_REG_A] & REG_A_UIP) == 0);
263bcdb9064SPhilippe Mathieu-Daudé timer_del(s->update_timer);
264bcdb9064SPhilippe Mathieu-Daudé return;
265bcdb9064SPhilippe Mathieu-Daudé }
266bcdb9064SPhilippe Mathieu-Daudé
267bcdb9064SPhilippe Mathieu-Daudé guest_nsec = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND;
268bcdb9064SPhilippe Mathieu-Daudé next_update_time = qemu_clock_get_ns(rtc_clock)
269bcdb9064SPhilippe Mathieu-Daudé + NANOSECONDS_PER_SECOND - guest_nsec;
270bcdb9064SPhilippe Mathieu-Daudé
271bcdb9064SPhilippe Mathieu-Daudé /* Compute time of next alarm. One second is already accounted
272bcdb9064SPhilippe Mathieu-Daudé * for in next_update_time.
273bcdb9064SPhilippe Mathieu-Daudé */
274bcdb9064SPhilippe Mathieu-Daudé next_alarm_sec = get_next_alarm(s);
275bcdb9064SPhilippe Mathieu-Daudé s->next_alarm_time = next_update_time +
276bcdb9064SPhilippe Mathieu-Daudé (next_alarm_sec - 1) * NANOSECONDS_PER_SECOND;
277bcdb9064SPhilippe Mathieu-Daudé
278bcdb9064SPhilippe Mathieu-Daudé /* If update_in_progress latched the UIP bit, we must keep the timer
279bcdb9064SPhilippe Mathieu-Daudé * programmed to the next second, so that UIP is cleared. Otherwise,
280bcdb9064SPhilippe Mathieu-Daudé * if UF is already set, we might be able to optimize.
281bcdb9064SPhilippe Mathieu-Daudé */
282bcdb9064SPhilippe Mathieu-Daudé if (!(s->cmos_data[RTC_REG_A] & REG_A_UIP) &&
283bcdb9064SPhilippe Mathieu-Daudé (s->cmos_data[RTC_REG_C] & REG_C_UF)) {
284bcdb9064SPhilippe Mathieu-Daudé /* If AF cannot change (i.e. either it is set already, or
285bcdb9064SPhilippe Mathieu-Daudé * SET=1 and then the time is not updated), nothing to do.
286bcdb9064SPhilippe Mathieu-Daudé */
287bcdb9064SPhilippe Mathieu-Daudé if ((s->cmos_data[RTC_REG_B] & REG_B_SET) ||
288bcdb9064SPhilippe Mathieu-Daudé (s->cmos_data[RTC_REG_C] & REG_C_AF)) {
289bcdb9064SPhilippe Mathieu-Daudé timer_del(s->update_timer);
290bcdb9064SPhilippe Mathieu-Daudé return;
291bcdb9064SPhilippe Mathieu-Daudé }
292bcdb9064SPhilippe Mathieu-Daudé
293bcdb9064SPhilippe Mathieu-Daudé /* UF is set, but AF is clear. Program the timer to target
294bcdb9064SPhilippe Mathieu-Daudé * the alarm time. */
295bcdb9064SPhilippe Mathieu-Daudé next_update_time = s->next_alarm_time;
296bcdb9064SPhilippe Mathieu-Daudé }
297bcdb9064SPhilippe Mathieu-Daudé if (next_update_time != timer_expire_time_ns(s->update_timer)) {
298bcdb9064SPhilippe Mathieu-Daudé timer_mod(s->update_timer, next_update_time);
299bcdb9064SPhilippe Mathieu-Daudé }
300bcdb9064SPhilippe Mathieu-Daudé }
301bcdb9064SPhilippe Mathieu-Daudé
convert_hour(MC146818RtcState * s,uint8_t hour)3028df71297SPhilippe Mathieu-Daudé static inline uint8_t convert_hour(MC146818RtcState *s, uint8_t hour)
303bcdb9064SPhilippe Mathieu-Daudé {
304bcdb9064SPhilippe Mathieu-Daudé if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) {
305bcdb9064SPhilippe Mathieu-Daudé hour %= 12;
306bcdb9064SPhilippe Mathieu-Daudé if (s->cmos_data[RTC_HOURS] & 0x80) {
307bcdb9064SPhilippe Mathieu-Daudé hour += 12;
308bcdb9064SPhilippe Mathieu-Daudé }
309bcdb9064SPhilippe Mathieu-Daudé }
310bcdb9064SPhilippe Mathieu-Daudé return hour;
311bcdb9064SPhilippe Mathieu-Daudé }
312bcdb9064SPhilippe Mathieu-Daudé
get_next_alarm(MC146818RtcState * s)3138df71297SPhilippe Mathieu-Daudé static uint64_t get_next_alarm(MC146818RtcState *s)
314bcdb9064SPhilippe Mathieu-Daudé {
315bcdb9064SPhilippe Mathieu-Daudé int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec;
316bcdb9064SPhilippe Mathieu-Daudé int32_t hour, min, sec;
317bcdb9064SPhilippe Mathieu-Daudé
318bcdb9064SPhilippe Mathieu-Daudé rtc_update_time(s);
319bcdb9064SPhilippe Mathieu-Daudé
320bcdb9064SPhilippe Mathieu-Daudé alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]);
321bcdb9064SPhilippe Mathieu-Daudé alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]);
322bcdb9064SPhilippe Mathieu-Daudé alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]);
323bcdb9064SPhilippe Mathieu-Daudé alarm_hour = alarm_hour == -1 ? -1 : convert_hour(s, alarm_hour);
324bcdb9064SPhilippe Mathieu-Daudé
325bcdb9064SPhilippe Mathieu-Daudé cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]);
326bcdb9064SPhilippe Mathieu-Daudé cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]);
327bcdb9064SPhilippe Mathieu-Daudé cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]);
328bcdb9064SPhilippe Mathieu-Daudé cur_hour = convert_hour(s, cur_hour);
329bcdb9064SPhilippe Mathieu-Daudé
330bcdb9064SPhilippe Mathieu-Daudé if (alarm_hour == -1) {
331bcdb9064SPhilippe Mathieu-Daudé alarm_hour = cur_hour;
332bcdb9064SPhilippe Mathieu-Daudé if (alarm_min == -1) {
333bcdb9064SPhilippe Mathieu-Daudé alarm_min = cur_min;
334bcdb9064SPhilippe Mathieu-Daudé if (alarm_sec == -1) {
335bcdb9064SPhilippe Mathieu-Daudé alarm_sec = cur_sec + 1;
336bcdb9064SPhilippe Mathieu-Daudé } else if (cur_sec > alarm_sec) {
337bcdb9064SPhilippe Mathieu-Daudé alarm_min++;
338bcdb9064SPhilippe Mathieu-Daudé }
339bcdb9064SPhilippe Mathieu-Daudé } else if (cur_min == alarm_min) {
340bcdb9064SPhilippe Mathieu-Daudé if (alarm_sec == -1) {
341bcdb9064SPhilippe Mathieu-Daudé alarm_sec = cur_sec + 1;
342bcdb9064SPhilippe Mathieu-Daudé } else {
343bcdb9064SPhilippe Mathieu-Daudé if (cur_sec > alarm_sec) {
344bcdb9064SPhilippe Mathieu-Daudé alarm_hour++;
345bcdb9064SPhilippe Mathieu-Daudé }
346bcdb9064SPhilippe Mathieu-Daudé }
347bcdb9064SPhilippe Mathieu-Daudé if (alarm_sec == SEC_PER_MIN) {
348bcdb9064SPhilippe Mathieu-Daudé /* wrap to next hour, minutes is not in don't care mode */
349bcdb9064SPhilippe Mathieu-Daudé alarm_sec = 0;
350bcdb9064SPhilippe Mathieu-Daudé alarm_hour++;
351bcdb9064SPhilippe Mathieu-Daudé }
352bcdb9064SPhilippe Mathieu-Daudé } else if (cur_min > alarm_min) {
353bcdb9064SPhilippe Mathieu-Daudé alarm_hour++;
354bcdb9064SPhilippe Mathieu-Daudé }
355bcdb9064SPhilippe Mathieu-Daudé } else if (cur_hour == alarm_hour) {
356bcdb9064SPhilippe Mathieu-Daudé if (alarm_min == -1) {
357bcdb9064SPhilippe Mathieu-Daudé alarm_min = cur_min;
358bcdb9064SPhilippe Mathieu-Daudé if (alarm_sec == -1) {
359bcdb9064SPhilippe Mathieu-Daudé alarm_sec = cur_sec + 1;
360bcdb9064SPhilippe Mathieu-Daudé } else if (cur_sec > alarm_sec) {
361bcdb9064SPhilippe Mathieu-Daudé alarm_min++;
362bcdb9064SPhilippe Mathieu-Daudé }
363bcdb9064SPhilippe Mathieu-Daudé
364bcdb9064SPhilippe Mathieu-Daudé if (alarm_sec == SEC_PER_MIN) {
365bcdb9064SPhilippe Mathieu-Daudé alarm_sec = 0;
366bcdb9064SPhilippe Mathieu-Daudé alarm_min++;
367bcdb9064SPhilippe Mathieu-Daudé }
368bcdb9064SPhilippe Mathieu-Daudé /* wrap to next day, hour is not in don't care mode */
369bcdb9064SPhilippe Mathieu-Daudé alarm_min %= MIN_PER_HOUR;
370bcdb9064SPhilippe Mathieu-Daudé } else if (cur_min == alarm_min) {
371bcdb9064SPhilippe Mathieu-Daudé if (alarm_sec == -1) {
372bcdb9064SPhilippe Mathieu-Daudé alarm_sec = cur_sec + 1;
373bcdb9064SPhilippe Mathieu-Daudé }
374bcdb9064SPhilippe Mathieu-Daudé /* wrap to next day, hours+minutes not in don't care mode */
375bcdb9064SPhilippe Mathieu-Daudé alarm_sec %= SEC_PER_MIN;
376bcdb9064SPhilippe Mathieu-Daudé }
377bcdb9064SPhilippe Mathieu-Daudé }
378bcdb9064SPhilippe Mathieu-Daudé
379bcdb9064SPhilippe Mathieu-Daudé /* values that are still don't care fire at the next min/sec */
380bcdb9064SPhilippe Mathieu-Daudé if (alarm_min == -1) {
381bcdb9064SPhilippe Mathieu-Daudé alarm_min = 0;
382bcdb9064SPhilippe Mathieu-Daudé }
383bcdb9064SPhilippe Mathieu-Daudé if (alarm_sec == -1) {
384bcdb9064SPhilippe Mathieu-Daudé alarm_sec = 0;
385bcdb9064SPhilippe Mathieu-Daudé }
386bcdb9064SPhilippe Mathieu-Daudé
387bcdb9064SPhilippe Mathieu-Daudé /* keep values in range */
388bcdb9064SPhilippe Mathieu-Daudé if (alarm_sec == SEC_PER_MIN) {
389bcdb9064SPhilippe Mathieu-Daudé alarm_sec = 0;
390bcdb9064SPhilippe Mathieu-Daudé alarm_min++;
391bcdb9064SPhilippe Mathieu-Daudé }
392bcdb9064SPhilippe Mathieu-Daudé if (alarm_min == MIN_PER_HOUR) {
393bcdb9064SPhilippe Mathieu-Daudé alarm_min = 0;
394bcdb9064SPhilippe Mathieu-Daudé alarm_hour++;
395bcdb9064SPhilippe Mathieu-Daudé }
396bcdb9064SPhilippe Mathieu-Daudé alarm_hour %= HOUR_PER_DAY;
397bcdb9064SPhilippe Mathieu-Daudé
398bcdb9064SPhilippe Mathieu-Daudé hour = alarm_hour - cur_hour;
399bcdb9064SPhilippe Mathieu-Daudé min = hour * MIN_PER_HOUR + alarm_min - cur_min;
400bcdb9064SPhilippe Mathieu-Daudé sec = min * SEC_PER_MIN + alarm_sec - cur_sec;
401bcdb9064SPhilippe Mathieu-Daudé return sec <= 0 ? sec + SEC_PER_DAY : sec;
402bcdb9064SPhilippe Mathieu-Daudé }
403bcdb9064SPhilippe Mathieu-Daudé
rtc_update_timer(void * opaque)404bcdb9064SPhilippe Mathieu-Daudé static void rtc_update_timer(void *opaque)
405bcdb9064SPhilippe Mathieu-Daudé {
4068df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = opaque;
407bcdb9064SPhilippe Mathieu-Daudé int32_t irqs = REG_C_UF;
408bcdb9064SPhilippe Mathieu-Daudé int32_t new_irqs;
409bcdb9064SPhilippe Mathieu-Daudé
410bcdb9064SPhilippe Mathieu-Daudé assert((s->cmos_data[RTC_REG_A] & 0x60) != 0x60);
411bcdb9064SPhilippe Mathieu-Daudé
412bcdb9064SPhilippe Mathieu-Daudé /* UIP might have been latched, update time and clear it. */
413bcdb9064SPhilippe Mathieu-Daudé rtc_update_time(s);
414bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
415bcdb9064SPhilippe Mathieu-Daudé
416bcdb9064SPhilippe Mathieu-Daudé if (qemu_clock_get_ns(rtc_clock) >= s->next_alarm_time) {
417bcdb9064SPhilippe Mathieu-Daudé irqs |= REG_C_AF;
418bcdb9064SPhilippe Mathieu-Daudé if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
419bcdb9064SPhilippe Mathieu-Daudé qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC, NULL);
420bcdb9064SPhilippe Mathieu-Daudé }
421bcdb9064SPhilippe Mathieu-Daudé }
422bcdb9064SPhilippe Mathieu-Daudé
423bcdb9064SPhilippe Mathieu-Daudé new_irqs = irqs & ~s->cmos_data[RTC_REG_C];
424bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] |= irqs;
425bcdb9064SPhilippe Mathieu-Daudé if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) {
426bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
427bcdb9064SPhilippe Mathieu-Daudé qemu_irq_raise(s->irq);
428bcdb9064SPhilippe Mathieu-Daudé }
429bcdb9064SPhilippe Mathieu-Daudé check_update_timer(s);
430bcdb9064SPhilippe Mathieu-Daudé }
431bcdb9064SPhilippe Mathieu-Daudé
cmos_ioport_write(void * opaque,hwaddr addr,uint64_t data,unsigned size)432bcdb9064SPhilippe Mathieu-Daudé static void cmos_ioport_write(void *opaque, hwaddr addr,
433bcdb9064SPhilippe Mathieu-Daudé uint64_t data, unsigned size)
434bcdb9064SPhilippe Mathieu-Daudé {
4358df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = opaque;
436bcdb9064SPhilippe Mathieu-Daudé uint32_t old_period;
437bcdb9064SPhilippe Mathieu-Daudé bool update_periodic_timer;
438bcdb9064SPhilippe Mathieu-Daudé
439bcdb9064SPhilippe Mathieu-Daudé if ((addr & 1) == 0) {
440bcdb9064SPhilippe Mathieu-Daudé s->cmos_index = data & 0x7f;
441bcdb9064SPhilippe Mathieu-Daudé } else {
442bcdb9064SPhilippe Mathieu-Daudé CMOS_DPRINTF("cmos: write index=0x%02x val=0x%02" PRIx64 "\n",
443bcdb9064SPhilippe Mathieu-Daudé s->cmos_index, data);
444bcdb9064SPhilippe Mathieu-Daudé switch(s->cmos_index) {
445bcdb9064SPhilippe Mathieu-Daudé case RTC_SECONDS_ALARM:
446bcdb9064SPhilippe Mathieu-Daudé case RTC_MINUTES_ALARM:
447bcdb9064SPhilippe Mathieu-Daudé case RTC_HOURS_ALARM:
448bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[s->cmos_index] = data;
449bcdb9064SPhilippe Mathieu-Daudé check_update_timer(s);
450bcdb9064SPhilippe Mathieu-Daudé break;
451bcdb9064SPhilippe Mathieu-Daudé case RTC_IBM_PS2_CENTURY_BYTE:
452bcdb9064SPhilippe Mathieu-Daudé s->cmos_index = RTC_CENTURY;
453bcdb9064SPhilippe Mathieu-Daudé /* fall through */
454bcdb9064SPhilippe Mathieu-Daudé case RTC_CENTURY:
455bcdb9064SPhilippe Mathieu-Daudé case RTC_SECONDS:
456bcdb9064SPhilippe Mathieu-Daudé case RTC_MINUTES:
457bcdb9064SPhilippe Mathieu-Daudé case RTC_HOURS:
458bcdb9064SPhilippe Mathieu-Daudé case RTC_DAY_OF_WEEK:
459bcdb9064SPhilippe Mathieu-Daudé case RTC_DAY_OF_MONTH:
460bcdb9064SPhilippe Mathieu-Daudé case RTC_MONTH:
461bcdb9064SPhilippe Mathieu-Daudé case RTC_YEAR:
462bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[s->cmos_index] = data;
463bcdb9064SPhilippe Mathieu-Daudé /* if in set mode, do not update the time */
464bcdb9064SPhilippe Mathieu-Daudé if (rtc_running(s)) {
465bcdb9064SPhilippe Mathieu-Daudé rtc_set_time(s);
466bcdb9064SPhilippe Mathieu-Daudé check_update_timer(s);
467bcdb9064SPhilippe Mathieu-Daudé }
468bcdb9064SPhilippe Mathieu-Daudé break;
469bcdb9064SPhilippe Mathieu-Daudé case RTC_REG_A:
470bcdb9064SPhilippe Mathieu-Daudé update_periodic_timer = (s->cmos_data[RTC_REG_A] ^ data) & 0x0f;
471bcdb9064SPhilippe Mathieu-Daudé old_period = rtc_periodic_clock_ticks(s);
472bcdb9064SPhilippe Mathieu-Daudé
473bcdb9064SPhilippe Mathieu-Daudé if ((data & 0x60) == 0x60) {
474bcdb9064SPhilippe Mathieu-Daudé if (rtc_running(s)) {
475bcdb9064SPhilippe Mathieu-Daudé rtc_update_time(s);
476bcdb9064SPhilippe Mathieu-Daudé }
477bcdb9064SPhilippe Mathieu-Daudé /* What happens to UIP when divider reset is enabled is
478bcdb9064SPhilippe Mathieu-Daudé * unclear from the datasheet. Shouldn't matter much
479bcdb9064SPhilippe Mathieu-Daudé * though.
480bcdb9064SPhilippe Mathieu-Daudé */
481bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
482bcdb9064SPhilippe Mathieu-Daudé } else if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) &&
483bcdb9064SPhilippe Mathieu-Daudé (data & 0x70) <= 0x20) {
484bcdb9064SPhilippe Mathieu-Daudé /* when the divider reset is removed, the first update cycle
485bcdb9064SPhilippe Mathieu-Daudé * begins one-half second later*/
486bcdb9064SPhilippe Mathieu-Daudé if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
487bcdb9064SPhilippe Mathieu-Daudé s->offset = 500000000;
488bcdb9064SPhilippe Mathieu-Daudé rtc_set_time(s);
489bcdb9064SPhilippe Mathieu-Daudé }
490bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
491bcdb9064SPhilippe Mathieu-Daudé }
492bcdb9064SPhilippe Mathieu-Daudé /* UIP bit is read only */
493bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
494bcdb9064SPhilippe Mathieu-Daudé (s->cmos_data[RTC_REG_A] & REG_A_UIP);
495bcdb9064SPhilippe Mathieu-Daudé
496bcdb9064SPhilippe Mathieu-Daudé if (update_periodic_timer) {
497bcdb9064SPhilippe Mathieu-Daudé periodic_timer_update(s, qemu_clock_get_ns(rtc_clock),
4987a3e29b1SPaolo Bonzini old_period, true);
499bcdb9064SPhilippe Mathieu-Daudé }
500bcdb9064SPhilippe Mathieu-Daudé
501bcdb9064SPhilippe Mathieu-Daudé check_update_timer(s);
502bcdb9064SPhilippe Mathieu-Daudé break;
503bcdb9064SPhilippe Mathieu-Daudé case RTC_REG_B:
504bcdb9064SPhilippe Mathieu-Daudé update_periodic_timer = (s->cmos_data[RTC_REG_B] ^ data)
505bcdb9064SPhilippe Mathieu-Daudé & REG_B_PIE;
506bcdb9064SPhilippe Mathieu-Daudé old_period = rtc_periodic_clock_ticks(s);
507bcdb9064SPhilippe Mathieu-Daudé
508bcdb9064SPhilippe Mathieu-Daudé if (data & REG_B_SET) {
509bcdb9064SPhilippe Mathieu-Daudé /* update cmos to when the rtc was stopping */
510bcdb9064SPhilippe Mathieu-Daudé if (rtc_running(s)) {
511bcdb9064SPhilippe Mathieu-Daudé rtc_update_time(s);
512bcdb9064SPhilippe Mathieu-Daudé }
513bcdb9064SPhilippe Mathieu-Daudé /* set mode: reset UIP mode */
514bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
515bcdb9064SPhilippe Mathieu-Daudé data &= ~REG_B_UIE;
516bcdb9064SPhilippe Mathieu-Daudé } else {
517bcdb9064SPhilippe Mathieu-Daudé /* if disabling set mode, update the time */
518bcdb9064SPhilippe Mathieu-Daudé if ((s->cmos_data[RTC_REG_B] & REG_B_SET) &&
519bcdb9064SPhilippe Mathieu-Daudé (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) {
520bcdb9064SPhilippe Mathieu-Daudé s->offset = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND;
521bcdb9064SPhilippe Mathieu-Daudé rtc_set_time(s);
522bcdb9064SPhilippe Mathieu-Daudé }
523bcdb9064SPhilippe Mathieu-Daudé }
524bcdb9064SPhilippe Mathieu-Daudé /* if an interrupt flag is already set when the interrupt
525bcdb9064SPhilippe Mathieu-Daudé * becomes enabled, raise an interrupt immediately. */
526bcdb9064SPhilippe Mathieu-Daudé if (data & s->cmos_data[RTC_REG_C] & REG_C_MASK) {
527bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
528bcdb9064SPhilippe Mathieu-Daudé qemu_irq_raise(s->irq);
529bcdb9064SPhilippe Mathieu-Daudé } else {
530bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] &= ~REG_C_IRQF;
531bcdb9064SPhilippe Mathieu-Daudé qemu_irq_lower(s->irq);
532bcdb9064SPhilippe Mathieu-Daudé }
533bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_B] = data;
534bcdb9064SPhilippe Mathieu-Daudé
535bcdb9064SPhilippe Mathieu-Daudé if (update_periodic_timer) {
536bcdb9064SPhilippe Mathieu-Daudé periodic_timer_update(s, qemu_clock_get_ns(rtc_clock),
5377a3e29b1SPaolo Bonzini old_period, true);
538bcdb9064SPhilippe Mathieu-Daudé }
539bcdb9064SPhilippe Mathieu-Daudé
540bcdb9064SPhilippe Mathieu-Daudé check_update_timer(s);
541bcdb9064SPhilippe Mathieu-Daudé break;
542bcdb9064SPhilippe Mathieu-Daudé case RTC_REG_C:
543bcdb9064SPhilippe Mathieu-Daudé case RTC_REG_D:
544bcdb9064SPhilippe Mathieu-Daudé /* cannot write to them */
545bcdb9064SPhilippe Mathieu-Daudé break;
546bcdb9064SPhilippe Mathieu-Daudé default:
547bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[s->cmos_index] = data;
548bcdb9064SPhilippe Mathieu-Daudé break;
549bcdb9064SPhilippe Mathieu-Daudé }
550bcdb9064SPhilippe Mathieu-Daudé }
551bcdb9064SPhilippe Mathieu-Daudé }
552bcdb9064SPhilippe Mathieu-Daudé
rtc_to_bcd(MC146818RtcState * s,int a)5538df71297SPhilippe Mathieu-Daudé static inline int rtc_to_bcd(MC146818RtcState *s, int a)
554bcdb9064SPhilippe Mathieu-Daudé {
555bcdb9064SPhilippe Mathieu-Daudé if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
556bcdb9064SPhilippe Mathieu-Daudé return a;
557bcdb9064SPhilippe Mathieu-Daudé } else {
558bcdb9064SPhilippe Mathieu-Daudé return ((a / 10) << 4) | (a % 10);
559bcdb9064SPhilippe Mathieu-Daudé }
560bcdb9064SPhilippe Mathieu-Daudé }
561bcdb9064SPhilippe Mathieu-Daudé
rtc_from_bcd(MC146818RtcState * s,int a)5628df71297SPhilippe Mathieu-Daudé static inline int rtc_from_bcd(MC146818RtcState *s, int a)
563bcdb9064SPhilippe Mathieu-Daudé {
564bcdb9064SPhilippe Mathieu-Daudé if ((a & 0xc0) == 0xc0) {
565bcdb9064SPhilippe Mathieu-Daudé return -1;
566bcdb9064SPhilippe Mathieu-Daudé }
567bcdb9064SPhilippe Mathieu-Daudé if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
568bcdb9064SPhilippe Mathieu-Daudé return a;
569bcdb9064SPhilippe Mathieu-Daudé } else {
570bcdb9064SPhilippe Mathieu-Daudé return ((a >> 4) * 10) + (a & 0x0f);
571bcdb9064SPhilippe Mathieu-Daudé }
572bcdb9064SPhilippe Mathieu-Daudé }
573bcdb9064SPhilippe Mathieu-Daudé
rtc_get_time(MC146818RtcState * s,struct tm * tm)5748df71297SPhilippe Mathieu-Daudé static void rtc_get_time(MC146818RtcState *s, struct tm *tm)
575bcdb9064SPhilippe Mathieu-Daudé {
576bcdb9064SPhilippe Mathieu-Daudé tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]);
577bcdb9064SPhilippe Mathieu-Daudé tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]);
578bcdb9064SPhilippe Mathieu-Daudé tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
579bcdb9064SPhilippe Mathieu-Daudé if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) {
580bcdb9064SPhilippe Mathieu-Daudé tm->tm_hour %= 12;
581bcdb9064SPhilippe Mathieu-Daudé if (s->cmos_data[RTC_HOURS] & 0x80) {
582bcdb9064SPhilippe Mathieu-Daudé tm->tm_hour += 12;
583bcdb9064SPhilippe Mathieu-Daudé }
584bcdb9064SPhilippe Mathieu-Daudé }
585bcdb9064SPhilippe Mathieu-Daudé tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1;
586bcdb9064SPhilippe Mathieu-Daudé tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
587bcdb9064SPhilippe Mathieu-Daudé tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
588bcdb9064SPhilippe Mathieu-Daudé tm->tm_year =
589bcdb9064SPhilippe Mathieu-Daudé rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year +
590bcdb9064SPhilippe Mathieu-Daudé rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900;
591bcdb9064SPhilippe Mathieu-Daudé }
592bcdb9064SPhilippe Mathieu-Daudé
rtc_set_time(MC146818RtcState * s)5938df71297SPhilippe Mathieu-Daudé static void rtc_set_time(MC146818RtcState *s)
594bcdb9064SPhilippe Mathieu-Daudé {
595394bca2fSVladimir Sementsov-Ogievskiy struct tm tm = {};
5962beb1e5fSMarkus Armbruster g_autofree const char *qom_path = object_get_canonical_path(OBJECT(s));
597bcdb9064SPhilippe Mathieu-Daudé
598bcdb9064SPhilippe Mathieu-Daudé rtc_get_time(s, &tm);
599bcdb9064SPhilippe Mathieu-Daudé s->base_rtc = mktimegm(&tm);
600bcdb9064SPhilippe Mathieu-Daudé s->last_update = qemu_clock_get_ns(rtc_clock);
601bcdb9064SPhilippe Mathieu-Daudé
6022beb1e5fSMarkus Armbruster qapi_event_send_rtc_change(qemu_timedate_diff(&tm), qom_path);
603bcdb9064SPhilippe Mathieu-Daudé }
604bcdb9064SPhilippe Mathieu-Daudé
rtc_set_cmos(MC146818RtcState * s,const struct tm * tm)6058df71297SPhilippe Mathieu-Daudé static void rtc_set_cmos(MC146818RtcState *s, const struct tm *tm)
606bcdb9064SPhilippe Mathieu-Daudé {
607bcdb9064SPhilippe Mathieu-Daudé int year;
608bcdb9064SPhilippe Mathieu-Daudé
609bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec);
610bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min);
611bcdb9064SPhilippe Mathieu-Daudé if (s->cmos_data[RTC_REG_B] & REG_B_24H) {
612bcdb9064SPhilippe Mathieu-Daudé /* 24 hour format */
613bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour);
614bcdb9064SPhilippe Mathieu-Daudé } else {
615bcdb9064SPhilippe Mathieu-Daudé /* 12 hour format */
616bcdb9064SPhilippe Mathieu-Daudé int h = (tm->tm_hour % 12) ? tm->tm_hour % 12 : 12;
617bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, h);
618bcdb9064SPhilippe Mathieu-Daudé if (tm->tm_hour >= 12)
619bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_HOURS] |= 0x80;
620bcdb9064SPhilippe Mathieu-Daudé }
621bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1);
622bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday);
623bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1);
624bcdb9064SPhilippe Mathieu-Daudé year = tm->tm_year + 1900 - s->base_year;
625bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year % 100);
626bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100);
627bcdb9064SPhilippe Mathieu-Daudé }
628bcdb9064SPhilippe Mathieu-Daudé
rtc_update_time(MC146818RtcState * s)6298df71297SPhilippe Mathieu-Daudé static void rtc_update_time(MC146818RtcState *s)
630bcdb9064SPhilippe Mathieu-Daudé {
631bcdb9064SPhilippe Mathieu-Daudé struct tm ret;
632bcdb9064SPhilippe Mathieu-Daudé time_t guest_sec;
633bcdb9064SPhilippe Mathieu-Daudé int64_t guest_nsec;
634bcdb9064SPhilippe Mathieu-Daudé
635bcdb9064SPhilippe Mathieu-Daudé guest_nsec = get_guest_rtc_ns(s);
636bcdb9064SPhilippe Mathieu-Daudé guest_sec = guest_nsec / NANOSECONDS_PER_SECOND;
637bcdb9064SPhilippe Mathieu-Daudé gmtime_r(&guest_sec, &ret);
638bcdb9064SPhilippe Mathieu-Daudé
639bcdb9064SPhilippe Mathieu-Daudé /* Is SET flag of Register B disabled? */
640bcdb9064SPhilippe Mathieu-Daudé if ((s->cmos_data[RTC_REG_B] & REG_B_SET) == 0) {
641bcdb9064SPhilippe Mathieu-Daudé rtc_set_cmos(s, &ret);
642bcdb9064SPhilippe Mathieu-Daudé }
643bcdb9064SPhilippe Mathieu-Daudé }
644bcdb9064SPhilippe Mathieu-Daudé
update_in_progress(MC146818RtcState * s)6458df71297SPhilippe Mathieu-Daudé static int update_in_progress(MC146818RtcState *s)
646bcdb9064SPhilippe Mathieu-Daudé {
647bcdb9064SPhilippe Mathieu-Daudé int64_t guest_nsec;
648bcdb9064SPhilippe Mathieu-Daudé
649bcdb9064SPhilippe Mathieu-Daudé if (!rtc_running(s)) {
650bcdb9064SPhilippe Mathieu-Daudé return 0;
651bcdb9064SPhilippe Mathieu-Daudé }
652bcdb9064SPhilippe Mathieu-Daudé if (timer_pending(s->update_timer)) {
653bcdb9064SPhilippe Mathieu-Daudé int64_t next_update_time = timer_expire_time_ns(s->update_timer);
654bcdb9064SPhilippe Mathieu-Daudé /* Latch UIP until the timer expires. */
655bcdb9064SPhilippe Mathieu-Daudé if (qemu_clock_get_ns(rtc_clock) >=
656bcdb9064SPhilippe Mathieu-Daudé (next_update_time - UIP_HOLD_LENGTH)) {
657bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_A] |= REG_A_UIP;
658bcdb9064SPhilippe Mathieu-Daudé return 1;
659bcdb9064SPhilippe Mathieu-Daudé }
660bcdb9064SPhilippe Mathieu-Daudé }
661bcdb9064SPhilippe Mathieu-Daudé
662bcdb9064SPhilippe Mathieu-Daudé guest_nsec = get_guest_rtc_ns(s);
663bcdb9064SPhilippe Mathieu-Daudé /* UIP bit will be set at last 244us of every second. */
664bcdb9064SPhilippe Mathieu-Daudé if ((guest_nsec % NANOSECONDS_PER_SECOND) >=
665bcdb9064SPhilippe Mathieu-Daudé (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH)) {
666bcdb9064SPhilippe Mathieu-Daudé return 1;
667bcdb9064SPhilippe Mathieu-Daudé }
668bcdb9064SPhilippe Mathieu-Daudé return 0;
669bcdb9064SPhilippe Mathieu-Daudé }
670bcdb9064SPhilippe Mathieu-Daudé
cmos_ioport_read(void * opaque,hwaddr addr,unsigned size)671bcdb9064SPhilippe Mathieu-Daudé static uint64_t cmos_ioport_read(void *opaque, hwaddr addr,
672bcdb9064SPhilippe Mathieu-Daudé unsigned size)
673bcdb9064SPhilippe Mathieu-Daudé {
6748df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = opaque;
675bcdb9064SPhilippe Mathieu-Daudé int ret;
676bcdb9064SPhilippe Mathieu-Daudé if ((addr & 1) == 0) {
677bcdb9064SPhilippe Mathieu-Daudé return 0xff;
678bcdb9064SPhilippe Mathieu-Daudé } else {
679bcdb9064SPhilippe Mathieu-Daudé switch(s->cmos_index) {
680bcdb9064SPhilippe Mathieu-Daudé case RTC_IBM_PS2_CENTURY_BYTE:
681bcdb9064SPhilippe Mathieu-Daudé s->cmos_index = RTC_CENTURY;
682bcdb9064SPhilippe Mathieu-Daudé /* fall through */
683bcdb9064SPhilippe Mathieu-Daudé case RTC_CENTURY:
684bcdb9064SPhilippe Mathieu-Daudé case RTC_SECONDS:
685bcdb9064SPhilippe Mathieu-Daudé case RTC_MINUTES:
686bcdb9064SPhilippe Mathieu-Daudé case RTC_HOURS:
687bcdb9064SPhilippe Mathieu-Daudé case RTC_DAY_OF_WEEK:
688bcdb9064SPhilippe Mathieu-Daudé case RTC_DAY_OF_MONTH:
689bcdb9064SPhilippe Mathieu-Daudé case RTC_MONTH:
690bcdb9064SPhilippe Mathieu-Daudé case RTC_YEAR:
691bcdb9064SPhilippe Mathieu-Daudé /* if not in set mode, calibrate cmos before
692bcdb9064SPhilippe Mathieu-Daudé * reading*/
693bcdb9064SPhilippe Mathieu-Daudé if (rtc_running(s)) {
694bcdb9064SPhilippe Mathieu-Daudé rtc_update_time(s);
695bcdb9064SPhilippe Mathieu-Daudé }
696bcdb9064SPhilippe Mathieu-Daudé ret = s->cmos_data[s->cmos_index];
697bcdb9064SPhilippe Mathieu-Daudé break;
698bcdb9064SPhilippe Mathieu-Daudé case RTC_REG_A:
699bcdb9064SPhilippe Mathieu-Daudé ret = s->cmos_data[s->cmos_index];
700bcdb9064SPhilippe Mathieu-Daudé if (update_in_progress(s)) {
701bcdb9064SPhilippe Mathieu-Daudé ret |= REG_A_UIP;
702bcdb9064SPhilippe Mathieu-Daudé }
703bcdb9064SPhilippe Mathieu-Daudé break;
704bcdb9064SPhilippe Mathieu-Daudé case RTC_REG_C:
705bcdb9064SPhilippe Mathieu-Daudé ret = s->cmos_data[s->cmos_index];
706bcdb9064SPhilippe Mathieu-Daudé qemu_irq_lower(s->irq);
707bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] = 0x00;
708bcdb9064SPhilippe Mathieu-Daudé if (ret & (REG_C_UF | REG_C_AF)) {
709bcdb9064SPhilippe Mathieu-Daudé check_update_timer(s);
710bcdb9064SPhilippe Mathieu-Daudé }
711bcdb9064SPhilippe Mathieu-Daudé
712bcdb9064SPhilippe Mathieu-Daudé if(s->irq_coalesced &&
713bcdb9064SPhilippe Mathieu-Daudé (s->cmos_data[RTC_REG_B] & REG_B_PIE) &&
714bcdb9064SPhilippe Mathieu-Daudé s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) {
715bcdb9064SPhilippe Mathieu-Daudé s->irq_reinject_on_ack_count++;
716bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_PF;
717bcdb9064SPhilippe Mathieu-Daudé DPRINTF_C("cmos: injecting on ack\n");
718bcdb9064SPhilippe Mathieu-Daudé if (rtc_policy_slew_deliver_irq(s)) {
719bcdb9064SPhilippe Mathieu-Daudé s->irq_coalesced--;
720bcdb9064SPhilippe Mathieu-Daudé DPRINTF_C("cmos: coalesced irqs decreased to %d\n",
721bcdb9064SPhilippe Mathieu-Daudé s->irq_coalesced);
722bcdb9064SPhilippe Mathieu-Daudé }
723bcdb9064SPhilippe Mathieu-Daudé }
724bcdb9064SPhilippe Mathieu-Daudé break;
725bcdb9064SPhilippe Mathieu-Daudé default:
726bcdb9064SPhilippe Mathieu-Daudé ret = s->cmos_data[s->cmos_index];
727bcdb9064SPhilippe Mathieu-Daudé break;
728bcdb9064SPhilippe Mathieu-Daudé }
729bcdb9064SPhilippe Mathieu-Daudé CMOS_DPRINTF("cmos: read index=0x%02x val=0x%02x\n",
730bcdb9064SPhilippe Mathieu-Daudé s->cmos_index, ret);
731bcdb9064SPhilippe Mathieu-Daudé return ret;
732bcdb9064SPhilippe Mathieu-Daudé }
733bcdb9064SPhilippe Mathieu-Daudé }
734bcdb9064SPhilippe Mathieu-Daudé
mc146818rtc_set_cmos_data(MC146818RtcState * s,int addr,int val)7352d4bd81eSPhilippe Mathieu-Daudé void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val)
736bcdb9064SPhilippe Mathieu-Daudé {
737bcdb9064SPhilippe Mathieu-Daudé if (addr >= 0 && addr <= 127)
738bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[addr] = val;
739bcdb9064SPhilippe Mathieu-Daudé }
740bcdb9064SPhilippe Mathieu-Daudé
mc146818rtc_get_cmos_data(MC146818RtcState * s,int addr)7412d4bd81eSPhilippe Mathieu-Daudé int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr)
742bcdb9064SPhilippe Mathieu-Daudé {
743bcdb9064SPhilippe Mathieu-Daudé assert(addr >= 0 && addr <= 127);
744bcdb9064SPhilippe Mathieu-Daudé return s->cmos_data[addr];
745bcdb9064SPhilippe Mathieu-Daudé }
746bcdb9064SPhilippe Mathieu-Daudé
rtc_set_date_from_host(ISADevice * dev)747bcdb9064SPhilippe Mathieu-Daudé static void rtc_set_date_from_host(ISADevice *dev)
748bcdb9064SPhilippe Mathieu-Daudé {
7498df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = MC146818_RTC(dev);
750bcdb9064SPhilippe Mathieu-Daudé struct tm tm;
751bcdb9064SPhilippe Mathieu-Daudé
752bcdb9064SPhilippe Mathieu-Daudé qemu_get_timedate(&tm, 0);
753bcdb9064SPhilippe Mathieu-Daudé
754bcdb9064SPhilippe Mathieu-Daudé s->base_rtc = mktimegm(&tm);
755bcdb9064SPhilippe Mathieu-Daudé s->last_update = qemu_clock_get_ns(rtc_clock);
756bcdb9064SPhilippe Mathieu-Daudé s->offset = 0;
757bcdb9064SPhilippe Mathieu-Daudé
758bcdb9064SPhilippe Mathieu-Daudé /* set the CMOS date */
759bcdb9064SPhilippe Mathieu-Daudé rtc_set_cmos(s, &tm);
760bcdb9064SPhilippe Mathieu-Daudé }
761bcdb9064SPhilippe Mathieu-Daudé
rtc_pre_save(void * opaque)762bcdb9064SPhilippe Mathieu-Daudé static int rtc_pre_save(void *opaque)
763bcdb9064SPhilippe Mathieu-Daudé {
7648df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = opaque;
765bcdb9064SPhilippe Mathieu-Daudé
766bcdb9064SPhilippe Mathieu-Daudé rtc_update_time(s);
767bcdb9064SPhilippe Mathieu-Daudé
768bcdb9064SPhilippe Mathieu-Daudé return 0;
769bcdb9064SPhilippe Mathieu-Daudé }
770bcdb9064SPhilippe Mathieu-Daudé
rtc_post_load(void * opaque,int version_id)771bcdb9064SPhilippe Mathieu-Daudé static int rtc_post_load(void *opaque, int version_id)
772bcdb9064SPhilippe Mathieu-Daudé {
7738df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = opaque;
774bcdb9064SPhilippe Mathieu-Daudé
775bcdb9064SPhilippe Mathieu-Daudé if (version_id <= 2 || rtc_clock == QEMU_CLOCK_REALTIME) {
776bcdb9064SPhilippe Mathieu-Daudé rtc_set_time(s);
777bcdb9064SPhilippe Mathieu-Daudé s->offset = 0;
778bcdb9064SPhilippe Mathieu-Daudé check_update_timer(s);
779bcdb9064SPhilippe Mathieu-Daudé }
7807a3e29b1SPaolo Bonzini s->period = rtc_periodic_clock_ticks(s);
781bcdb9064SPhilippe Mathieu-Daudé
782bcdb9064SPhilippe Mathieu-Daudé /* The periodic timer is deterministic in record/replay mode,
783bcdb9064SPhilippe Mathieu-Daudé * so there is no need to update it after loading the vmstate.
784bcdb9064SPhilippe Mathieu-Daudé * Reading RTC here would misalign record and replay.
785bcdb9064SPhilippe Mathieu-Daudé */
786bcdb9064SPhilippe Mathieu-Daudé if (replay_mode == REPLAY_MODE_NONE) {
787bcdb9064SPhilippe Mathieu-Daudé uint64_t now = qemu_clock_get_ns(rtc_clock);
788bcdb9064SPhilippe Mathieu-Daudé if (now < s->next_periodic_time ||
789bcdb9064SPhilippe Mathieu-Daudé now > (s->next_periodic_time + get_max_clock_jump())) {
7907a3e29b1SPaolo Bonzini periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), s->period, false);
791bcdb9064SPhilippe Mathieu-Daudé }
792bcdb9064SPhilippe Mathieu-Daudé }
793bcdb9064SPhilippe Mathieu-Daudé
794bcdb9064SPhilippe Mathieu-Daudé if (version_id >= 2) {
795bcdb9064SPhilippe Mathieu-Daudé if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) {
796bcdb9064SPhilippe Mathieu-Daudé rtc_coalesced_timer_update(s);
797bcdb9064SPhilippe Mathieu-Daudé }
798bcdb9064SPhilippe Mathieu-Daudé }
799bcdb9064SPhilippe Mathieu-Daudé return 0;
800bcdb9064SPhilippe Mathieu-Daudé }
801bcdb9064SPhilippe Mathieu-Daudé
rtc_irq_reinject_on_ack_count_needed(void * opaque)802bcdb9064SPhilippe Mathieu-Daudé static bool rtc_irq_reinject_on_ack_count_needed(void *opaque)
803bcdb9064SPhilippe Mathieu-Daudé {
8048df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = (MC146818RtcState *)opaque;
805bcdb9064SPhilippe Mathieu-Daudé return s->irq_reinject_on_ack_count != 0;
806bcdb9064SPhilippe Mathieu-Daudé }
807bcdb9064SPhilippe Mathieu-Daudé
808bcdb9064SPhilippe Mathieu-Daudé static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = {
809bcdb9064SPhilippe Mathieu-Daudé .name = "mc146818rtc/irq_reinject_on_ack_count",
810bcdb9064SPhilippe Mathieu-Daudé .version_id = 1,
811bcdb9064SPhilippe Mathieu-Daudé .minimum_version_id = 1,
812bcdb9064SPhilippe Mathieu-Daudé .needed = rtc_irq_reinject_on_ack_count_needed,
813a80cc662SRichard Henderson .fields = (const VMStateField[]) {
8148df71297SPhilippe Mathieu-Daudé VMSTATE_UINT16(irq_reinject_on_ack_count, MC146818RtcState),
815bcdb9064SPhilippe Mathieu-Daudé VMSTATE_END_OF_LIST()
816bcdb9064SPhilippe Mathieu-Daudé }
817bcdb9064SPhilippe Mathieu-Daudé };
818bcdb9064SPhilippe Mathieu-Daudé
819bcdb9064SPhilippe Mathieu-Daudé static const VMStateDescription vmstate_rtc = {
820bcdb9064SPhilippe Mathieu-Daudé .name = "mc146818rtc",
821bcdb9064SPhilippe Mathieu-Daudé .version_id = 3,
822bcdb9064SPhilippe Mathieu-Daudé .minimum_version_id = 1,
823bcdb9064SPhilippe Mathieu-Daudé .pre_save = rtc_pre_save,
824bcdb9064SPhilippe Mathieu-Daudé .post_load = rtc_post_load,
825a80cc662SRichard Henderson .fields = (const VMStateField[]) {
8268df71297SPhilippe Mathieu-Daudé VMSTATE_BUFFER(cmos_data, MC146818RtcState),
8278df71297SPhilippe Mathieu-Daudé VMSTATE_UINT8(cmos_index, MC146818RtcState),
828bcdb9064SPhilippe Mathieu-Daudé VMSTATE_UNUSED(7*4),
8298df71297SPhilippe Mathieu-Daudé VMSTATE_TIMER_PTR(periodic_timer, MC146818RtcState),
8308df71297SPhilippe Mathieu-Daudé VMSTATE_INT64(next_periodic_time, MC146818RtcState),
831bcdb9064SPhilippe Mathieu-Daudé VMSTATE_UNUSED(3*8),
8328df71297SPhilippe Mathieu-Daudé VMSTATE_UINT32_V(irq_coalesced, MC146818RtcState, 2),
8338df71297SPhilippe Mathieu-Daudé VMSTATE_UINT32_V(period, MC146818RtcState, 2),
8348df71297SPhilippe Mathieu-Daudé VMSTATE_UINT64_V(base_rtc, MC146818RtcState, 3),
8358df71297SPhilippe Mathieu-Daudé VMSTATE_UINT64_V(last_update, MC146818RtcState, 3),
8368df71297SPhilippe Mathieu-Daudé VMSTATE_INT64_V(offset, MC146818RtcState, 3),
8378df71297SPhilippe Mathieu-Daudé VMSTATE_TIMER_PTR_V(update_timer, MC146818RtcState, 3),
8388df71297SPhilippe Mathieu-Daudé VMSTATE_UINT64_V(next_alarm_time, MC146818RtcState, 3),
839bcdb9064SPhilippe Mathieu-Daudé VMSTATE_END_OF_LIST()
840bcdb9064SPhilippe Mathieu-Daudé },
841a80cc662SRichard Henderson .subsections = (const VMStateDescription * const []) {
842bcdb9064SPhilippe Mathieu-Daudé &vmstate_rtc_irq_reinject_on_ack_count,
843bcdb9064SPhilippe Mathieu-Daudé NULL
844bcdb9064SPhilippe Mathieu-Daudé }
845bcdb9064SPhilippe Mathieu-Daudé };
846bcdb9064SPhilippe Mathieu-Daudé
847bcdb9064SPhilippe Mathieu-Daudé /* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
848bcdb9064SPhilippe Mathieu-Daudé BIOS will read it and start S3 resume at POST Entry */
rtc_notify_suspend(Notifier * notifier,void * data)849bcdb9064SPhilippe Mathieu-Daudé static void rtc_notify_suspend(Notifier *notifier, void *data)
850bcdb9064SPhilippe Mathieu-Daudé {
8518df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = container_of(notifier, MC146818RtcState,
8528df71297SPhilippe Mathieu-Daudé suspend_notifier);
8532d4bd81eSPhilippe Mathieu-Daudé mc146818rtc_set_cmos_data(s, 0xF, 0xFE);
854bcdb9064SPhilippe Mathieu-Daudé }
855bcdb9064SPhilippe Mathieu-Daudé
856bcdb9064SPhilippe Mathieu-Daudé static const MemoryRegionOps cmos_ops = {
857bcdb9064SPhilippe Mathieu-Daudé .read = cmos_ioport_read,
858bcdb9064SPhilippe Mathieu-Daudé .write = cmos_ioport_write,
859bcdb9064SPhilippe Mathieu-Daudé .impl = {
860bcdb9064SPhilippe Mathieu-Daudé .min_access_size = 1,
861bcdb9064SPhilippe Mathieu-Daudé .max_access_size = 1,
862bcdb9064SPhilippe Mathieu-Daudé },
863bcdb9064SPhilippe Mathieu-Daudé .endianness = DEVICE_LITTLE_ENDIAN,
864bcdb9064SPhilippe Mathieu-Daudé };
865bcdb9064SPhilippe Mathieu-Daudé
rtc_get_date(Object * obj,struct tm * current_tm,Error ** errp)866bcdb9064SPhilippe Mathieu-Daudé static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp)
867bcdb9064SPhilippe Mathieu-Daudé {
8688df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = MC146818_RTC(obj);
869bcdb9064SPhilippe Mathieu-Daudé
870bcdb9064SPhilippe Mathieu-Daudé rtc_update_time(s);
871bcdb9064SPhilippe Mathieu-Daudé rtc_get_time(s, current_tm);
872bcdb9064SPhilippe Mathieu-Daudé }
873bcdb9064SPhilippe Mathieu-Daudé
rtc_realizefn(DeviceState * dev,Error ** errp)874bcdb9064SPhilippe Mathieu-Daudé static void rtc_realizefn(DeviceState *dev, Error **errp)
875bcdb9064SPhilippe Mathieu-Daudé {
876bcdb9064SPhilippe Mathieu-Daudé ISADevice *isadev = ISA_DEVICE(dev);
8778df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = MC146818_RTC(dev);
878bcdb9064SPhilippe Mathieu-Daudé
879bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_A] = 0x26;
880bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_B] = 0x02;
881bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] = 0x00;
882bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_D] = 0x80;
883bcdb9064SPhilippe Mathieu-Daudé
884bcdb9064SPhilippe Mathieu-Daudé /* This is for historical reasons. The default base year qdev property
885bcdb9064SPhilippe Mathieu-Daudé * was set to 2000 for most machine types before the century byte was
886bcdb9064SPhilippe Mathieu-Daudé * implemented.
887bcdb9064SPhilippe Mathieu-Daudé *
888bcdb9064SPhilippe Mathieu-Daudé * This if statement means that the century byte will be always 0
889bcdb9064SPhilippe Mathieu-Daudé * (at least until 2079...) for base_year = 1980, but will be set
890bcdb9064SPhilippe Mathieu-Daudé * correctly for base_year = 2000.
891bcdb9064SPhilippe Mathieu-Daudé */
892bcdb9064SPhilippe Mathieu-Daudé if (s->base_year == 2000) {
893bcdb9064SPhilippe Mathieu-Daudé s->base_year = 0;
894bcdb9064SPhilippe Mathieu-Daudé }
895bcdb9064SPhilippe Mathieu-Daudé
8963b004a16SBernhard Beschow if (s->isairq >= ISA_NUM_IRQS) {
8973b004a16SBernhard Beschow error_setg(errp, "Maximum value for \"irq\" is: %u", ISA_NUM_IRQS - 1);
8983b004a16SBernhard Beschow return;
8993b004a16SBernhard Beschow }
9003b004a16SBernhard Beschow
901bcdb9064SPhilippe Mathieu-Daudé rtc_set_date_from_host(isadev);
902bcdb9064SPhilippe Mathieu-Daudé
903bcdb9064SPhilippe Mathieu-Daudé switch (s->lost_tick_policy) {
904bcdb9064SPhilippe Mathieu-Daudé case LOST_TICK_POLICY_SLEW:
905bcdb9064SPhilippe Mathieu-Daudé s->coalesced_timer =
906bcdb9064SPhilippe Mathieu-Daudé timer_new_ns(rtc_clock, rtc_coalesced_timer, s);
907bcdb9064SPhilippe Mathieu-Daudé break;
908bcdb9064SPhilippe Mathieu-Daudé case LOST_TICK_POLICY_DISCARD:
909bcdb9064SPhilippe Mathieu-Daudé break;
910bcdb9064SPhilippe Mathieu-Daudé default:
911bcdb9064SPhilippe Mathieu-Daudé error_setg(errp, "Invalid lost tick policy.");
912bcdb9064SPhilippe Mathieu-Daudé return;
913bcdb9064SPhilippe Mathieu-Daudé }
914bcdb9064SPhilippe Mathieu-Daudé
915bcdb9064SPhilippe Mathieu-Daudé s->periodic_timer = timer_new_ns(rtc_clock, rtc_periodic_timer, s);
916bcdb9064SPhilippe Mathieu-Daudé s->update_timer = timer_new_ns(rtc_clock, rtc_update_timer, s);
917bcdb9064SPhilippe Mathieu-Daudé check_update_timer(s);
918bcdb9064SPhilippe Mathieu-Daudé
919bcdb9064SPhilippe Mathieu-Daudé s->suspend_notifier.notify = rtc_notify_suspend;
920bcdb9064SPhilippe Mathieu-Daudé qemu_register_suspend_notifier(&s->suspend_notifier);
921bcdb9064SPhilippe Mathieu-Daudé
922bcdb9064SPhilippe Mathieu-Daudé memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2);
9235b21b331SBernhard Beschow isa_register_ioport(isadev, &s->io, s->io_base);
924bcdb9064SPhilippe Mathieu-Daudé
925bcdb9064SPhilippe Mathieu-Daudé /* register rtc 0x70 port for coalesced_pio */
926bcdb9064SPhilippe Mathieu-Daudé memory_region_set_flush_coalesced(&s->io);
927bcdb9064SPhilippe Mathieu-Daudé memory_region_init_io(&s->coalesced_io, OBJECT(s), &cmos_ops,
928bcdb9064SPhilippe Mathieu-Daudé s, "rtc-index", 1);
929bcdb9064SPhilippe Mathieu-Daudé memory_region_add_subregion(&s->io, 0, &s->coalesced_io);
930bcdb9064SPhilippe Mathieu-Daudé memory_region_add_coalescing(&s->coalesced_io, 0, 1);
931bcdb9064SPhilippe Mathieu-Daudé
9325b21b331SBernhard Beschow qdev_set_legacy_instance_id(dev, s->io_base, 3);
933bcdb9064SPhilippe Mathieu-Daudé
934d2623129SMarkus Armbruster object_property_add_tm(OBJECT(s), "date", rtc_get_date);
935bcdb9064SPhilippe Mathieu-Daudé
936bcdb9064SPhilippe Mathieu-Daudé qdev_init_gpio_out(dev, &s->irq, 1);
937bcdb9064SPhilippe Mathieu-Daudé }
938bcdb9064SPhilippe Mathieu-Daudé
mc146818_rtc_init(ISABus * bus,int base_year,qemu_irq intercept_irq)93955c86cb8SPhilippe Mathieu-Daudé MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year,
94055c86cb8SPhilippe Mathieu-Daudé qemu_irq intercept_irq)
941bcdb9064SPhilippe Mathieu-Daudé {
942bcdb9064SPhilippe Mathieu-Daudé DeviceState *dev;
943bcdb9064SPhilippe Mathieu-Daudé ISADevice *isadev;
9448df71297SPhilippe Mathieu-Daudé MC146818RtcState *s;
945bcdb9064SPhilippe Mathieu-Daudé
94696927c74SMarkus Armbruster isadev = isa_new(TYPE_MC146818_RTC);
947bcdb9064SPhilippe Mathieu-Daudé dev = DEVICE(isadev);
9483b004a16SBernhard Beschow s = MC146818_RTC(isadev);
949bcdb9064SPhilippe Mathieu-Daudé qdev_prop_set_int32(dev, "base_year", base_year);
95096927c74SMarkus Armbruster isa_realize_and_unref(isadev, bus, &error_fatal);
951bcdb9064SPhilippe Mathieu-Daudé if (intercept_irq) {
952bcdb9064SPhilippe Mathieu-Daudé qdev_connect_gpio_out(dev, 0, intercept_irq);
953bcdb9064SPhilippe Mathieu-Daudé } else {
9543b004a16SBernhard Beschow isa_connect_gpio_out(isadev, 0, s->isairq);
955bcdb9064SPhilippe Mathieu-Daudé }
956bcdb9064SPhilippe Mathieu-Daudé
957673652a7SPaolo Bonzini object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(isadev),
958d2623129SMarkus Armbruster "date");
959bcdb9064SPhilippe Mathieu-Daudé
96055c86cb8SPhilippe Mathieu-Daudé return s;
961bcdb9064SPhilippe Mathieu-Daudé }
962bcdb9064SPhilippe Mathieu-Daudé
963bcdb9064SPhilippe Mathieu-Daudé static Property mc146818rtc_properties[] = {
9648df71297SPhilippe Mathieu-Daudé DEFINE_PROP_INT32("base_year", MC146818RtcState, base_year, 1980),
9658df71297SPhilippe Mathieu-Daudé DEFINE_PROP_UINT16("iobase", MC146818RtcState, io_base, RTC_ISA_BASE),
9668df71297SPhilippe Mathieu-Daudé DEFINE_PROP_UINT8("irq", MC146818RtcState, isairq, RTC_ISA_IRQ),
9678df71297SPhilippe Mathieu-Daudé DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", MC146818RtcState,
968bcdb9064SPhilippe Mathieu-Daudé lost_tick_policy, LOST_TICK_POLICY_DISCARD),
969bcdb9064SPhilippe Mathieu-Daudé DEFINE_PROP_END_OF_LIST(),
970bcdb9064SPhilippe Mathieu-Daudé };
971bcdb9064SPhilippe Mathieu-Daudé
rtc_reset_enter(Object * obj,ResetType type)972fae5a042SPhilippe Mathieu-Daudé static void rtc_reset_enter(Object *obj, ResetType type)
973bcdb9064SPhilippe Mathieu-Daudé {
9748df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = MC146818_RTC(obj);
975bcdb9064SPhilippe Mathieu-Daudé
976bcdb9064SPhilippe Mathieu-Daudé /* Reason: VM do suspend self will set 0xfe
977bcdb9064SPhilippe Mathieu-Daudé * Reset any values other than 0xfe(Guest suspend case) */
978bcdb9064SPhilippe Mathieu-Daudé if (s->cmos_data[0x0f] != 0xfe) {
979bcdb9064SPhilippe Mathieu-Daudé s->cmos_data[0x0f] = 0x00;
980bcdb9064SPhilippe Mathieu-Daudé }
981fae5a042SPhilippe Mathieu-Daudé
982fae5a042SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE);
983fae5a042SPhilippe Mathieu-Daudé s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF);
984fae5a042SPhilippe Mathieu-Daudé check_update_timer(s);
985fae5a042SPhilippe Mathieu-Daudé
986fae5a042SPhilippe Mathieu-Daudé
987fae5a042SPhilippe Mathieu-Daudé if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) {
988fae5a042SPhilippe Mathieu-Daudé s->irq_coalesced = 0;
989fae5a042SPhilippe Mathieu-Daudé s->irq_reinject_on_ack_count = 0;
990fae5a042SPhilippe Mathieu-Daudé }
991fae5a042SPhilippe Mathieu-Daudé }
992fae5a042SPhilippe Mathieu-Daudé
rtc_reset_hold(Object * obj,ResetType type)993ad80e367SPeter Maydell static void rtc_reset_hold(Object *obj, ResetType type)
994fae5a042SPhilippe Mathieu-Daudé {
9958df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = MC146818_RTC(obj);
996fae5a042SPhilippe Mathieu-Daudé
997fae5a042SPhilippe Mathieu-Daudé qemu_irq_lower(s->irq);
998bcdb9064SPhilippe Mathieu-Daudé }
999bcdb9064SPhilippe Mathieu-Daudé
rtc_build_aml(AcpiDevAmlIf * adev,Aml * scope)1000d9cf178cSIgor Mammedov static void rtc_build_aml(AcpiDevAmlIf *adev, Aml *scope)
1001df9b9b42SGerd Hoffmann {
10028df71297SPhilippe Mathieu-Daudé MC146818RtcState *s = MC146818_RTC(adev);
1003df9b9b42SGerd Hoffmann Aml *dev;
1004df9b9b42SGerd Hoffmann Aml *crs;
1005df9b9b42SGerd Hoffmann
1006f592b94fSGerd Hoffmann /*
1007f592b94fSGerd Hoffmann * Reserving 8 io ports here, following what physical hardware
1008f592b94fSGerd Hoffmann * does, even though qemu only responds to the first two ports.
1009f592b94fSGerd Hoffmann */
1010df9b9b42SGerd Hoffmann crs = aml_resource_template();
10115b21b331SBernhard Beschow aml_append(crs, aml_io(AML_DECODE16, s->io_base, s->io_base,
1012f592b94fSGerd Hoffmann 0x01, 0x08));
10133b004a16SBernhard Beschow aml_append(crs, aml_irq_no_flags(s->isairq));
1014df9b9b42SGerd Hoffmann
1015df9b9b42SGerd Hoffmann dev = aml_device("RTC");
1016df9b9b42SGerd Hoffmann aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0B00")));
1017df9b9b42SGerd Hoffmann aml_append(dev, aml_name_decl("_CRS", crs));
1018df9b9b42SGerd Hoffmann
1019df9b9b42SGerd Hoffmann aml_append(scope, dev);
1020df9b9b42SGerd Hoffmann }
1021df9b9b42SGerd Hoffmann
rtc_class_initfn(ObjectClass * klass,void * data)1022bcdb9064SPhilippe Mathieu-Daudé static void rtc_class_initfn(ObjectClass *klass, void *data)
1023bcdb9064SPhilippe Mathieu-Daudé {
1024bcdb9064SPhilippe Mathieu-Daudé DeviceClass *dc = DEVICE_CLASS(klass);
1025fae5a042SPhilippe Mathieu-Daudé ResettableClass *rc = RESETTABLE_CLASS(klass);
1026d9cf178cSIgor Mammedov AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
1027bcdb9064SPhilippe Mathieu-Daudé
1028bcdb9064SPhilippe Mathieu-Daudé dc->realize = rtc_realizefn;
1029bcdb9064SPhilippe Mathieu-Daudé dc->vmsd = &vmstate_rtc;
1030fae5a042SPhilippe Mathieu-Daudé rc->phases.enter = rtc_reset_enter;
1031fae5a042SPhilippe Mathieu-Daudé rc->phases.hold = rtc_reset_hold;
1032d9cf178cSIgor Mammedov adevc->build_dev_aml = rtc_build_aml;
10334f67d30bSMarc-André Lureau device_class_set_props(dc, mc146818rtc_properties);
103476d79cf3SGan Qixin set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1035bcdb9064SPhilippe Mathieu-Daudé }
1036bcdb9064SPhilippe Mathieu-Daudé
1037bcdb9064SPhilippe Mathieu-Daudé static const TypeInfo mc146818rtc_info = {
1038bcdb9064SPhilippe Mathieu-Daudé .name = TYPE_MC146818_RTC,
1039bcdb9064SPhilippe Mathieu-Daudé .parent = TYPE_ISA_DEVICE,
10408df71297SPhilippe Mathieu-Daudé .instance_size = sizeof(MC146818RtcState),
1041bcdb9064SPhilippe Mathieu-Daudé .class_init = rtc_class_initfn,
1042d9cf178cSIgor Mammedov .interfaces = (InterfaceInfo[]) {
1043d9cf178cSIgor Mammedov { TYPE_ACPI_DEV_AML_IF },
1044d9cf178cSIgor Mammedov { },
1045d9cf178cSIgor Mammedov },
1046bcdb9064SPhilippe Mathieu-Daudé };
1047bcdb9064SPhilippe Mathieu-Daudé
mc146818rtc_register_types(void)1048bcdb9064SPhilippe Mathieu-Daudé static void mc146818rtc_register_types(void)
1049bcdb9064SPhilippe Mathieu-Daudé {
1050bcdb9064SPhilippe Mathieu-Daudé type_register_static(&mc146818rtc_info);
1051bcdb9064SPhilippe Mathieu-Daudé }
1052bcdb9064SPhilippe Mathieu-Daudé
1053bcdb9064SPhilippe Mathieu-Daudé type_init(mc146818rtc_register_types)
1054