source: trunk/packages/xen-3.1/xen-3.1/tools/ioemu/hw/mc146818rtc.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 14.4 KB
Line 
1/*
2 * QEMU MC146818 RTC emulation
3 *
4 * Copyright (c) 2003-2004 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#include "vl.h"
25
26//#define DEBUG_CMOS
27
28#define RTC_SECONDS             0
29#define RTC_SECONDS_ALARM       1
30#define RTC_MINUTES             2
31#define RTC_MINUTES_ALARM       3
32#define RTC_HOURS               4
33#define RTC_HOURS_ALARM         5
34#define RTC_ALARM_DONT_CARE    0xC0
35
36#define RTC_DAY_OF_WEEK         6
37#define RTC_DAY_OF_MONTH        7
38#define RTC_MONTH               8
39#define RTC_YEAR                9
40
41#define RTC_REG_A               10
42#define RTC_REG_B               11
43#define RTC_REG_C               12
44#define RTC_REG_D               13
45
46#define REG_A_UIP 0x80
47
48#define REG_B_SET 0x80
49#define REG_B_PIE 0x40
50#define REG_B_AIE 0x20
51#define REG_B_UIE 0x10
52
53struct RTCState {
54    uint8_t cmos_data[128];
55    uint8_t cmos_index;
56    struct tm current_tm;
57    int irq;
58    /* periodic timer */
59    QEMUTimer *periodic_timer;
60    int64_t next_periodic_time;
61    /* second update */
62    int64_t next_second_time;
63    QEMUTimer *second_timer;
64    QEMUTimer *second_timer2;
65};
66
67static void rtc_set_time(RTCState *s);
68static void rtc_copy_date(RTCState *s);
69
70static void rtc_timer_update(RTCState *s, int64_t current_time)
71{
72    int period_code, period;
73    int64_t cur_clock, next_irq_clock;
74
75    period_code = s->cmos_data[RTC_REG_A] & 0x0f;
76    if (period_code != 0 && 
77        (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
78        if (period_code <= 2)
79            period_code += 7;
80        /* period in 32 Khz cycles */
81        period = 1 << (period_code - 1);
82        /* compute 32 khz clock */
83        cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
84        next_irq_clock = (cur_clock & ~(period - 1)) + period;
85        s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
86        qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
87    } else {
88        qemu_del_timer(s->periodic_timer);
89    }
90}
91
92static void rtc_periodic_timer(void *opaque)
93{
94    RTCState *s = opaque;
95
96    rtc_timer_update(s, s->next_periodic_time);
97    s->cmos_data[RTC_REG_C] |= 0xc0;
98    pic_set_irq(s->irq, 1);
99}
100
101static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
102{
103    RTCState *s = opaque;
104
105    if ((addr & 1) == 0) {
106        s->cmos_index = data & 0x7f;
107    } else {
108#ifdef DEBUG_CMOS
109        printf("cmos: write index=0x%02x val=0x%02x\n",
110               s->cmos_index, data);
111#endif       
112        switch(s->cmos_index) {
113        case RTC_SECONDS_ALARM:
114        case RTC_MINUTES_ALARM:
115        case RTC_HOURS_ALARM:
116            /* XXX: not supported */
117            s->cmos_data[s->cmos_index] = data;
118            break;
119        case RTC_SECONDS:
120        case RTC_MINUTES:
121        case RTC_HOURS:
122        case RTC_DAY_OF_WEEK:
123        case RTC_DAY_OF_MONTH:
124        case RTC_MONTH:
125        case RTC_YEAR:
126            s->cmos_data[s->cmos_index] = data;
127            /* if in set mode, do not update the time */
128            if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
129                rtc_set_time(s);
130            }
131            break;
132        case RTC_REG_A:
133            /* UIP bit is read only */
134            s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
135                (s->cmos_data[RTC_REG_A] & REG_A_UIP);
136            rtc_timer_update(s, qemu_get_clock(vm_clock));
137            break;
138        case RTC_REG_B:
139            if (data & REG_B_SET) {
140                /* set mode: reset UIP mode */
141                s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
142                data &= ~REG_B_UIE;
143            } else {
144                /* if disabling set mode, update the time */
145                if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
146                    rtc_set_time(s);
147                }
148            }
149            s->cmos_data[RTC_REG_B] = data;
150            rtc_timer_update(s, qemu_get_clock(vm_clock));
151            break;
152        case RTC_REG_C:
153        case RTC_REG_D:
154            /* cannot write to them */
155            break;
156        default:
157            s->cmos_data[s->cmos_index] = data;
158            break;
159        }
160    }
161}
162
163static inline int to_bcd(RTCState *s, int a)
164{
165    if (s->cmos_data[RTC_REG_B] & 0x04) {
166        return a;
167    } else {
168        return ((a / 10) << 4) | (a % 10);
169    }
170}
171
172static inline int from_bcd(RTCState *s, int a)
173{
174    if (s->cmos_data[RTC_REG_B] & 0x04) {
175        return a;
176    } else {
177        return ((a >> 4) * 10) + (a & 0x0f);
178    }
179}
180
181static void send_timeoffset_msg(time_t delta)
182{
183
184/* This routine is used to inform another entity that the
185   base time offset has changed. For instance, if you
186   were using xenstore, you might want to write to the store
187   at this point.  Or, you might use some other method.
188   Whatever you might choose, here's a hook point to implement it.
189
190   One item of note is that this delta is in addition to
191   any existing offset you might be already using. */
192
193    return;
194}
195
196static void rtc_set_time(RTCState *s)
197{
198    struct tm *tm = &s->current_tm;
199    time_t before, after;
200   
201    before = mktime(tm);
202    tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
203    tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
204    tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
205    if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
206        (s->cmos_data[RTC_HOURS] & 0x80)) {
207        tm->tm_hour += 12;
208    }
209    tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
210    tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
211    tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
212    tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
213
214    /* Compute, and send, the additional time delta
215       We could compute the total time delta, but this is
216       sufficient, and simple. */
217    after = mktime(tm);
218    send_timeoffset_msg(after-before);
219}
220
221static void rtc_copy_date(RTCState *s)
222{
223    const struct tm *tm = &s->current_tm;
224
225    s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
226    s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
227    if (s->cmos_data[RTC_REG_B] & 0x02) {
228        /* 24 hour format */
229        s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
230    } else {
231        /* 12 hour format */
232        s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
233        if (tm->tm_hour >= 12)
234            s->cmos_data[RTC_HOURS] |= 0x80;
235    }
236    s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
237    s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
238    s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
239    s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
240}
241
242/* month is between 0 and 11. */
243static int get_days_in_month(int month, int year)
244{
245    static const int days_tab[12] = { 
246        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 
247    };
248    int d;
249    if ((unsigned )month >= 12)
250        return 31;
251    d = days_tab[month];
252    if (month == 1) {
253        if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
254            d++;
255    }
256    return d;
257}
258
259/* update 'tm' to the next second */
260static void rtc_next_second(struct tm *tm)
261{
262    int days_in_month;
263
264    tm->tm_sec++;
265    if ((unsigned)tm->tm_sec >= 60) {
266        tm->tm_sec = 0;
267        tm->tm_min++;
268        if ((unsigned)tm->tm_min >= 60) {
269            tm->tm_min = 0;
270            tm->tm_hour++;
271            if ((unsigned)tm->tm_hour >= 24) {
272                tm->tm_hour = 0;
273                /* next day */
274                tm->tm_wday++;
275                if ((unsigned)tm->tm_wday >= 7)
276                    tm->tm_wday = 0;
277                days_in_month = get_days_in_month(tm->tm_mon, 
278                                                  tm->tm_year + 1900);
279                tm->tm_mday++;
280                if (tm->tm_mday < 1) {
281                    tm->tm_mday = 1;
282                } else if (tm->tm_mday > days_in_month) {
283                    tm->tm_mday = 1;
284                    tm->tm_mon++;
285                    if (tm->tm_mon >= 12) {
286                        tm->tm_mon = 0;
287                        tm->tm_year++;
288                    }
289                }
290            }
291        }
292    }
293}
294
295
296static void rtc_update_second(void *opaque)
297{
298    RTCState *s = opaque;
299    int64_t delay;
300
301    /* if the oscillator is not in normal operation, we do not update */
302    if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
303        s->next_second_time += ticks_per_sec;
304        qemu_mod_timer(s->second_timer, s->next_second_time);
305    } else {
306        rtc_next_second(&s->current_tm);
307       
308        if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
309            /* update in progress bit */
310            s->cmos_data[RTC_REG_A] |= REG_A_UIP;
311        }
312        /* should be 244 us = 8 / 32768 seconds, but currently the
313           timers do not have the necessary resolution. */
314        delay = (ticks_per_sec * 1) / 100;
315        if (delay < 1)
316            delay = 1;
317        qemu_mod_timer(s->second_timer2, 
318                       s->next_second_time + delay);
319    }
320}
321
322static void rtc_update_second2(void *opaque)
323{
324    RTCState *s = opaque;
325
326    if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
327        rtc_copy_date(s);
328    }
329
330    /* check alarm */
331    if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
332        if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
333             s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) &&
334            ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
335             s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) &&
336            ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
337             s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
338
339            s->cmos_data[RTC_REG_C] |= 0xa0; 
340            pic_set_irq(s->irq, 1);
341        }
342    }
343
344    /* update ended interrupt */
345    if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
346        s->cmos_data[RTC_REG_C] |= 0x90; 
347        pic_set_irq(s->irq, 1);
348    }
349
350    /* clear update in progress bit */
351    s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
352
353    s->next_second_time += ticks_per_sec;
354    qemu_mod_timer(s->second_timer, s->next_second_time);
355}
356
357static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
358{
359    RTCState *s = opaque;
360    int ret;
361    if ((addr & 1) == 0) {
362        return 0xff;
363    } else {
364        switch(s->cmos_index) {
365        case RTC_SECONDS:
366        case RTC_MINUTES:
367        case RTC_HOURS:
368        case RTC_DAY_OF_WEEK:
369        case RTC_DAY_OF_MONTH:
370        case RTC_MONTH:
371        case RTC_YEAR:
372            ret = s->cmos_data[s->cmos_index];
373            break;
374        case RTC_REG_A:
375            ret = s->cmos_data[s->cmos_index];
376            break;
377        case RTC_REG_C:
378            ret = s->cmos_data[s->cmos_index];
379            pic_set_irq(s->irq, 0);
380            s->cmos_data[RTC_REG_C] = 0x00; 
381            break;
382        default:
383            ret = s->cmos_data[s->cmos_index];
384            break;
385        }
386#ifdef DEBUG_CMOS
387        printf("cmos: read index=0x%02x val=0x%02x\n",
388               s->cmos_index, ret);
389#endif
390        return ret;
391    }
392}
393
394void rtc_set_memory(RTCState *s, int addr, int val)
395{
396    if (addr >= 0 && addr <= 127)
397        s->cmos_data[addr] = val;
398}
399
400void rtc_set_date(RTCState *s, const struct tm *tm)
401{
402    s->current_tm = *tm;
403    rtc_copy_date(s);
404}
405
406static void rtc_save(QEMUFile *f, void *opaque)
407{
408    RTCState *s = opaque;
409
410    qemu_put_buffer(f, s->cmos_data, 128);
411    qemu_put_8s(f, &s->cmos_index);
412   
413    qemu_put_be32s(f, &s->current_tm.tm_sec);
414    qemu_put_be32s(f, &s->current_tm.tm_min);
415    qemu_put_be32s(f, &s->current_tm.tm_hour);
416    qemu_put_be32s(f, &s->current_tm.tm_wday);
417    qemu_put_be32s(f, &s->current_tm.tm_mday);
418    qemu_put_be32s(f, &s->current_tm.tm_mon);
419    qemu_put_be32s(f, &s->current_tm.tm_year);
420
421    qemu_put_timer(f, s->periodic_timer);
422    qemu_put_be64s(f, &s->next_periodic_time);
423
424    qemu_put_be64s(f, &s->next_second_time);
425    qemu_put_timer(f, s->second_timer);
426    qemu_put_timer(f, s->second_timer2);
427}
428
429static int rtc_load(QEMUFile *f, void *opaque, int version_id)
430{
431    RTCState *s = opaque;
432
433    if (version_id != 1)
434        return -EINVAL;
435
436    qemu_get_buffer(f, s->cmos_data, 128);
437    qemu_get_8s(f, &s->cmos_index);
438
439    qemu_get_be32s(f, &s->current_tm.tm_sec);
440    qemu_get_be32s(f, &s->current_tm.tm_min);
441    qemu_get_be32s(f, &s->current_tm.tm_hour);
442    qemu_get_be32s(f, &s->current_tm.tm_wday);
443    qemu_get_be32s(f, &s->current_tm.tm_mday);
444    qemu_get_be32s(f, &s->current_tm.tm_mon);
445    qemu_get_be32s(f, &s->current_tm.tm_year);
446
447    qemu_get_timer(f, s->periodic_timer);
448    qemu_get_be64s(f, &s->next_periodic_time);
449
450    qemu_get_be64s(f, &s->next_second_time);
451    qemu_get_timer(f, s->second_timer);
452    qemu_get_timer(f, s->second_timer2);
453    return 0;
454}
455
456RTCState *rtc_init(int base, int irq)
457{
458    RTCState *s;
459
460    s = qemu_mallocz(sizeof(RTCState));
461    if (!s)
462        return NULL;
463
464    s->irq = irq;
465    s->cmos_data[RTC_REG_A] = 0x26;
466    s->cmos_data[RTC_REG_B] = 0x02;
467    s->cmos_data[RTC_REG_C] = 0x00;
468    s->cmos_data[RTC_REG_D] = 0x80;
469
470    s->periodic_timer = qemu_new_timer(vm_clock, 
471                                       rtc_periodic_timer, s);
472    s->second_timer = qemu_new_timer(vm_clock, 
473                                     rtc_update_second, s);
474    s->second_timer2 = qemu_new_timer(vm_clock, 
475                                      rtc_update_second2, s);
476
477    s->next_second_time = qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100;
478    qemu_mod_timer(s->second_timer2, s->next_second_time);
479
480    register_ioport_write(base, 2, 1, cmos_ioport_write, s);
481    register_ioport_read(base, 2, 1, cmos_ioport_read, s);
482
483    register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
484    return s;
485}
486
Note: See TracBrowser for help on using the repository browser.