Commit dff38e7b40b398dd713643d57d89f280c6d09ff1

Authored by bellard
1 parent 1f1af9fd

more precise RTC emulation (periodic timers + time updates)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@688 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 264 additions and 34 deletions
hw/mc146818rtc.c
... ... @@ -63,11 +63,62 @@
63 63 #define RTC_REG_C 12
64 64 #define RTC_REG_D 13
65 65  
66   -/* PC cmos mappings */
67   -#define REG_IBM_CENTURY_BYTE 0x32
68   -#define REG_IBM_PS2_CENTURY_BYTE 0x37
  66 +#define REG_A_UIP 0x80
69 67  
70   -RTCState rtc_state;
  68 +#define REG_B_SET 0x80
  69 +#define REG_B_PIE 0x40
  70 +#define REG_B_AIE 0x20
  71 +#define REG_B_UIE 0x10
  72 +
  73 +struct RTCState {
  74 + uint8_t cmos_data[128];
  75 + uint8_t cmos_index;
  76 + int current_time; /* in seconds */
  77 + int irq;
  78 + uint8_t buf_data[10]; /* buffered data */
  79 + /* periodic timer */
  80 + QEMUTimer *periodic_timer;
  81 + int64_t next_periodic_time;
  82 + /* second update */
  83 + int64_t next_second_time;
  84 + QEMUTimer *second_timer;
  85 + QEMUTimer *second_timer2;
  86 +};
  87 +
  88 +static void rtc_set_time(RTCState *s);
  89 +static void rtc_set_date_buf(RTCState *s, const struct tm *tm);
  90 +static void rtc_copy_date(RTCState *s);
  91 +
  92 +static void rtc_timer_update(RTCState *s, int64_t current_time)
  93 +{
  94 + int period_code, period;
  95 + int64_t cur_clock, next_irq_clock;
  96 +
  97 + period_code = s->cmos_data[RTC_REG_A] & 0x0f;
  98 + if (period_code != 0 &&
  99 + (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
  100 + if (period_code <= 2)
  101 + period_code += 7;
  102 + /* period in 32 Khz cycles */
  103 + period = 1 << (period_code - 1);
  104 + /* compute 32 khz clock */
  105 + cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
  106 + next_irq_clock = (cur_clock & ~(period - 1)) + period;
  107 + s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
  108 + qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
  109 + } else {
  110 + qemu_del_timer(s->periodic_timer);
  111 + }
  112 +}
  113 +
  114 +static void rtc_periodic_timer(void *opaque)
  115 +{
  116 + RTCState *s = opaque;
  117 +
  118 + rtc_timer_update(s, s->next_periodic_time);
  119 + s->cmos_data[RTC_REG_C] |= 0xc0;
  120 + pic_set_irq(s->irq, 1);
  121 +}
71 122  
72 123 static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
73 124 {
... ... @@ -80,7 +131,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
80 131 printf("cmos: write index=0x%02x val=0x%02x\n",
81 132 s->cmos_index, data);
82 133 #endif
83   - switch(addr) {
  134 + switch(s->cmos_index) {
84 135 case RTC_SECONDS_ALARM:
85 136 case RTC_MINUTES_ALARM:
86 137 case RTC_HOURS_ALARM:
... ... @@ -95,10 +146,30 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
95 146 case RTC_MONTH:
96 147 case RTC_YEAR:
97 148 s->cmos_data[s->cmos_index] = data;
  149 + /* if in set mode, do not update the time */
  150 + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
  151 + rtc_set_time(s);
  152 + }
98 153 break;
99 154 case RTC_REG_A:
  155 + /* UIP bit is read only */
  156 + s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
  157 + (s->cmos_data[RTC_REG_A] & REG_A_UIP);
  158 + rtc_timer_update(s, qemu_get_clock(vm_clock));
  159 + break;
100 160 case RTC_REG_B:
101   - s->cmos_data[s->cmos_index] = data;
  161 + if (data & REG_B_SET) {
  162 + /* set mode: reset UIP mode */
  163 + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
  164 + data &= ~REG_B_UIE;
  165 + } else {
  166 + /* if disabling set mode, update the time */
  167 + if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
  168 + rtc_set_time(s);
  169 + }
  170 + }
  171 + s->cmos_data[RTC_REG_B] = data;
  172 + rtc_timer_update(s, qemu_get_clock(vm_clock));
102 173 break;
103 174 case RTC_REG_C:
104 175 case RTC_REG_D:
... ... @@ -111,27 +182,104 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
111 182 }
112 183 }
113 184  
114   -static inline int to_bcd(int a)
  185 +static inline int to_bcd(RTCState *s, int a)
