root / hw / mc146818rtc.c @ 5e22c276
History | View | Annotate | Download (27.7 kB)
1 | 80cabfad | bellard | /*
|
---|---|---|---|
2 | 80cabfad | bellard | * QEMU MC146818 RTC emulation
|
3 | 5fafdf24 | ths | *
|
4 | 80cabfad | bellard | * Copyright (c) 2003-2004 Fabrice Bellard
|
5 | 5fafdf24 | ths | *
|
6 | 80cabfad | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 80cabfad | bellard | * of this software and associated documentation files (the "Software"), to deal
|
8 | 80cabfad | bellard | * in the Software without restriction, including without limitation the rights
|
9 | 80cabfad | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 80cabfad | bellard | * copies of the Software, and to permit persons to whom the Software is
|
11 | 80cabfad | bellard | * furnished to do so, subject to the following conditions:
|
12 | 80cabfad | bellard | *
|
13 | 80cabfad | bellard | * The above copyright notice and this permission notice shall be included in
|
14 | 80cabfad | bellard | * all copies or substantial portions of the Software.
|
15 | 80cabfad | bellard | *
|
16 | 80cabfad | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 80cabfad | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 80cabfad | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 80cabfad | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 80cabfad | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 80cabfad | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 80cabfad | bellard | * THE SOFTWARE.
|
23 | 80cabfad | bellard | */
|
24 | 87ecb68b | pbrook | #include "hw.h" |
25 | 1de7afc9 | Paolo Bonzini | #include "qemu/timer.h" |
26 | 9c17d615 | Paolo Bonzini | #include "sysemu/sysemu.h" |
27 | 1d914fa0 | Isaku Yamahata | #include "mc146818rtc.h" |
28 | 7b1b5d19 | Paolo Bonzini | #include "qapi/visitor.h" |
29 | 80cabfad | bellard | |
30 | d362e757 | Jan Kiszka | #ifdef TARGET_I386
|
31 | d362e757 | Jan Kiszka | #include "apic.h" |
32 | d362e757 | Jan Kiszka | #endif
|
33 | d362e757 | Jan Kiszka | |
34 | 80cabfad | bellard | //#define DEBUG_CMOS
|
35 | aa6f63ff | Blue Swirl | //#define DEBUG_COALESCED
|
36 | 80cabfad | bellard | |
37 | ec51e364 | Isaku Yamahata | #ifdef DEBUG_CMOS
|
38 | ec51e364 | Isaku Yamahata | # define CMOS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) |
39 | ec51e364 | Isaku Yamahata | #else
|
40 | ec51e364 | Isaku Yamahata | # define CMOS_DPRINTF(format, ...) do { } while (0) |
41 | ec51e364 | Isaku Yamahata | #endif
|
42 | ec51e364 | Isaku Yamahata | |
43 | aa6f63ff | Blue Swirl | #ifdef DEBUG_COALESCED
|
44 | aa6f63ff | Blue Swirl | # define DPRINTF_C(format, ...) printf(format, ## __VA_ARGS__) |
45 | aa6f63ff | Blue Swirl | #else
|
46 | aa6f63ff | Blue Swirl | # define DPRINTF_C(format, ...) do { } while (0) |
47 | aa6f63ff | Blue Swirl | #endif
|
48 | aa6f63ff | Blue Swirl | |
49 | 56038ef6 | Yang Zhang | #define NSEC_PER_SEC 1000000000LL |
50 | 00cf5774 | Paolo Bonzini | #define SEC_PER_MIN 60 |
51 | 00cf5774 | Paolo Bonzini | #define MIN_PER_HOUR 60 |
52 | 00cf5774 | Paolo Bonzini | #define SEC_PER_HOUR 3600 |
53 | 00cf5774 | Paolo Bonzini | #define HOUR_PER_DAY 24 |
54 | 00cf5774 | Paolo Bonzini | #define SEC_PER_DAY 86400 |
55 | 56038ef6 | Yang Zhang | |
56 | dd17765b | Gleb Natapov | #define RTC_REINJECT_ON_ACK_COUNT 20 |
57 | e46deaba | Paolo Bonzini | #define RTC_CLOCK_RATE 32768 |
58 | 56038ef6 | Yang Zhang | #define UIP_HOLD_LENGTH (8 * NSEC_PER_SEC / 32768) |
59 | ba32edab | Gleb Natapov | |
60 | 1d914fa0 | Isaku Yamahata | typedef struct RTCState { |
61 | 32e0c826 | Gerd Hoffmann | ISADevice dev; |
62 | b2c5009b | Richard Henderson | MemoryRegion io; |
63 | dff38e7b | bellard | uint8_t cmos_data[128];
|
64 | dff38e7b | bellard | uint8_t cmos_index; |
65 | 32e0c826 | Gerd Hoffmann | int32_t base_year; |
66 | 56038ef6 | Yang Zhang | uint64_t base_rtc; |
67 | 56038ef6 | Yang Zhang | uint64_t last_update; |
68 | 56038ef6 | Yang Zhang | int64_t offset; |
69 | d537cf6c | pbrook | qemu_irq irq; |
70 | 100d9891 | aurel32 | qemu_irq sqw_irq; |
71 | 18c6e2ff | ths | int it_shift;
|
72 | dff38e7b | bellard | /* periodic timer */
|
73 | dff38e7b | bellard | QEMUTimer *periodic_timer; |
74 | dff38e7b | bellard | int64_t next_periodic_time; |
75 | 56038ef6 | Yang Zhang | /* update-ended timer */
|
76 | 56038ef6 | Yang Zhang | QEMUTimer *update_timer; |
77 | 00cf5774 | Paolo Bonzini | uint64_t next_alarm_time; |
78 | ba32edab | Gleb Natapov | uint16_t irq_reinject_on_ack_count; |
79 | 73822ec8 | aliguori | uint32_t irq_coalesced; |
80 | 73822ec8 | aliguori | uint32_t period; |
81 | 93b66569 | aliguori | QEMUTimer *coalesced_timer; |
82 | 17604dac | Jan Kiszka | Notifier clock_reset_notifier; |
83 | 433acf0d | Jan Kiszka | LostTickPolicy lost_tick_policy; |
84 | da98c8eb | Gerd Hoffmann | Notifier suspend_notifier; |
85 | 1d914fa0 | Isaku Yamahata | } RTCState; |
86 | dff38e7b | bellard | |
87 | dff38e7b | bellard | static void rtc_set_time(RTCState *s); |
88 | 56038ef6 | Yang Zhang | static void rtc_update_time(RTCState *s); |
89 | e2826cf4 | Paolo Bonzini | static void rtc_set_cmos(RTCState *s, const struct tm *tm); |
90 | 56038ef6 | Yang Zhang | static inline int rtc_from_bcd(RTCState *s, int a); |
91 | 00cf5774 | Paolo Bonzini | static uint64_t get_next_alarm(RTCState *s);
|
92 | 56038ef6 | Yang Zhang | |
93 | 41a9b8b2 | Yang Zhang | static inline bool rtc_running(RTCState *s) |
94 | 41a9b8b2 | Yang Zhang | { |
95 | 41a9b8b2 | Yang Zhang | return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) &&
|
96 | 41a9b8b2 | Yang Zhang | (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20); |
97 | 41a9b8b2 | Yang Zhang | } |
98 | 41a9b8b2 | Yang Zhang | |
99 | 56038ef6 | Yang Zhang | static uint64_t get_guest_rtc_ns(RTCState *s)
|
100 | 56038ef6 | Yang Zhang | { |
101 | 56038ef6 | Yang Zhang | uint64_t guest_rtc; |
102 | 56038ef6 | Yang Zhang | uint64_t guest_clock = qemu_get_clock_ns(rtc_clock); |
103 | 56038ef6 | Yang Zhang | |
104 | 56038ef6 | Yang Zhang | guest_rtc = s->base_rtc * NSEC_PER_SEC |
105 | 56038ef6 | Yang Zhang | + guest_clock - s->last_update + s->offset; |
106 | 56038ef6 | Yang Zhang | return guest_rtc;
|
107 | 56038ef6 | Yang Zhang | } |
108 | dff38e7b | bellard | |
109 | 93b66569 | aliguori | #ifdef TARGET_I386
|
110 | 93b66569 | aliguori | static void rtc_coalesced_timer_update(RTCState *s) |
111 | 93b66569 | aliguori | { |
112 | 93b66569 | aliguori | if (s->irq_coalesced == 0) { |
113 | 93b66569 | aliguori | qemu_del_timer(s->coalesced_timer); |
114 | 93b66569 | aliguori | } else {
|
115 | 93b66569 | aliguori | /* divide each RTC interval to 2 - 8 smaller intervals */
|
116 | 93b66569 | aliguori | int c = MIN(s->irq_coalesced, 7) + 1; |
117 | 74475455 | Paolo Bonzini | int64_t next_clock = qemu_get_clock_ns(rtc_clock) + |
118 | e46deaba | Paolo Bonzini | muldiv64(s->period / c, get_ticks_per_sec(), RTC_CLOCK_RATE); |
119 | 93b66569 | aliguori | qemu_mod_timer(s->coalesced_timer, next_clock); |
120 | 93b66569 | aliguori | } |
121 | 93b66569 | aliguori | } |
122 | 93b66569 | aliguori | |
123 | 93b66569 | aliguori | static void rtc_coalesced_timer(void *opaque) |
124 | 93b66569 | aliguori | { |
125 | 93b66569 | aliguori | RTCState *s = opaque; |
126 | 93b66569 | aliguori | |
127 | 93b66569 | aliguori | if (s->irq_coalesced != 0) { |
128 | 93b66569 | aliguori | apic_reset_irq_delivered(); |
129 | 93b66569 | aliguori | s->cmos_data[RTC_REG_C] |= 0xc0;
|
130 | aa6f63ff | Blue Swirl | DPRINTF_C("cmos: injecting from timer\n");
|
131 | 7d932dfd | Jan Kiszka | qemu_irq_raise(s->irq); |
132 | 93b66569 | aliguori | if (apic_get_irq_delivered()) {
|
133 | 93b66569 | aliguori | s->irq_coalesced--; |
134 | aa6f63ff | Blue Swirl | DPRINTF_C("cmos: coalesced irqs decreased to %d\n",
|
135 | aa6f63ff | Blue Swirl | s->irq_coalesced); |
136 | 93b66569 | aliguori | } |
137 | 93b66569 | aliguori | } |
138 | 93b66569 | aliguori | |
139 | 93b66569 | aliguori | rtc_coalesced_timer_update(s); |
140 | 93b66569 | aliguori | } |
141 | 93b66569 | aliguori | #endif
|
142 | 93b66569 | aliguori | |
143 | 56038ef6 | Yang Zhang | /* handle periodic timer */
|
144 | c4c18e24 | Yang Zhang | static void periodic_timer_update(RTCState *s, int64_t current_time) |
145 | dff38e7b | bellard | { |
146 | dff38e7b | bellard | int period_code, period;
|
147 | dff38e7b | bellard | int64_t cur_clock, next_irq_clock; |
148 | dff38e7b | bellard | |
149 | dff38e7b | bellard | period_code = s->cmos_data[RTC_REG_A] & 0x0f;
|
150 | 100d9891 | aurel32 | if (period_code != 0 |
151 | 7d932dfd | Jan Kiszka | && ((s->cmos_data[RTC_REG_B] & REG_B_PIE) |
152 | 100d9891 | aurel32 | || ((s->cmos_data[RTC_REG_B] & REG_B_SQWE) && s->sqw_irq))) { |
153 | dff38e7b | bellard | if (period_code <= 2) |
154 | dff38e7b | bellard | period_code += 7;
|
155 | dff38e7b | bellard | /* period in 32 Khz cycles */
|
156 | dff38e7b | bellard | period = 1 << (period_code - 1); |
157 | 73822ec8 | aliguori | #ifdef TARGET_I386
|
158 | aa6f63ff | Blue Swirl | if (period != s->period) {
|
159 | 73822ec8 | aliguori | s->irq_coalesced = (s->irq_coalesced * s->period) / period; |
160 | aa6f63ff | Blue Swirl | DPRINTF_C("cmos: coalesced irqs scaled to %d\n", s->irq_coalesced);
|
161 | aa6f63ff | Blue Swirl | } |
162 | 73822ec8 | aliguori | s->period = period; |
163 | 73822ec8 | aliguori | #endif
|
164 | dff38e7b | bellard | /* compute 32 khz clock */
|
165 | e46deaba | Paolo Bonzini | cur_clock = muldiv64(current_time, RTC_CLOCK_RATE, get_ticks_per_sec()); |
166 | dff38e7b | bellard | next_irq_clock = (cur_clock & ~(period - 1)) + period;
|
167 | 6875204c | Jan Kiszka | s->next_periodic_time = |
168 | e46deaba | Paolo Bonzini | muldiv64(next_irq_clock, get_ticks_per_sec(), RTC_CLOCK_RATE) + 1;
|
169 | dff38e7b | bellard | qemu_mod_timer(s->periodic_timer, s->next_periodic_time); |
170 | dff38e7b | bellard | } else {
|
171 | 73822ec8 | aliguori | #ifdef TARGET_I386
|
172 | 73822ec8 | aliguori | s->irq_coalesced = 0;
|
173 | 73822ec8 | aliguori | #endif
|
174 | dff38e7b | bellard | qemu_del_timer(s->periodic_timer); |
175 | dff38e7b | bellard | } |
176 | dff38e7b | bellard | } |
177 | dff38e7b | bellard | |
178 | dff38e7b | bellard | static void rtc_periodic_timer(void *opaque) |
179 | dff38e7b | bellard | { |
180 | dff38e7b | bellard | RTCState *s = opaque; |
181 | dff38e7b | bellard | |
182 | c4c18e24 | Yang Zhang | periodic_timer_update(s, s->next_periodic_time); |
183 | 663447d4 | Paolo Bonzini | s->cmos_data[RTC_REG_C] |= REG_C_PF; |
184 | 100d9891 | aurel32 | if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
|
185 | 663447d4 | Paolo Bonzini | s->cmos_data[RTC_REG_C] |= REG_C_IRQF; |
186 | 93b66569 | aliguori | #ifdef TARGET_I386
|
187 | 433acf0d | Jan Kiszka | if (s->lost_tick_policy == LOST_TICK_SLEW) {
|
188 | ba32edab | Gleb Natapov | if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT)
|
189 | ba32edab | Gleb Natapov | s->irq_reinject_on_ack_count = 0;
|
190 | 93b66569 | aliguori | apic_reset_irq_delivered(); |
191 | 7d932dfd | Jan Kiszka | qemu_irq_raise(s->irq); |
192 | 93b66569 | aliguori | if (!apic_get_irq_delivered()) {
|
193 | 93b66569 | aliguori | s->irq_coalesced++; |
194 | 93b66569 | aliguori | rtc_coalesced_timer_update(s); |
195 | aa6f63ff | Blue Swirl | DPRINTF_C("cmos: coalesced irqs increased to %d\n",
|
196 | aa6f63ff | Blue Swirl | s->irq_coalesced); |
197 | 93b66569 | aliguori | } |
198 | 93b66569 | aliguori | } else
|
199 | 93b66569 | aliguori | #endif
|
200 | 7d932dfd | Jan Kiszka | qemu_irq_raise(s->irq); |
201 | 100d9891 | aurel32 | } |
202 | 100d9891 | aurel32 | if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) {
|
203 | 100d9891 | aurel32 | /* Not square wave at all but we don't want 2048Hz interrupts!
|
204 | 100d9891 | aurel32 | Must be seen as a pulse. */
|
205 | 100d9891 | aurel32 | qemu_irq_raise(s->sqw_irq); |
206 | 100d9891 | aurel32 | } |
207 | dff38e7b | bellard | } |
208 | 80cabfad | bellard | |
209 | 56038ef6 | Yang Zhang | /* handle update-ended timer */
|
210 | 56038ef6 | Yang Zhang | static void check_update_timer(RTCState *s) |
211 | 56038ef6 | Yang Zhang | { |
212 | 56038ef6 | Yang Zhang | uint64_t next_update_time; |
213 | 56038ef6 | Yang Zhang | uint64_t guest_nsec; |
214 | 00cf5774 | Paolo Bonzini | int next_alarm_sec;
|
215 | 56038ef6 | Yang Zhang | |
216 | 41a9b8b2 | Yang Zhang | /* From the data sheet: "Holding the dividers in reset prevents
|
217 | 41a9b8b2 | Yang Zhang | * interrupts from operating, while setting the SET bit allows"
|
218 | 41a9b8b2 | Yang Zhang | * them to occur. However, it will prevent an alarm interrupt
|
219 | 41a9b8b2 | Yang Zhang | * from occurring, because the time of day is not updated.
|
220 | 56038ef6 | Yang Zhang | */
|
221 | 41a9b8b2 | Yang Zhang | if ((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) { |
222 | 41a9b8b2 | Yang Zhang | qemu_del_timer(s->update_timer); |
223 | 41a9b8b2 | Yang Zhang | return;
|
224 | 41a9b8b2 | Yang Zhang | } |
225 | 56038ef6 | Yang Zhang | if ((s->cmos_data[RTC_REG_C] & REG_C_UF) &&
|
226 | 56038ef6 | Yang Zhang | (s->cmos_data[RTC_REG_B] & REG_B_SET)) { |
227 | 56038ef6 | Yang Zhang | qemu_del_timer(s->update_timer); |
228 | 56038ef6 | Yang Zhang | return;
|
229 | 56038ef6 | Yang Zhang | } |
230 | 56038ef6 | Yang Zhang | if ((s->cmos_data[RTC_REG_C] & REG_C_UF) &&
|
231 | 56038ef6 | Yang Zhang | (s->cmos_data[RTC_REG_C] & REG_C_AF)) { |
232 | 56038ef6 | Yang Zhang | qemu_del_timer(s->update_timer); |
233 | 56038ef6 | Yang Zhang | return;
|
234 | 56038ef6 | Yang Zhang | } |
235 | 56038ef6 | Yang Zhang | |
236 | 56038ef6 | Yang Zhang | guest_nsec = get_guest_rtc_ns(s) % NSEC_PER_SEC; |
237 | 00cf5774 | Paolo Bonzini | /* if UF is clear, reprogram to next second */
|
238 | 56038ef6 | Yang Zhang | next_update_time = qemu_get_clock_ns(rtc_clock) |
239 | 56038ef6 | Yang Zhang | + NSEC_PER_SEC - guest_nsec; |
240 | 00cf5774 | Paolo Bonzini | |
241 | 00cf5774 | Paolo Bonzini | /* Compute time of next alarm. One second is already accounted
|
242 | 00cf5774 | Paolo Bonzini | * for in next_update_time.
|
243 | 00cf5774 | Paolo Bonzini | */
|
244 | 00cf5774 | Paolo Bonzini | next_alarm_sec = get_next_alarm(s); |
245 | 00cf5774 | Paolo Bonzini | s->next_alarm_time = next_update_time + (next_alarm_sec - 1) * NSEC_PER_SEC;
|
246 | 00cf5774 | Paolo Bonzini | |
247 | 00cf5774 | Paolo Bonzini | if (s->cmos_data[RTC_REG_C] & REG_C_UF) {
|
248 | 00cf5774 | Paolo Bonzini | /* UF is set, but AF is clear. Program the timer to target
|
249 | 00cf5774 | Paolo Bonzini | * the alarm time. */
|
250 | 00cf5774 | Paolo Bonzini | next_update_time = s->next_alarm_time; |
251 | 00cf5774 | Paolo Bonzini | } |
252 | 56038ef6 | Yang Zhang | if (next_update_time != qemu_timer_expire_time_ns(s->update_timer)) {
|
253 | 56038ef6 | Yang Zhang | qemu_mod_timer(s->update_timer, next_update_time); |
254 | 56038ef6 | Yang Zhang | } |
255 | 56038ef6 | Yang Zhang | } |
256 | 56038ef6 | Yang Zhang | |
257 | 56038ef6 | Yang Zhang | static inline uint8_t convert_hour(RTCState *s, uint8_t hour) |
258 | 56038ef6 | Yang Zhang | { |
259 | 56038ef6 | Yang Zhang | if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) {
|
260 | 56038ef6 | Yang Zhang | hour %= 12;
|
261 | 56038ef6 | Yang Zhang | if (s->cmos_data[RTC_HOURS] & 0x80) { |
262 | 56038ef6 | Yang Zhang | hour += 12;
|
263 | 56038ef6 | Yang Zhang | } |
264 | 56038ef6 | Yang Zhang | } |
265 | 56038ef6 | Yang Zhang | return hour;
|
266 | 56038ef6 | Yang Zhang | } |
267 | 56038ef6 | Yang Zhang | |
268 | 00cf5774 | Paolo Bonzini | static uint64_t get_next_alarm(RTCState *s)
|
269 | 56038ef6 | Yang Zhang | { |
270 | 00cf5774 | Paolo Bonzini | int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; |
271 | 00cf5774 | Paolo Bonzini | int32_t hour, min, sec; |
272 | 00cf5774 | Paolo Bonzini | |
273 | 00cf5774 | Paolo Bonzini | rtc_update_time(s); |
274 | 56038ef6 | Yang Zhang | |
275 | 56038ef6 | Yang Zhang | alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]); |
276 | 56038ef6 | Yang Zhang | alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]); |
277 | 56038ef6 | Yang Zhang | alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]); |
278 | 00cf5774 | Paolo Bonzini | alarm_hour = alarm_hour == -1 ? -1 : convert_hour(s, alarm_hour); |
279 | 56038ef6 | Yang Zhang | |
280 | 56038ef6 | Yang Zhang | cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); |
281 | 56038ef6 | Yang Zhang | cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); |
282 | 56038ef6 | Yang Zhang | cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]); |
283 | 56038ef6 | Yang Zhang | cur_hour = convert_hour(s, cur_hour); |
284 | 56038ef6 | Yang Zhang | |
285 | 00cf5774 | Paolo Bonzini | if (alarm_hour == -1) { |
286 | 00cf5774 | Paolo Bonzini | alarm_hour = cur_hour; |
287 | 00cf5774 | Paolo Bonzini | if (alarm_min == -1) { |
288 | 00cf5774 | Paolo Bonzini | alarm_min = cur_min; |
289 | 00cf5774 | Paolo Bonzini | if (alarm_sec == -1) { |
290 | 00cf5774 | Paolo Bonzini | alarm_sec = cur_sec + 1;
|
291 | 00cf5774 | Paolo Bonzini | } else if (cur_sec > alarm_sec) { |
292 | 00cf5774 | Paolo Bonzini | alarm_min++; |
293 | 00cf5774 | Paolo Bonzini | } |
294 | 00cf5774 | Paolo Bonzini | } else if (cur_min == alarm_min) { |
295 | 00cf5774 | Paolo Bonzini | if (alarm_sec == -1) { |
296 | 00cf5774 | Paolo Bonzini | alarm_sec = cur_sec + 1;
|
297 | 00cf5774 | Paolo Bonzini | } else {
|
298 | 00cf5774 | Paolo Bonzini | if (cur_sec > alarm_sec) {
|
299 | 00cf5774 | Paolo Bonzini | alarm_hour++; |
300 | 00cf5774 | Paolo Bonzini | } |
301 | 00cf5774 | Paolo Bonzini | } |
302 | 00cf5774 | Paolo Bonzini | if (alarm_sec == SEC_PER_MIN) {
|
303 | 00cf5774 | Paolo Bonzini | /* wrap to next hour, minutes is not in don't care mode */
|
304 | 00cf5774 | Paolo Bonzini | alarm_sec = 0;
|
305 | 00cf5774 | Paolo Bonzini | alarm_hour++; |
306 | 00cf5774 | Paolo Bonzini | } |
307 | 00cf5774 | Paolo Bonzini | } else if (cur_min > alarm_min) { |
308 | 00cf5774 | Paolo Bonzini | alarm_hour++; |
309 | 00cf5774 | Paolo Bonzini | } |
310 | 00cf5774 | Paolo Bonzini | } else if (cur_hour == alarm_hour) { |
311 | 00cf5774 | Paolo Bonzini | if (alarm_min == -1) { |
312 | 00cf5774 | Paolo Bonzini | alarm_min = cur_min; |
313 | 00cf5774 | Paolo Bonzini | if (alarm_sec == -1) { |
314 | 00cf5774 | Paolo Bonzini | alarm_sec = cur_sec + 1;
|
315 | 00cf5774 | Paolo Bonzini | } else if (cur_sec > alarm_sec) { |
316 | 00cf5774 | Paolo Bonzini | alarm_min++; |
317 | 00cf5774 | Paolo Bonzini | } |
318 | 00cf5774 | Paolo Bonzini | |
319 | 00cf5774 | Paolo Bonzini | if (alarm_sec == SEC_PER_MIN) {
|
320 | 00cf5774 | Paolo Bonzini | alarm_sec = 0;
|
321 | 00cf5774 | Paolo Bonzini | alarm_min++; |
322 | 00cf5774 | Paolo Bonzini | } |
323 | 00cf5774 | Paolo Bonzini | /* wrap to next day, hour is not in don't care mode */
|
324 | 00cf5774 | Paolo Bonzini | alarm_min %= MIN_PER_HOUR; |
325 | 00cf5774 | Paolo Bonzini | } else if (cur_min == alarm_min) { |
326 | 00cf5774 | Paolo Bonzini | if (alarm_sec == -1) { |
327 | 00cf5774 | Paolo Bonzini | alarm_sec = cur_sec + 1;
|
328 | 00cf5774 | Paolo Bonzini | } |
329 | 00cf5774 | Paolo Bonzini | /* wrap to next day, hours+minutes not in don't care mode */
|
330 | 00cf5774 | Paolo Bonzini | alarm_sec %= SEC_PER_MIN; |
331 | 00cf5774 | Paolo Bonzini | } |
332 | 56038ef6 | Yang Zhang | } |
333 | 56038ef6 | Yang Zhang | |
334 | 00cf5774 | Paolo Bonzini | /* values that are still don't care fire at the next min/sec */
|
335 | 00cf5774 | Paolo Bonzini | if (alarm_min == -1) { |
336 | 00cf5774 | Paolo Bonzini | alarm_min = 0;
|
337 | 00cf5774 | Paolo Bonzini | } |
338 | 00cf5774 | Paolo Bonzini | if (alarm_sec == -1) { |
339 | 00cf5774 | Paolo Bonzini | alarm_sec = 0;
|
340 | 00cf5774 | Paolo Bonzini | } |
341 | 00cf5774 | Paolo Bonzini | |
342 | 00cf5774 | Paolo Bonzini | /* keep values in range */
|
343 | 00cf5774 | Paolo Bonzini | if (alarm_sec == SEC_PER_MIN) {
|
344 | 00cf5774 | Paolo Bonzini | alarm_sec = 0;
|
345 | 00cf5774 | Paolo Bonzini | alarm_min++; |
346 | 00cf5774 | Paolo Bonzini | } |
347 | 00cf5774 | Paolo Bonzini | if (alarm_min == MIN_PER_HOUR) {
|
348 | 00cf5774 | Paolo Bonzini | alarm_min = 0;
|
349 | 00cf5774 | Paolo Bonzini | alarm_hour++; |
350 | 00cf5774 | Paolo Bonzini | } |
351 | 00cf5774 | Paolo Bonzini | alarm_hour %= HOUR_PER_DAY; |
352 | 00cf5774 | Paolo Bonzini | |
353 | 00cf5774 | Paolo Bonzini | hour = alarm_hour - cur_hour; |
354 | 00cf5774 | Paolo Bonzini | min = hour * MIN_PER_HOUR + alarm_min - cur_min; |
355 | 00cf5774 | Paolo Bonzini | sec = min * SEC_PER_MIN + alarm_sec - cur_sec; |
356 | 00cf5774 | Paolo Bonzini | return sec <= 0 ? sec + SEC_PER_DAY : sec; |
357 | 56038ef6 | Yang Zhang | } |
358 | 56038ef6 | Yang Zhang | |
359 | 56038ef6 | Yang Zhang | static void rtc_update_timer(void *opaque) |
360 | 56038ef6 | Yang Zhang | { |
361 | 56038ef6 | Yang Zhang | RTCState *s = opaque; |
362 | 56038ef6 | Yang Zhang | int32_t irqs = REG_C_UF; |
363 | 56038ef6 | Yang Zhang | int32_t new_irqs; |
364 | 56038ef6 | Yang Zhang | |
365 | 41a9b8b2 | Yang Zhang | assert((s->cmos_data[RTC_REG_A] & 0x60) != 0x60); |
366 | 41a9b8b2 | Yang Zhang | |
367 | 56038ef6 | Yang Zhang | /* UIP might have been latched, update time and clear it. */
|
368 | 56038ef6 | Yang Zhang | rtc_update_time(s); |
369 | 56038ef6 | Yang Zhang | s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; |
370 | 56038ef6 | Yang Zhang | |
371 | 00cf5774 | Paolo Bonzini | if (qemu_get_clock_ns(rtc_clock) >= s->next_alarm_time) {
|
372 | 56038ef6 | Yang Zhang | irqs |= REG_C_AF; |
373 | 56038ef6 | Yang Zhang | if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
|
374 | 56038ef6 | Yang Zhang | qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC); |
375 | 56038ef6 | Yang Zhang | } |
376 | 56038ef6 | Yang Zhang | } |
377 | 00cf5774 | Paolo Bonzini | |
378 | 56038ef6 | Yang Zhang | new_irqs = irqs & ~s->cmos_data[RTC_REG_C]; |
379 | 56038ef6 | Yang Zhang | s->cmos_data[RTC_REG_C] |= irqs; |
380 | 56038ef6 | Yang Zhang | if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) { |
381 | 56038ef6 | Yang Zhang | s->cmos_data[RTC_REG_C] |= REG_C_IRQF; |
382 | 56038ef6 | Yang Zhang | qemu_irq_raise(s->irq); |
383 | 56038ef6 | Yang Zhang | } |
384 | 56038ef6 | Yang Zhang | check_update_timer(s); |
385 | 56038ef6 | Yang Zhang | } |
386 | 56038ef6 | Yang Zhang | |
387 | 0da8c842 | Alexander Graf | static void cmos_ioport_write(void *opaque, hwaddr addr, |
388 | 0da8c842 | Alexander Graf | uint64_t data, unsigned size)
|
389 | 80cabfad | bellard | { |
390 | b41a2cd1 | bellard | RTCState *s = opaque; |
391 | 80cabfad | bellard | |
392 | 80cabfad | bellard | if ((addr & 1) == 0) { |
393 | 80cabfad | bellard | s->cmos_index = data & 0x7f;
|
394 | 80cabfad | bellard | } else {
|
395 | ec51e364 | Isaku Yamahata | CMOS_DPRINTF("cmos: write index=0x%02x val=0x%02x\n",
|
396 | ec51e364 | Isaku Yamahata | s->cmos_index, data); |
397 | dff38e7b | bellard | switch(s->cmos_index) {
|
398 | 80cabfad | bellard | case RTC_SECONDS_ALARM:
|
399 | 80cabfad | bellard | case RTC_MINUTES_ALARM:
|
400 | 80cabfad | bellard | case RTC_HOURS_ALARM:
|
401 | 80cabfad | bellard | s->cmos_data[s->cmos_index] = data; |
402 | 56038ef6 | Yang Zhang | check_update_timer(s); |
403 | 80cabfad | bellard | break;
|
404 | e67edb94 | Paolo Bonzini | case RTC_IBM_PS2_CENTURY_BYTE:
|
405 | e67edb94 | Paolo Bonzini | s->cmos_index = RTC_CENTURY; |
406 | e67edb94 | Paolo Bonzini | /* fall through */
|
407 | e67edb94 | Paolo Bonzini | case RTC_CENTURY:
|
408 | 80cabfad | bellard | case RTC_SECONDS:
|
409 | 80cabfad | bellard | case RTC_MINUTES:
|
410 | 80cabfad | bellard | case RTC_HOURS:
|
411 | 80cabfad | bellard | case RTC_DAY_OF_WEEK:
|
412 | 80cabfad | bellard | case RTC_DAY_OF_MONTH:
|
413 | 80cabfad | bellard | case RTC_MONTH:
|
414 | 80cabfad | bellard | case RTC_YEAR:
|
415 | 80cabfad | bellard | s->cmos_data[s->cmos_index] = data; |
416 | dff38e7b | bellard | /* if in set mode, do not update the time */
|
417 | 41a9b8b2 | Yang Zhang | if (rtc_running(s)) {
|
418 | dff38e7b | bellard | rtc_set_time(s); |
419 | 56038ef6 | Yang Zhang | check_update_timer(s); |
420 | dff38e7b | bellard | } |
421 | 80cabfad | bellard | break;
|
422 | 80cabfad | bellard | case RTC_REG_A:
|
423 | 41a9b8b2 | Yang Zhang | if ((data & 0x60) == 0x60) { |
424 | 41a9b8b2 | Yang Zhang | if (rtc_running(s)) {
|
425 | 41a9b8b2 | Yang Zhang | rtc_update_time(s); |
426 | 41a9b8b2 | Yang Zhang | } |
427 | 41a9b8b2 | Yang Zhang | /* What happens to UIP when divider reset is enabled is
|
428 | 41a9b8b2 | Yang Zhang | * unclear from the datasheet. Shouldn't matter much
|
429 | 41a9b8b2 | Yang Zhang | * though.
|
430 | 41a9b8b2 | Yang Zhang | */
|
431 | 41a9b8b2 | Yang Zhang | s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; |
432 | 41a9b8b2 | Yang Zhang | } else if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) && |
433 | 41a9b8b2 | Yang Zhang | (data & 0x70) <= 0x20) { |
434 | 41a9b8b2 | Yang Zhang | /* when the divider reset is removed, the first update cycle
|
435 | 41a9b8b2 | Yang Zhang | * begins one-half second later*/
|
436 | 41a9b8b2 | Yang Zhang | if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
|
437 | 41a9b8b2 | Yang Zhang | s->offset = 500000000;
|
438 | 41a9b8b2 | Yang Zhang | rtc_set_time(s); |
439 | 41a9b8b2 | Yang Zhang | } |
440 | 41a9b8b2 | Yang Zhang | s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; |
441 | 41a9b8b2 | Yang Zhang | } |
442 | dff38e7b | bellard | /* UIP bit is read only */
|
443 | dff38e7b | bellard | s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | |
444 | dff38e7b | bellard | (s->cmos_data[RTC_REG_A] & REG_A_UIP); |
445 | c4c18e24 | Yang Zhang | periodic_timer_update(s, qemu_get_clock_ns(rtc_clock)); |
446 | 56038ef6 | Yang Zhang | check_update_timer(s); |
447 | dff38e7b | bellard | break;
|
448 | 80cabfad | bellard | case RTC_REG_B:
|
449 | dff38e7b | bellard | if (data & REG_B_SET) {
|
450 | 56038ef6 | Yang Zhang | /* update cmos to when the rtc was stopping */
|
451 | 41a9b8b2 | Yang Zhang | if (rtc_running(s)) {
|
452 | 56038ef6 | Yang Zhang | rtc_update_time(s); |
453 | 56038ef6 | Yang Zhang | } |
454 | dff38e7b | bellard | /* set mode: reset UIP mode */
|
455 | dff38e7b | bellard | s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; |
456 | dff38e7b | bellard | data &= ~REG_B_UIE; |
457 | dff38e7b | bellard | } else {
|
458 | dff38e7b | bellard | /* if disabling set mode, update the time */
|
459 | 41a9b8b2 | Yang Zhang | if ((s->cmos_data[RTC_REG_B] & REG_B_SET) &&
|
460 | 41a9b8b2 | Yang Zhang | (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) { |
461 | 56038ef6 | Yang Zhang | s->offset = get_guest_rtc_ns(s) % NSEC_PER_SEC; |
462 | dff38e7b | bellard | rtc_set_time(s); |
463 | dff38e7b | bellard | } |
464 | dff38e7b | bellard | } |
465 | 9324cc50 | Yang Zhang | /* if an interrupt flag is already set when the interrupt
|
466 | 9324cc50 | Yang Zhang | * becomes enabled, raise an interrupt immediately. */
|
467 | 9324cc50 | Yang Zhang | if (data & s->cmos_data[RTC_REG_C] & REG_C_MASK) {
|
468 | 9324cc50 | Yang Zhang | s->cmos_data[RTC_REG_C] |= REG_C_IRQF; |
469 | 9324cc50 | Yang Zhang | qemu_irq_raise(s->irq); |
470 | 9324cc50 | Yang Zhang | } else {
|
471 | 9324cc50 | Yang Zhang | s->cmos_data[RTC_REG_C] &= ~REG_C_IRQF; |
472 | 9324cc50 | Yang Zhang | qemu_irq_lower(s->irq); |
473 | 9324cc50 | Yang Zhang | } |
474 | bedc572e | Yang Zhang | s->cmos_data[RTC_REG_B] = data; |
475 | c4c18e24 | Yang Zhang | periodic_timer_update(s, qemu_get_clock_ns(rtc_clock)); |
476 | 56038ef6 | Yang Zhang | check_update_timer(s); |
477 | 80cabfad | bellard | break;
|
478 | 80cabfad | bellard | case RTC_REG_C:
|
479 | 80cabfad | bellard | case RTC_REG_D:
|
480 | 80cabfad | bellard | /* cannot write to them */
|
481 | 80cabfad | bellard | break;
|
482 | 80cabfad | bellard | default:
|
483 | 80cabfad | bellard | s->cmos_data[s->cmos_index] = data; |
484 | 80cabfad | bellard | break;
|
485 | 80cabfad | bellard | } |
486 | 80cabfad | bellard | } |
487 | 80cabfad | bellard | } |
488 | 80cabfad | bellard | |
489 | abd0c6bd | Paul Brook | static inline int rtc_to_bcd(RTCState *s, int a) |
490 | 80cabfad | bellard | { |
491 | 6f1bf24d | aurel32 | if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
|
492 | dff38e7b | bellard | return a;
|
493 | dff38e7b | bellard | } else {
|
494 | dff38e7b | bellard | return ((a / 10) << 4) | (a % 10); |
495 | dff38e7b | bellard | } |
496 | 80cabfad | bellard | } |
497 | 80cabfad | bellard | |
498 | abd0c6bd | Paul Brook | static inline int rtc_from_bcd(RTCState *s, int a) |
499 | 80cabfad | bellard | { |
500 | 00cf5774 | Paolo Bonzini | if ((a & 0xc0) == 0xc0) { |
501 | 00cf5774 | Paolo Bonzini | return -1; |
502 | 00cf5774 | Paolo Bonzini | } |
503 | 6f1bf24d | aurel32 | if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
|
504 | dff38e7b | bellard | return a;
|
505 | dff38e7b | bellard | } else {
|
506 | dff38e7b | bellard | return ((a >> 4) * 10) + (a & 0x0f); |
507 | dff38e7b | bellard | } |
508 | dff38e7b | bellard | } |
509 | dff38e7b | bellard | |
510 | e2826cf4 | Paolo Bonzini | static void rtc_get_time(RTCState *s, struct tm *tm) |
511 | dff38e7b | bellard | { |
512 | abd0c6bd | Paul Brook | tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); |
513 | abd0c6bd | Paul Brook | tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); |
514 | abd0c6bd | Paul Brook | tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
|
515 | 3b89eb43 | Paolo Bonzini | if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) {
|
516 | 3b89eb43 | Paolo Bonzini | tm->tm_hour %= 12;
|
517 | 3b89eb43 | Paolo Bonzini | if (s->cmos_data[RTC_HOURS] & 0x80) { |
518 | 3b89eb43 | Paolo Bonzini | tm->tm_hour += 12;
|
519 | 3b89eb43 | Paolo Bonzini | } |
520 | 43f493af | bellard | } |
521 | abd0c6bd | Paul Brook | tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1;
|
522 | abd0c6bd | Paul Brook | tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); |
523 | abd0c6bd | Paul Brook | tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
|
524 | b8994faf | Paolo Bonzini | tm->tm_year = |
525 | b8994faf | Paolo Bonzini | rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year + |
526 | b8994faf | Paolo Bonzini | rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; |
527 | e2826cf4 | Paolo Bonzini | } |
528 | e2826cf4 | Paolo Bonzini | |
529 | e2826cf4 | Paolo Bonzini | static void rtc_set_time(RTCState *s) |
530 | e2826cf4 | Paolo Bonzini | { |
531 | e2826cf4 | Paolo Bonzini | struct tm tm;
|
532 | 80cd3478 | Luiz Capitulino | |
533 | e2826cf4 | Paolo Bonzini | rtc_get_time(s, &tm); |
534 | e2826cf4 | Paolo Bonzini | s->base_rtc = mktimegm(&tm); |
535 | 56038ef6 | Yang Zhang | s->last_update = qemu_get_clock_ns(rtc_clock); |
536 | 56038ef6 | Yang Zhang | |
537 | e2826cf4 | Paolo Bonzini | rtc_change_mon_event(&tm); |
538 | 43f493af | bellard | } |
539 | 43f493af | bellard | |
540 | e2826cf4 | Paolo Bonzini | static void rtc_set_cmos(RTCState *s, const struct tm *tm) |
541 | 43f493af | bellard | { |
542 | 42fc73a1 | aurel32 | int year;
|
543 | dff38e7b | bellard | |
544 | abd0c6bd | Paul Brook | s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec); |
545 | abd0c6bd | Paul Brook | s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min); |
546 | c29cd656 | Aurelien Jarno | if (s->cmos_data[RTC_REG_B] & REG_B_24H) {
|
547 | 43f493af | bellard | /* 24 hour format */
|
548 | abd0c6bd | Paul Brook | s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour); |
549 | 43f493af | bellard | } else {
|
550 | 43f493af | bellard | /* 12 hour format */
|
551 | 3b89eb43 | Paolo Bonzini | int h = (tm->tm_hour % 12) ? tm->tm_hour % 12 : 12; |
552 | 3b89eb43 | Paolo Bonzini | s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, h); |
553 | 43f493af | bellard | if (tm->tm_hour >= 12) |
554 | 43f493af | bellard | s->cmos_data[RTC_HOURS] |= 0x80;
|
555 | 43f493af | bellard | } |
556 | abd0c6bd | Paul Brook | s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1);
|
557 | abd0c6bd | Paul Brook | s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday); |
558 | abd0c6bd | Paul Brook | s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1);
|
559 | b8994faf | Paolo Bonzini | year = tm->tm_year + 1900 - s->base_year;
|
560 | b8994faf | Paolo Bonzini | s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year % 100);
|
561 | b8994faf | Paolo Bonzini | s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100);
|
562 | 43f493af | bellard | } |
563 | 43f493af | bellard | |
564 | 56038ef6 | Yang Zhang | static void rtc_update_time(RTCState *s) |
565 | 43f493af | bellard | { |
566 | 56038ef6 | Yang Zhang | struct tm ret;
|
567 | 56038ef6 | Yang Zhang | time_t guest_sec; |
568 | 56038ef6 | Yang Zhang | int64_t guest_nsec; |
569 | 56038ef6 | Yang Zhang | |
570 | 56038ef6 | Yang Zhang | guest_nsec = get_guest_rtc_ns(s); |
571 | 56038ef6 | Yang Zhang | guest_sec = guest_nsec / NSEC_PER_SEC; |
572 | 56038ef6 | Yang Zhang | gmtime_r(&guest_sec, &ret); |
573 | 02c6ccc6 | Alex Horn | |
574 | 02c6ccc6 | Alex Horn | /* Is SET flag of Register B disabled? */
|
575 | 02c6ccc6 | Alex Horn | if ((s->cmos_data[RTC_REG_B] & REG_B_SET) == 0) { |
576 | 02c6ccc6 | Alex Horn | rtc_set_cmos(s, &ret); |
577 | 02c6ccc6 | Alex Horn | } |
578 | 43f493af | bellard | } |
579 | 43f493af | bellard | |
580 | 56038ef6 | Yang Zhang | static int update_in_progress(RTCState *s) |
581 | 43f493af | bellard | { |
582 | 56038ef6 | Yang Zhang | int64_t guest_nsec; |
583 | 3b46e624 | ths | |
584 | 41a9b8b2 | Yang Zhang | if (!rtc_running(s)) {
|
585 | 56038ef6 | Yang Zhang | return 0; |
586 | dff38e7b | bellard | } |
587 | 56038ef6 | Yang Zhang | if (qemu_timer_pending(s->update_timer)) {
|
588 | 56038ef6 | Yang Zhang | int64_t next_update_time = qemu_timer_expire_time_ns(s->update_timer); |
589 | 56038ef6 | Yang Zhang | /* Latch UIP until the timer expires. */
|
590 | 56038ef6 | Yang Zhang | if (qemu_get_clock_ns(rtc_clock) >= (next_update_time - UIP_HOLD_LENGTH)) {
|
591 | 56038ef6 | Yang Zhang | s->cmos_data[RTC_REG_A] |= REG_A_UIP; |
592 | 56038ef6 | Yang Zhang | return 1; |
593 | dff38e7b | bellard | } |
594 | dff38e7b | bellard | } |
595 | dff38e7b | bellard | |
596 | 56038ef6 | Yang Zhang | guest_nsec = get_guest_rtc_ns(s); |
597 | 56038ef6 | Yang Zhang | /* UIP bit will be set at last 244us of every second. */
|
598 | 56038ef6 | Yang Zhang | if ((guest_nsec % NSEC_PER_SEC) >= (NSEC_PER_SEC - UIP_HOLD_LENGTH)) {
|
599 | 56038ef6 | Yang Zhang | return 1; |
600 | dff38e7b | bellard | } |
601 | 56038ef6 | Yang Zhang | return 0; |
602 | 80cabfad | bellard | } |
603 | 80cabfad | bellard | |
604 | 0da8c842 | Alexander Graf | static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, |
605 | 0da8c842 | Alexander Graf | unsigned size)
|
606 | 80cabfad | bellard | { |
607 | b41a2cd1 | bellard | RTCState *s = opaque; |
608 | 80cabfad | bellard | int ret;
|
609 | 80cabfad | bellard | if ((addr & 1) == 0) { |
610 | 80cabfad | bellard | return 0xff; |
611 | 80cabfad | bellard | } else {
|
612 | 80cabfad | bellard | switch(s->cmos_index) {
|
613 | e67edb94 | Paolo Bonzini | case RTC_IBM_PS2_CENTURY_BYTE:
|
614 | e67edb94 | Paolo Bonzini | s->cmos_index = RTC_CENTURY; |
615 | e67edb94 | Paolo Bonzini | /* fall through */
|
616 | e67edb94 | Paolo Bonzini | case RTC_CENTURY:
|
617 | 80cabfad | bellard | case RTC_SECONDS:
|
618 | 80cabfad | bellard | case RTC_MINUTES:
|
619 | 80cabfad | bellard | case RTC_HOURS:
|
620 | 80cabfad | bellard | case RTC_DAY_OF_WEEK:
|
621 | 80cabfad | bellard | case RTC_DAY_OF_MONTH:
|
622 | 80cabfad | bellard | case RTC_MONTH:
|
623 | 80cabfad | bellard | case RTC_YEAR:
|
624 | 56038ef6 | Yang Zhang | /* if not in set mode, calibrate cmos before
|
625 | 56038ef6 | Yang Zhang | * reading*/
|
626 | 41a9b8b2 | Yang Zhang | if (rtc_running(s)) {
|
627 | 56038ef6 | Yang Zhang | rtc_update_time(s); |
628 | 56038ef6 | Yang Zhang | } |
629 | 80cabfad | bellard | ret = s->cmos_data[s->cmos_index]; |
630 | 80cabfad | bellard | break;
|
631 | 80cabfad | bellard | case RTC_REG_A:
|
632 | 56038ef6 | Yang Zhang | if (update_in_progress(s)) {
|
633 | 56038ef6 | Yang Zhang | s->cmos_data[s->cmos_index] |= REG_A_UIP; |
634 | 56038ef6 | Yang Zhang | } else {
|
635 | 56038ef6 | Yang Zhang | s->cmos_data[s->cmos_index] &= ~REG_A_UIP; |
636 | 56038ef6 | Yang Zhang | } |
637 | 80cabfad | bellard | ret = s->cmos_data[s->cmos_index]; |
638 | 80cabfad | bellard | break;
|
639 | 80cabfad | bellard | case RTC_REG_C:
|
640 | 80cabfad | bellard | ret = s->cmos_data[s->cmos_index]; |
641 | d537cf6c | pbrook | qemu_irq_lower(s->irq); |
642 | fbc15e27 | Paolo Bonzini | s->cmos_data[RTC_REG_C] = 0x00;
|
643 | 56038ef6 | Yang Zhang | if (ret & (REG_C_UF | REG_C_AF)) {
|
644 | 56038ef6 | Yang Zhang | check_update_timer(s); |
645 | 56038ef6 | Yang Zhang | } |
646 | ba32edab | Gleb Natapov | #ifdef TARGET_I386
|
647 | ba32edab | Gleb Natapov | if(s->irq_coalesced &&
|
648 | fbc15e27 | Paolo Bonzini | (s->cmos_data[RTC_REG_B] & REG_B_PIE) && |
649 | ba32edab | Gleb Natapov | s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) { |
650 | ba32edab | Gleb Natapov | s->irq_reinject_on_ack_count++; |
651 | fbc15e27 | Paolo Bonzini | s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_PF; |
652 | ba32edab | Gleb Natapov | apic_reset_irq_delivered(); |
653 | aa6f63ff | Blue Swirl | DPRINTF_C("cmos: injecting on ack\n");
|
654 | ba32edab | Gleb Natapov | qemu_irq_raise(s->irq); |
655 | aa6f63ff | Blue Swirl | if (apic_get_irq_delivered()) {
|
656 | ba32edab | Gleb Natapov | s->irq_coalesced--; |
657 | aa6f63ff | Blue Swirl | DPRINTF_C("cmos: coalesced irqs decreased to %d\n",
|
658 | aa6f63ff | Blue Swirl | s->irq_coalesced); |
659 | aa6f63ff | Blue Swirl | } |
660 | ba32edab | Gleb Natapov | } |
661 | ba32edab | Gleb Natapov | #endif
|
662 | 80cabfad | bellard | break;
|
663 | 80cabfad | bellard | default:
|
664 | 80cabfad | bellard | ret = s->cmos_data[s->cmos_index]; |
665 | 80cabfad | bellard | break;
|
666 | 80cabfad | bellard | } |
667 | ec51e364 | Isaku Yamahata | CMOS_DPRINTF("cmos: read index=0x%02x val=0x%02x\n",
|
668 | ec51e364 | Isaku Yamahata | s->cmos_index, ret); |
669 | 80cabfad | bellard | return ret;
|
670 | 80cabfad | bellard | } |
671 | 80cabfad | bellard | } |
672 | 80cabfad | bellard | |
673 | 1d914fa0 | Isaku Yamahata | void rtc_set_memory(ISADevice *dev, int addr, int val) |
674 | dff38e7b | bellard | { |
675 | 1d914fa0 | Isaku Yamahata | RTCState *s = DO_UPCAST(RTCState, dev, dev); |
676 | dff38e7b | bellard | if (addr >= 0 && addr <= 127) |
677 | dff38e7b | bellard | s->cmos_data[addr] = val; |
678 | dff38e7b | bellard | } |
679 | dff38e7b | bellard | |
680 | 1d914fa0 | Isaku Yamahata | static void rtc_set_date_from_host(ISADevice *dev) |
681 | ea55ffb3 | ths | { |
682 | 1d914fa0 | Isaku Yamahata | RTCState *s = DO_UPCAST(RTCState, dev, dev); |
683 | f6503059 | balrog | struct tm tm;
|
684 | ea55ffb3 | ths | |
685 | f6503059 | balrog | qemu_get_timedate(&tm, 0);
|
686 | 56038ef6 | Yang Zhang | |
687 | 56038ef6 | Yang Zhang | s->base_rtc = mktimegm(&tm); |
688 | 56038ef6 | Yang Zhang | s->last_update = qemu_get_clock_ns(rtc_clock); |
689 | 56038ef6 | Yang Zhang | s->offset = 0;
|
690 | 56038ef6 | Yang Zhang | |
691 | 56038ef6 | Yang Zhang | /* set the CMOS date */
|
692 | e2826cf4 | Paolo Bonzini | rtc_set_cmos(s, &tm); |
693 | ea55ffb3 | ths | } |
694 | ea55ffb3 | ths | |
695 | 6b075b8a | Juan Quintela | static int rtc_post_load(void *opaque, int version_id) |
696 | 80cabfad | bellard | { |
697 | dff38e7b | bellard | RTCState *s = opaque; |
698 | dff38e7b | bellard | |
699 | 56038ef6 | Yang Zhang | if (version_id <= 2) { |
700 | 56038ef6 | Yang Zhang | rtc_set_time(s); |
701 | 56038ef6 | Yang Zhang | s->offset = 0;
|
702 | 56038ef6 | Yang Zhang | check_update_timer(s); |
703 | 56038ef6 | Yang Zhang | } |
704 | 56038ef6 | Yang Zhang | |
705 | 56038ef6 | Yang Zhang | #ifdef TARGET_I386
|
706 | 048c74c4 | Juan Quintela | if (version_id >= 2) { |
707 | 433acf0d | Jan Kiszka | if (s->lost_tick_policy == LOST_TICK_SLEW) {
|
708 | 048c74c4 | Juan Quintela | rtc_coalesced_timer_update(s); |
709 | 048c74c4 | Juan Quintela | } |
710 | 048c74c4 | Juan Quintela | } |
711 | 6b075b8a | Juan Quintela | #endif
|
712 | 73822ec8 | aliguori | return 0; |
713 | 73822ec8 | aliguori | } |
714 | 73822ec8 | aliguori | |
715 | 6b075b8a | Juan Quintela | static const VMStateDescription vmstate_rtc = { |
716 | 6b075b8a | Juan Quintela | .name = "mc146818rtc",
|
717 | 56038ef6 | Yang Zhang | .version_id = 3,
|
718 | 6b075b8a | Juan Quintela | .minimum_version_id = 1,
|
719 | 6b075b8a | Juan Quintela | .minimum_version_id_old = 1,
|
720 | 6b075b8a | Juan Quintela | .post_load = rtc_post_load, |
721 | 6b075b8a | Juan Quintela | .fields = (VMStateField []) { |
722 | 6b075b8a | Juan Quintela | VMSTATE_BUFFER(cmos_data, RTCState), |
723 | 6b075b8a | Juan Quintela | VMSTATE_UINT8(cmos_index, RTCState), |
724 | 89166459 | Paolo Bonzini | VMSTATE_UNUSED(7*4), |
725 | 6b075b8a | Juan Quintela | VMSTATE_TIMER(periodic_timer, RTCState), |
726 | 6b075b8a | Juan Quintela | VMSTATE_INT64(next_periodic_time, RTCState), |
727 | 56038ef6 | Yang Zhang | VMSTATE_UNUSED(3*8), |
728 | 6b075b8a | Juan Quintela | VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
|
729 | 6b075b8a | Juan Quintela | VMSTATE_UINT32_V(period, RTCState, 2),
|
730 | 56038ef6 | Yang Zhang | VMSTATE_UINT64_V(base_rtc, RTCState, 3),
|
731 | 56038ef6 | Yang Zhang | VMSTATE_UINT64_V(last_update, RTCState, 3),
|
732 | 56038ef6 | Yang Zhang | VMSTATE_INT64_V(offset, RTCState, 3),
|
733 | 56038ef6 | Yang Zhang | VMSTATE_TIMER_V(update_timer, RTCState, 3),
|
734 | 00cf5774 | Paolo Bonzini | VMSTATE_UINT64_V(next_alarm_time, RTCState, 3),
|
735 | 6b075b8a | Juan Quintela | VMSTATE_END_OF_LIST() |
736 | 6b075b8a | Juan Quintela | } |
737 | 6b075b8a | Juan Quintela | }; |
738 | 6b075b8a | Juan Quintela | |
739 | 17604dac | Jan Kiszka | static void rtc_notify_clock_reset(Notifier *notifier, void *data) |
740 | 17604dac | Jan Kiszka | { |
741 | 17604dac | Jan Kiszka | RTCState *s = container_of(notifier, RTCState, clock_reset_notifier); |
742 | 17604dac | Jan Kiszka | int64_t now = *(int64_t *)data; |
743 | 17604dac | Jan Kiszka | |
744 | 17604dac | Jan Kiszka | rtc_set_date_from_host(&s->dev); |
745 | c4c18e24 | Yang Zhang | periodic_timer_update(s, now); |
746 | 56038ef6 | Yang Zhang | check_update_timer(s); |
747 | 17604dac | Jan Kiszka | #ifdef TARGET_I386
|
748 | 433acf0d | Jan Kiszka | if (s->lost_tick_policy == LOST_TICK_SLEW) {
|
749 | 17604dac | Jan Kiszka | rtc_coalesced_timer_update(s); |
750 | 17604dac | Jan Kiszka | } |
751 | 17604dac | Jan Kiszka | #endif
|
752 | 17604dac | Jan Kiszka | } |
753 | 17604dac | Jan Kiszka | |
754 | da98c8eb | Gerd Hoffmann | /* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
|
755 | da98c8eb | Gerd Hoffmann | BIOS will read it and start S3 resume at POST Entry */
|
756 | da98c8eb | Gerd Hoffmann | static void rtc_notify_suspend(Notifier *notifier, void *data) |
757 | da98c8eb | Gerd Hoffmann | { |
758 | da98c8eb | Gerd Hoffmann | RTCState *s = container_of(notifier, RTCState, suspend_notifier); |
759 | da98c8eb | Gerd Hoffmann | rtc_set_memory(&s->dev, 0xF, 0xFE); |
760 | da98c8eb | Gerd Hoffmann | } |
761 | da98c8eb | Gerd Hoffmann | |
762 | eeb7c03c | Gleb Natapov | static void rtc_reset(void *opaque) |
763 | eeb7c03c | Gleb Natapov | { |
764 | eeb7c03c | Gleb Natapov | RTCState *s = opaque; |
765 | eeb7c03c | Gleb Natapov | |
766 | 72716184 | Anthony Liguori | s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE); |
767 | 72716184 | Anthony Liguori | s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF); |
768 | 56038ef6 | Yang Zhang | check_update_timer(s); |
769 | eeb7c03c | Gleb Natapov | |
770 | 72716184 | Anthony Liguori | qemu_irq_lower(s->irq); |
771 | eeb7c03c | Gleb Natapov | |
772 | eeb7c03c | Gleb Natapov | #ifdef TARGET_I386
|
773 | 433acf0d | Jan Kiszka | if (s->lost_tick_policy == LOST_TICK_SLEW) {
|
774 | 433acf0d | Jan Kiszka | s->irq_coalesced = 0;
|
775 | 433acf0d | Jan Kiszka | } |
776 | eeb7c03c | Gleb Natapov | #endif
|
777 | eeb7c03c | Gleb Natapov | } |
778 | eeb7c03c | Gleb Natapov | |
779 | b2c5009b | Richard Henderson | static const MemoryRegionOps cmos_ops = { |
780 | 0da8c842 | Alexander Graf | .read = cmos_ioport_read, |
781 | 0da8c842 | Alexander Graf | .write = cmos_ioport_write, |
782 | 0da8c842 | Alexander Graf | .impl = { |
783 | 0da8c842 | Alexander Graf | .min_access_size = 1,
|
784 | 0da8c842 | Alexander Graf | .max_access_size = 1,
|
785 | 0da8c842 | Alexander Graf | }, |
786 | 0da8c842 | Alexander Graf | .endianness = DEVICE_LITTLE_ENDIAN, |
787 | b2c5009b | Richard Henderson | }; |
788 | b2c5009b | Richard Henderson | |
789 | 57c9fafe | Anthony Liguori | static void rtc_get_date(Object *obj, Visitor *v, void *opaque, |
790 | 18297050 | Anthony Liguori | const char *name, Error **errp) |
791 | 18297050 | Anthony Liguori | { |
792 | 57c9fafe | Anthony Liguori | ISADevice *isa = ISA_DEVICE(obj); |
793 | 18297050 | Anthony Liguori | RTCState *s = DO_UPCAST(RTCState, dev, isa); |
794 | e2826cf4 | Paolo Bonzini | struct tm current_tm;
|
795 | 18297050 | Anthony Liguori | |
796 | 56038ef6 | Yang Zhang | rtc_update_time(s); |
797 | e2826cf4 | Paolo Bonzini | rtc_get_time(s, ¤t_tm); |
798 | 18297050 | Anthony Liguori | visit_start_struct(v, NULL, "struct tm", name, 0, errp); |
799 | e2826cf4 | Paolo Bonzini | visit_type_int32(v, ¤t_tm.tm_year, "tm_year", errp);
|
800 | e2826cf4 | Paolo Bonzini | visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", errp);
|
801 | e2826cf4 | Paolo Bonzini | visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", errp);
|
802 | e2826cf4 | Paolo Bonzini | visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", errp);
|
803 | e2826cf4 | Paolo Bonzini | visit_type_int32(v, ¤t_tm.tm_min, "tm_min", errp);
|
804 | e2826cf4 | Paolo Bonzini | visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", errp);
|
805 | 18297050 | Anthony Liguori | visit_end_struct(v, errp); |
806 | 18297050 | Anthony Liguori | } |
807 | 18297050 | Anthony Liguori | |
808 | 32e0c826 | Gerd Hoffmann | static int rtc_initfn(ISADevice *dev) |
809 | dff38e7b | bellard | { |
810 | 32e0c826 | Gerd Hoffmann | RTCState *s = DO_UPCAST(RTCState, dev, dev); |
811 | 32e0c826 | Gerd Hoffmann | int base = 0x70; |
812 | 80cabfad | bellard | |
813 | 80cabfad | bellard | s->cmos_data[RTC_REG_A] = 0x26;
|
814 | 80cabfad | bellard | s->cmos_data[RTC_REG_B] = 0x02;
|
815 | 80cabfad | bellard | s->cmos_data[RTC_REG_C] = 0x00;
|
816 | 80cabfad | bellard | s->cmos_data[RTC_REG_D] = 0x80;
|
817 | 80cabfad | bellard | |
818 | b8994faf | Paolo Bonzini | /* This is for historical reasons. The default base year qdev property
|
819 | b8994faf | Paolo Bonzini | * was set to 2000 for most machine types before the century byte was
|
820 | b8994faf | Paolo Bonzini | * implemented.
|
821 | b8994faf | Paolo Bonzini | *
|
822 | b8994faf | Paolo Bonzini | * This if statement means that the century byte will be always 0
|
823 | b8994faf | Paolo Bonzini | * (at least until 2079...) for base_year = 1980, but will be set
|
824 | b8994faf | Paolo Bonzini | * correctly for base_year = 2000.
|
825 | b8994faf | Paolo Bonzini | */
|
826 | b8994faf | Paolo Bonzini | if (s->base_year == 2000) { |
827 | b8994faf | Paolo Bonzini | s->base_year = 0;
|
828 | b8994faf | Paolo Bonzini | } |
829 | b8994faf | Paolo Bonzini | |
830 | 1d914fa0 | Isaku Yamahata | rtc_set_date_from_host(dev); |
831 | ea55ffb3 | ths | |
832 | 93b66569 | aliguori | #ifdef TARGET_I386
|
833 | 433acf0d | Jan Kiszka | switch (s->lost_tick_policy) {
|
834 | 433acf0d | Jan Kiszka | case LOST_TICK_SLEW:
|
835 | 6875204c | Jan Kiszka | s->coalesced_timer = |
836 | 74475455 | Paolo Bonzini | qemu_new_timer_ns(rtc_clock, rtc_coalesced_timer, s); |
837 | 433acf0d | Jan Kiszka | break;
|
838 | 433acf0d | Jan Kiszka | case LOST_TICK_DISCARD:
|
839 | 433acf0d | Jan Kiszka | break;
|
840 | 433acf0d | Jan Kiszka | default:
|
841 | 433acf0d | Jan Kiszka | return -EINVAL;
|
842 | 433acf0d | Jan Kiszka | } |
843 | 93b66569 | aliguori | #endif
|
844 | 433acf0d | Jan Kiszka | |
845 | 433acf0d | Jan Kiszka | s->periodic_timer = qemu_new_timer_ns(rtc_clock, rtc_periodic_timer, s); |
846 | 56038ef6 | Yang Zhang | s->update_timer = qemu_new_timer_ns(rtc_clock, rtc_update_timer, s); |
847 | 56038ef6 | Yang Zhang | check_update_timer(s); |
848 | dff38e7b | bellard | |
849 | 17604dac | Jan Kiszka | s->clock_reset_notifier.notify = rtc_notify_clock_reset; |
850 | 17604dac | Jan Kiszka | qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier); |
851 | 17604dac | Jan Kiszka | |
852 | da98c8eb | Gerd Hoffmann | s->suspend_notifier.notify = rtc_notify_suspend; |
853 | da98c8eb | Gerd Hoffmann | qemu_register_suspend_notifier(&s->suspend_notifier); |
854 | da98c8eb | Gerd Hoffmann | |
855 | b2c5009b | Richard Henderson | memory_region_init_io(&s->io, &cmos_ops, s, "rtc", 2); |
856 | b2c5009b | Richard Henderson | isa_register_ioport(dev, &s->io, base); |
857 | dff38e7b | bellard | |
858 | 56038ef6 | Yang Zhang | qdev_set_legacy_instance_id(&dev->qdev, base, 3);
|
859 | a08d4367 | Jan Kiszka | qemu_register_reset(rtc_reset, s); |
860 | 18297050 | Anthony Liguori | |
861 | 57c9fafe | Anthony Liguori | object_property_add(OBJECT(s), "date", "struct tm", |
862 | 57c9fafe | Anthony Liguori | rtc_get_date, NULL, NULL, s, NULL); |
863 | 18297050 | Anthony Liguori | |
864 | 32e0c826 | Gerd Hoffmann | return 0; |
865 | 32e0c826 | Gerd Hoffmann | } |
866 | 32e0c826 | Gerd Hoffmann | |
867 | 48a18b3c | Hervé Poussineau | ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq)
|
868 | 32e0c826 | Gerd Hoffmann | { |
869 | 32e0c826 | Gerd Hoffmann | ISADevice *dev; |
870 | 7d932dfd | Jan Kiszka | RTCState *s; |
871 | eeb7c03c | Gleb Natapov | |
872 | 48a18b3c | Hervé Poussineau | dev = isa_create(bus, "mc146818rtc");
|
873 | 7d932dfd | Jan Kiszka | s = DO_UPCAST(RTCState, dev, dev); |
874 | 32e0c826 | Gerd Hoffmann | qdev_prop_set_int32(&dev->qdev, "base_year", base_year);
|
875 | e23a1b33 | Markus Armbruster | qdev_init_nofail(&dev->qdev); |
876 | 7d932dfd | Jan Kiszka | if (intercept_irq) {
|
877 | 7d932dfd | Jan Kiszka | s->irq = intercept_irq; |
878 | 7d932dfd | Jan Kiszka | } else {
|
879 | 7d932dfd | Jan Kiszka | isa_init_irq(dev, &s->irq, RTC_ISA_IRQ); |
880 | 7d932dfd | Jan Kiszka | } |
881 | 1d914fa0 | Isaku Yamahata | return dev;
|
882 | 80cabfad | bellard | } |
883 | 80cabfad | bellard | |
884 | 39bffca2 | Anthony Liguori | static Property mc146818rtc_properties[] = {
|
885 | 39bffca2 | Anthony Liguori | DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980), |
886 | 39bffca2 | Anthony Liguori | DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState,
|
887 | 39bffca2 | Anthony Liguori | lost_tick_policy, LOST_TICK_DISCARD), |
888 | 39bffca2 | Anthony Liguori | DEFINE_PROP_END_OF_LIST(), |
889 | 39bffca2 | Anthony Liguori | }; |
890 | 39bffca2 | Anthony Liguori | |
891 | 8f04ee08 | Anthony Liguori | static void rtc_class_initfn(ObjectClass *klass, void *data) |
892 | 8f04ee08 | Anthony Liguori | { |
893 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
894 | 8f04ee08 | Anthony Liguori | ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); |
895 | 8f04ee08 | Anthony Liguori | ic->init = rtc_initfn; |
896 | 39bffca2 | Anthony Liguori | dc->no_user = 1;
|
897 | 39bffca2 | Anthony Liguori | dc->vmsd = &vmstate_rtc; |
898 | 39bffca2 | Anthony Liguori | dc->props = mc146818rtc_properties; |
899 | 8f04ee08 | Anthony Liguori | } |
900 | 8f04ee08 | Anthony Liguori | |
901 | 39bffca2 | Anthony Liguori | static TypeInfo mc146818rtc_info = {
|
902 | 39bffca2 | Anthony Liguori | .name = "mc146818rtc",
|
903 | 39bffca2 | Anthony Liguori | .parent = TYPE_ISA_DEVICE, |
904 | 39bffca2 | Anthony Liguori | .instance_size = sizeof(RTCState),
|
905 | 39bffca2 | Anthony Liguori | .class_init = rtc_class_initfn, |
906 | 32e0c826 | Gerd Hoffmann | }; |
907 | 32e0c826 | Gerd Hoffmann | |
908 | 83f7d43a | Andreas Färber | static void mc146818rtc_register_types(void) |
909 | 100d9891 | aurel32 | { |
910 | 39bffca2 | Anthony Liguori | type_register_static(&mc146818rtc_info); |
911 | 100d9891 | aurel32 | } |
912 | 83f7d43a | Andreas Färber | |
913 | 83f7d43a | Andreas Färber | type_init(mc146818rtc_register_types) |