115 186 {
116   - return ((a / 10) << 4) | (a % 10);
  187 + if (s->cmos_data[RTC_REG_B] & 0x04) {
  188 + return a;
  189 + } else {
  190 + return ((a / 10) << 4) | (a % 10);
  191 + }
117 192 }
118 193  
119   -static void cmos_update_time(RTCState *s)
  194 +static inline int from_bcd(RTCState *s, int a)
120 195 {
121   - struct tm *tm;
  196 + if (s->cmos_data[RTC_REG_B] & 0x04) {
  197 + return a;
  198 + } else {
  199 + return ((a >> 4) * 10) + (a & 0x0f);
  200 + }
  201 +}
  202 +
  203 +static void rtc_set_time(RTCState *s)
  204 +{
  205 + struct tm tm1, *tm = &tm1;
  206 +
  207 + tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
  208 + tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
  209 + tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS]);
  210 + tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
  211 + tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
  212 + tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
  213 + tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
  214 +
  215 + /* update internal state */
  216 + s->buf_data[RTC_SECONDS] = s->cmos_data[RTC_SECONDS];
  217 + s->buf_data[RTC_MINUTES] = s->cmos_data[RTC_MINUTES];
  218 + s->buf_data[RTC_HOURS] = s->cmos_data[RTC_HOURS];
  219 + s->buf_data[RTC_DAY_OF_WEEK] = s->cmos_data[RTC_DAY_OF_WEEK];
  220 + s->buf_data[RTC_DAY_OF_MONTH] = s->cmos_data[RTC_DAY_OF_MONTH];
  221 + s->buf_data[RTC_MONTH] = s->cmos_data[RTC_MONTH];
  222 + s->buf_data[RTC_YEAR] = s->cmos_data[RTC_YEAR];
  223 + s->current_time = mktime(tm);
  224 +}
  225 +
  226 +static void rtc_update_second(void *opaque)
  227 +{
  228 + RTCState *s = opaque;
  229 +
  230 + /* if the oscillator is not in normal operation, we do not update */
  231 + if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
  232 + s->next_second_time += ticks_per_sec;
  233 + qemu_mod_timer(s->second_timer, s->next_second_time);
  234 + } else {
  235 + s->current_time++;
  236 +
  237 + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
  238 + /* update in progress bit */
  239 + s->cmos_data[RTC_REG_A] |= REG_A_UIP;
  240 + }
  241 + qemu_mod_timer(s->second_timer2,
  242 + s->next_second_time + (ticks_per_sec * 99) / 100);
  243 + }
  244 +}
  245 +
  246 +static void rtc_update_second2(void *opaque)
  247 +{
  248 + RTCState *s = opaque;
122 249 time_t ti;
123 250  
124   - ti = time(NULL);
125   - tm = gmtime(&ti);
126   - s->cmos_data[RTC_SECONDS] = to_bcd(tm->tm_sec);
127   - s->cmos_data[RTC_MINUTES] = to_bcd(tm->tm_min);
128   - s->cmos_data[RTC_HOURS] = to_bcd(tm->tm_hour);
129   - s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(tm->tm_wday);
130   - s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(tm->tm_mday);
131   - s->cmos_data[RTC_MONTH] = to_bcd(tm->tm_mon + 1);
132   - s->cmos_data[RTC_YEAR] = to_bcd(tm->tm_year % 100);
133   - s->cmos_data[REG_IBM_CENTURY_BYTE] = to_bcd((tm->tm_year / 100) + 19);
134   - s->cmos_data[REG_IBM_PS2_CENTURY_BYTE] = s->cmos_data[REG_IBM_CENTURY_BYTE];
  251 + ti = s->current_time;
  252 + rtc_set_date_buf(s, gmtime(&ti));
  253 +
  254 + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
  255 + rtc_copy_date(s);
  256 + }
  257 +
  258 + /* check alarm */
  259 + if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
  260 + if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
  261 + s->cmos_data[RTC_SECONDS_ALARM] == s->buf_data[RTC_SECONDS]) &&
  262 + ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
  263 + s->cmos_data[RTC_MINUTES_ALARM] == s->buf_data[RTC_MINUTES]) &&
  264 + ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
  265 + s->cmos_data[RTC_HOURS_ALARM] == s->buf_data[RTC_HOURS])) {
  266 +
  267 + s->cmos_data[RTC_REG_C] |= 0xa0;
  268 + pic_set_irq(s->irq, 1);
  269 + }
  270 + }
  271 +
  272 + /* update ended interrupt */
  273 + if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
  274 + s->cmos_data[RTC_REG_C] |= 0x90;
  275 + pic_set_irq(s->irq, 1);
  276 + }
  277 +
  278 + /* clear update in progress bit */
  279 + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
  280 +
  281 + s->next_second_time += ticks_per_sec;
  282 + qemu_mod_timer(s->second_timer, s->next_second_time);
135 283 }
136 284  
137 285 static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
... ... @@ -149,16 +297,10 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
149 297 case RTC_DAY_OF_MONTH:
150 298 case RTC_MONTH:
151 299 case RTC_YEAR:
152   - case REG_IBM_CENTURY_BYTE:
153   - case REG_IBM_PS2_CENTURY_BYTE:
154   - cmos_update_time(s);
155 300 ret = s->cmos_data[s->cmos_index];
156 301 break;
157 302 case RTC_REG_A:
158 303 ret = s->cmos_data[s->cmos_index];
159   - /* toggle update-in-progress bit for Linux (same hack as
160   - plex86) */
161   - s->cmos_data[RTC_REG_A] ^= 0x80;
162 304 break;
163 305 case RTC_REG_C:
164 306 ret = s->cmos_data[s->cmos_index];
... ... @@ -177,19 +319,94 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
177 319 }
178 320 }
179 321  
180   -void rtc_timer(void)
  322 +static void rtc_set_date_buf(RTCState *s, const struct tm *tm)
181 323 {
182   - RTCState *s = &rtc_state;
183   - if (s->cmos_data[RTC_REG_B] & 0x50) {
184   - pic_set_irq(s->irq, 1);
  324 + s->buf_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
  325 + s->buf_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
  326 + if (s->cmos_data[RTC_REG_B] & 0x02) {
  327 + /* 24 hour format */
  328 + s->buf_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
  329 + } else {
  330 + /* 12 hour format */
  331 + s->buf_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
  332 + if (tm->tm_hour >= 12)
  333 + s->buf_data[RTC_HOURS] |= 0x80;
185 334 }
  335 + s->buf_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
  336 + s->buf_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
  337 + s->buf_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
  338 + s->buf_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
  339 +}
  340 +
  341 +static void rtc_copy_date(RTCState *s)
  342 +{
  343 + s->cmos_data[RTC_SECONDS] = s->buf_data[RTC_SECONDS];
  344 + s->cmos_data[RTC_MINUTES] = s->buf_data[RTC_MINUTES];
  345 + s->cmos_data[RTC_HOURS] = s->buf_data[RTC_HOURS];
  346 + s->cmos_data[RTC_DAY_OF_WEEK] = s->buf_data[RTC_DAY_OF_WEEK];
  347 + s->cmos_data[RTC_DAY_OF_MONTH] = s->buf_data[RTC_DAY_OF_MONTH];
  348 + s->cmos_data[RTC_MONTH] = s->buf_data[RTC_MONTH];
  349 + s->cmos_data[RTC_YEAR] = s->buf_data[RTC_YEAR];
  350 +}
  351 +
  352 +void rtc_set_memory(RTCState *s, int addr, int val)
  353 +{
  354 + if (addr >= 0 && addr <= 127)
  355 + s->cmos_data[addr] = val;
  356 +}
  357 +
  358 +void rtc_set_date(RTCState *s, const struct tm *tm)
  359 +{
  360 + s->current_time = mktime((struct tm *)tm);
  361 + rtc_set_date_buf(s, tm);
  362 + rtc_copy_date(s);
  363 +}
  364 +
  365 +static void rtc_save(QEMUFile *f, void *opaque)
  366 +{
  367 + RTCState *s = opaque;
  368 +
  369 + qemu_put_buffer(f, s->cmos_data, 128);
  370 + qemu_put_8s(f, &s->cmos_index);
  371 + qemu_put_be32s(f, &s->current_time);
  372 + qemu_put_buffer(f, s->buf_data, 10);
  373 +
  374 + qemu_put_timer(f, s->periodic_timer);
  375 + qemu_put_be64s(f, &s->next_periodic_time);
  376 +
  377 + qemu_put_be64s(f, &s->next_second_time);
  378 + qemu_put_timer(f, s->second_timer);
  379 + qemu_put_timer(f, s->second_timer2);
186 380 }
187 381  
188   -void rtc_init(int base, int irq)
  382 +static int rtc_load(QEMUFile *f, void *opaque, int version_id)
189 383 {
190   - RTCState *s = &rtc_state;
  384 + RTCState *s = opaque;
  385 +
  386 + if (version_id != 1)
  387 + return -EINVAL;
191 388  
192   - cmos_update_time(s);
  389 + qemu_get_buffer(f, s->cmos_data, 128);
  390 + qemu_get_8s(f, &s->cmos_index);
  391 + qemu_get_be32s(f, &s->current_time);
  392 + qemu_get_buffer(f, s->buf_data, 10);
  393 +
  394 + qemu_get_timer(f, s->periodic_timer);
  395 + qemu_get_be64s(f, &s->next_periodic_time);
  396 +
  397 + qemu_get_be64s(f, &s->next_second_time);
  398 + qemu_get_timer(f, s->second_timer);
  399 + qemu_get_timer(f, s->second_timer2);
  400 + return 0;
  401 +}
  402 +
  403 +RTCState *rtc_init(int base, int irq)
  404 +{
  405 + RTCState *s;
  406 +
  407 + s = qemu_mallocz(sizeof(RTCState));
  408 + if (!s)
  409 + return NULL;
193 410  
194 411 s->irq = irq;
195 412 s->cmos_data[RTC_REG_A] = 0x26;
... ... @@ -197,7 +414,20 @@ void rtc_init(int base, int irq)
197 414 s->cmos_data[RTC_REG_C] = 0x00;
198 415 s->cmos_data[RTC_REG_D] = 0x80;
199 416  
  417 + s->periodic_timer = qemu_new_timer(vm_clock,
  418 + rtc_periodic_timer, s);
  419 + s->second_timer = qemu_new_timer(vm_clock,
  420 + rtc_update_second, s);
  421 + s->second_timer2 = qemu_new_timer(vm_clock,
  422 + rtc_update_second2, s);
  423 +
  424 + s->next_second_time = qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100;
  425 + qemu_mod_timer(s->second_timer2, s->next_second_time);
  426 +
200 427 register_ioport_write(base, 2, 1, cmos_ioport_write, s);
201 428 register_ioport_read(base, 2, 1, cmos_ioport_read, s);
  429 +
  430 + register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
  431 + return s;
202 432 }
203 433  
... ...