Tizen 2.1 base
[sdk/emulator/qemu.git] / hw / s5pc1xx_rtc.c
1 /*
2  * Real Time Clock for S5C110-based board emulation
3  *
4  * Copyright (c) 2009 Samsung Electronics.
5  * Contributed by Vladimir Monakhov <vladimir.monakhov@ispras.ru>
6  */
7
8 #include "qemu-common.h"
9 #include "qemu-timer.h"
10 #include "sysbus.h"
11
12 /* Registers addresses */
13
14 #define INT_PEND    0x30    /* R/W Interrupt Pending Register */
15 #define RTC_CON     0x40    /* R/W RTC Control Register */
16 #define TIC_CNT     0x44    /* R/W Tick Time Count Register */
17 #define RTC_ALM     0x50    /* R/W RTC Alarm Control Register */
18 #define ALM_SEC     0x54    /* R/W Alarm Second Data Register */
19 #define ALM_MIN     0x58    /* R/W Alarm Minute Data Register */
20 #define ALM_HOUR    0x5C    /* R/W Alarm Hour Data Register */
21 #define ALM_DAY     0x60    /* R/W Alarm Day of the month Data Register */
22 #define ALM_MON     0x64    /* R/W Alarm Month Data Register */
23 #define ALM_YEAR    0x68    /* R/W Alarm Year Data Register */
24 #define BCD_SEC     0x70    /* R/W BCD Second Register */
25 #define BCD_MIN     0x74    /* R/W BCD Minute Register */
26 #define BCD_HOUR    0x78    /* R/W BCD Hour Register */
27 #define BCD_DAY     0x7C    /* R/W BCD value for a day of the month (1-31) */
28 #define BCD_WEEKDAY 0x80    /* R/W BCD value for a day of the week (1-7) */
29 #define BCD_MON     0x84    /* R/W BCD Month Register */
30 #define BCD_YEAR    0x88    /* R/W BCD Year Register */
31 #define CUR_TIC_CNT 0x90    /* R Current Tick Time Counter Register */
32
33 /* Bit mapping for INT_PEND register */
34
35 #define INT_ALM     0x02    /* Alarm interrupt pending bit */
36 #define INT_TIC     0x01    /* Time TIC interrupt pending bit */
37
38 /* Bit mapping for RTC_CON register */
39
40 #define TIC_EN      0x100   /* Tick timer enable */
41 #define TIC_CK_SEL_SHIFT (4)/* Tick timer sub clock selection */
42 #define CLK_RST     0x08    /* RTC clock count reset */
43 #define CNT_SEL     0x04    /* BCD count select */
44 #define CLK_SEL     0x02    /* BCD clock select */
45 #define RTC_EN      0x01    /* RTC control enable */
46
47 /* Bit mapping for RTC_ALM register */
48
49 #define ALM_EN      0x40    /* Alarm global enable */
50 #define YEAR_EN     0x20    /* Year alarm enable */
51 #define MON_EN      0x10    /* Month alarm enable */
52 #define DAY_EN      0x08    /* Day of the month alarm enable */
53 #define HOUR_EN     0x04    /* Hour alarm enable */
54 #define MIN_EN      0x02    /* Minute alarm enable */
55 #define SEC_EN      0x01    /* Second alarm enable */
56
57 #define S5PC1XX_RTC_REG_MEM_SIZE 0x94
58
59
60 typedef struct S5pc1xxRTCState {
61     SysBusDevice busdev;
62
63     uint32_t  regs[BCD_YEAR + 1];
64
65     /* periodic timer */
66     QEMUTimer *periodic_timer;
67     uint32_t  freq_out;
68     uint64_t  last_tick;
69     uint64_t  cur_tic_cnt;
70     qemu_irq  tick_irq;
71
72     /* second update */
73     QEMUTimer *second_timer;
74     struct tm current_tm;
75     qemu_irq  alm_irq;
76
77 } S5pc1xxRTCState;
78
79
80 static void s5pc1xx_rtc_periodic_tick(void *opaque);
81 static void s5pc1xx_rtc_second_update(void *opaque);
82
83 static void s5pc1xx_rtc_set_time(S5pc1xxRTCState *s)
84 {
85     struct tm *tm = &(s->current_tm);
86
87     tm->tm_sec = from_bcd(s->regs[BCD_SEC] & 0x7F);
88     tm->tm_min = from_bcd(s->regs[BCD_MIN] & 0x7F);
89     tm->tm_hour = from_bcd(s->regs[BCD_HOUR] & 0x3F);
90     tm->tm_wday = from_bcd(s->regs[BCD_WEEKDAY] & 0x07);
91     tm->tm_mday = from_bcd(s->regs[BCD_DAY] & 0x3F);
92     /* month value in qemu is between 0 and 11 */
93     tm->tm_mon = from_bcd(s->regs[BCD_MON] & 0x1F) - 1;
94     /* year value is with base_year = 2000 (not 1900 as in qemu) */
95     tm->tm_year = 100 + from_bcd(s->regs[BCD_YEAR] & 0xFF) +
96                   from_bcd((s->regs[BCD_YEAR] >> 8) & 0xF) * 100;
97 }
98
99 static void s5pc1xx_rtc_read_time(S5pc1xxRTCState *s)
100 {
101     const struct tm *tm = &(s->current_tm);
102
103     s->regs[BCD_SEC] = to_bcd(tm->tm_sec);
104     s->regs[BCD_MIN] = to_bcd(tm->tm_min);
105     s->regs[BCD_HOUR] = to_bcd(tm->tm_hour);
106     s->regs[BCD_WEEKDAY] = to_bcd(tm->tm_wday);
107     s->regs[BCD_DAY] = to_bcd(tm->tm_mday);
108     /* month value in qemu is between 0 and 11 */
109     s->regs[BCD_MON] = to_bcd(tm->tm_mon + 1);
110     /* year value is with base_year = 2000 (not 1900 as in qemu) */
111     s->regs[BCD_YEAR] = to_bcd((tm->tm_year - 100) % 100) |
112                         (to_bcd((tm->tm_year - 100) % 1000 / 100) << 8);
113 }
114
115 /* set default values for all fields */
116 static void s5pc1xx_rtc_reset(S5pc1xxRTCState *s)
117 {
118     short i;
119
120     /* stop tick timer */
121     s->regs[RTC_CON] &= ~TIC_EN;
122     s5pc1xx_rtc_periodic_tick(s);
123
124     /* stop second timer */
125     s->regs[RTC_CON] &= ~RTC_EN;
126     s5pc1xx_rtc_second_update(s);
127
128     for (i = 0x30; i < 0x90; i += 0x4) {
129         s->regs[i] = 0;
130     }
131
132     s->last_tick = 0;
133     s->freq_out  = 0;
134 }
135
136 /* Setup next timer tick */
137 static void s5pc1xx_rtc_periodic_update(S5pc1xxRTCState *s)
138 {
139     uint64_t next_periodic_time, last = qemu_get_clock(vm_clock);
140     s->last_tick = last;
141     next_periodic_time =
142         last + muldiv64(s->regs[TIC_CNT], get_ticks_per_sec(), s->freq_out);
143     qemu_mod_timer(s->periodic_timer, next_periodic_time);
144 }
145
146 /* Send periodic interrupt */
147 static void s5pc1xx_rtc_periodic_tick(void *opaque)
148 {
149     S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
150
151     /* tick actually happens not every CUR_TIC_CNT but rather at irq time;
152      * if current ticnto value is needed it is calculated in s5pc1xx_rtc_read */
153     if (s->regs[RTC_CON] & TIC_EN) {
154         qemu_irq_raise(s->tick_irq);
155         s->regs[INT_PEND] |= INT_TIC;
156         s5pc1xx_rtc_periodic_update(s);
157     } else {
158         s->last_tick = 0;
159         qemu_del_timer(s->periodic_timer);
160     }
161 }
162
163 /* Update tick timer frequency */
164 static void s5pc1xx_rtc_periodic_rate(S5pc1xxRTCState *s)
165 {
166     short freq_code;
167
168     /* get four binary digits to determine the frequency */
169     freq_code = (s->regs[RTC_CON] >> TIC_CK_SEL_SHIFT) & 0xF;
170
171     /* tick timer frequency */
172     s->freq_out = 32768 / (1 << freq_code);
173 }
174
175 /* Month is between 0 and 11 */
176 static int get_days_in_month(int month, int year)
177 {
178     short d;
179     static const int days_tab[12] = {
180         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
181     };
182
183     if ((unsigned) month >= 12) {
184         return 31;
185     }
186     d = days_tab[month];
187     if (month == 1) {
188         if ((year % 4) == 0) {
189             d++;
190         }
191     }
192     return d;
193 }
194
195 /* Update 'tm' to the next second */
196 static void s5pc1xx_rtc_next_second(struct tm *tm)
197 {
198     int days_in_month;
199
200     tm->tm_sec++;
201     if ((unsigned) tm->tm_sec >= 60) {
202         tm->tm_sec = 0;
203         tm->tm_min++;
204         if ((unsigned) tm->tm_min >= 60) {
205             tm->tm_min = 0;
206             tm->tm_hour++;
207             if ((unsigned) tm->tm_hour >= 24) {
208                 tm->tm_hour = 0;
209                 /* next day */
210                 tm->tm_wday++;
211                 /* TODO: check if wday value in qemu is between 1 and 7 */
212                 if ((unsigned) tm->tm_wday >= 8) {
213                     tm->tm_wday = 1;
214                 }
215                 days_in_month = get_days_in_month(tm->tm_mon, tm->tm_year);
216                 tm->tm_mday++;
217                 if (tm->tm_mday < 1) {
218                     tm->tm_mday = 1;
219                 } else if (tm->tm_mday > days_in_month) {
220                     tm->tm_mday = 1;
221                     tm->tm_mon++;
222                     if (tm->tm_mon >= 12) {
223                         tm->tm_mon = 0;
224                         tm->tm_year++;
225                     }
226                 }
227             }
228         }
229     }
230 }
231
232 /* Working with up-to-date time and check alarm */
233 static void s5pc1xx_rtc_second_update(void *opaque)
234 {
235     S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
236     uint64_t next_second_time;
237
238     if (s->regs[RTC_CON] & RTC_EN) {
239         /* check if the alarm is generally enabled */
240         /* then check if an alarm of at least one kind is enabled */
241         if (s->regs[RTC_ALM] & ALM_EN &&
242             (s->regs[RTC_ALM] & SEC_EN ||
243              s->regs[RTC_ALM] & MIN_EN ||
244              s->regs[RTC_ALM] & HOUR_EN ||
245              s->regs[RTC_ALM] & DAY_EN ||
246              s->regs[RTC_ALM] & MON_EN ||
247              s->regs[RTC_ALM] & YEAR_EN)) {
248
249             /* check alarm values together with corresponding permissive bits */
250             if (((s->regs[ALM_SEC] & 0x7F) ==
251                     to_bcd(s->current_tm.tm_sec) ||
252                  !(s->regs[RTC_ALM] & SEC_EN)) &&
253
254                 ((s->regs[ALM_MIN] & 0x7F) ==
255                     to_bcd(s->current_tm.tm_min) ||
256                  !(s->regs[RTC_ALM] & MIN_EN)) &&
257
258                 ((s->regs[ALM_HOUR] & 0x3F) ==
259                     to_bcd(s->current_tm.tm_hour) ||
260                  !(s->regs[RTC_ALM] & HOUR_EN)) &&
261
262                 ((s->regs[ALM_DAY] & 0x3F) ==
263                     to_bcd(s->current_tm.tm_mday) ||
264                  !(s->regs[RTC_ALM] & DAY_EN)) &&
265
266                 ((s->regs[ALM_MON]  & 0x1F) ==
267                     to_bcd(s->current_tm.tm_mon) ||
268                  !(s->regs[RTC_ALM] & MON_EN)) &&
269
270                 (((s->regs[ALM_YEAR] & 0xFF) ==
271                     to_bcd((s->current_tm.tm_year - 100) % 100) &&
272                   ((s->regs[ALM_YEAR] >> 8) & 0xF) ==
273                     to_bcd((s->current_tm.tm_year - 100) % 1000 / 100)) ||
274                  !(s->regs[RTC_ALM] & YEAR_EN))) {
275
276                 qemu_irq_raise(s->alm_irq);
277                 s->regs[INT_PEND] |= INT_ALM;
278             }
279         }
280
281         s5pc1xx_rtc_next_second(&s->current_tm);
282         next_second_time = qemu_get_clock(vm_clock) + get_ticks_per_sec();
283         qemu_mod_timer(s->second_timer, next_second_time);
284     } else {
285         qemu_del_timer(s->second_timer);
286     }
287 }
288
289 static uint32_t s5pc1xx_rtc_read(void *opaque, target_phys_addr_t offset)
290 {
291     S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
292
293     switch (offset) {
294         case BCD_SEC:
295         case BCD_MIN:
296         case BCD_HOUR:
297         case BCD_WEEKDAY:
298         case BCD_DAY:
299         case BCD_MON:
300         case BCD_YEAR:
301             s5pc1xx_rtc_read_time(s);
302             /* fallthrough */
303         case INT_PEND:
304         case RTC_CON:
305         case TIC_CNT:
306         case RTC_ALM:
307         case ALM_SEC:
308         case ALM_MIN:
309         case ALM_HOUR:
310         case ALM_DAY:
311         case ALM_MON:
312         case ALM_YEAR:
313             return s->regs[offset];
314         case CUR_TIC_CNT:
315             if (s->freq_out && s->last_tick && s->regs[TIC_CNT]) {
316                 s->cur_tic_cnt = s->regs[TIC_CNT] -
317                     muldiv64(qemu_get_clock(vm_clock) - s->last_tick,
318                              s->freq_out, get_ticks_per_sec()) %
319                              s->regs[TIC_CNT];
320             } else {
321                 s->cur_tic_cnt = 0;
322             }
323
324             return s->cur_tic_cnt;
325         default:
326             hw_error("s5pc1xx.rtc: bad read offset " TARGET_FMT_plx "\n",
327                      offset);
328     }
329 }
330
331 static void s5pc1xx_rtc_write(void *opaque, target_phys_addr_t offset,
332                               uint32_t value)
333 {
334     S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
335
336     switch (offset) {
337         case INT_PEND:
338             /* lower interrupts if any */
339             if (value & INT_TIC) {
340                 qemu_irq_lower(s->tick_irq);
341             }
342             if (value & INT_ALM) {
343                 qemu_irq_lower(s->alm_irq);
344             }
345
346             /* clear INT_* bits if they are set in value */
347             s->regs[INT_PEND] &= ~(value & (INT_TIC | INT_ALM));
348             break;
349         case RTC_CON:
350             /* reset tick counter */
351             if (value & CLK_RST) {
352                 s5pc1xx_rtc_reset(s);
353                 value &= ~CLK_RST;
354             }
355             /* start second timer */
356             if ((value & RTC_EN) > (s->regs[RTC_CON] & RTC_EN)) {
357                 s->regs[RTC_CON] |= RTC_EN;
358                 qemu_get_timedate(&s->current_tm, 0);
359                 s5pc1xx_rtc_second_update(s);
360             }
361             /* start tick timer */
362             if ((value & TIC_EN) > (s->regs[RTC_CON] & TIC_EN)) {
363                 s->regs[RTC_CON] = value;
364                 s5pc1xx_rtc_periodic_rate(s);
365                 s5pc1xx_rtc_periodic_update(s);
366                 break;
367             } else if ((value & TIC_EN) < (s->regs[RTC_CON] & TIC_EN)) {
368                 qemu_del_timer(s->periodic_timer);
369             }
370             s->regs[RTC_CON] = value;
371             s5pc1xx_rtc_periodic_rate(s);
372             break;
373         case TIC_CNT:
374         case RTC_ALM:
375         case ALM_SEC:
376         case ALM_MIN:
377         case ALM_HOUR:
378         case ALM_DAY:
379         case ALM_MON:
380         case ALM_YEAR:
381             s->regs[offset] = value;
382             break;
383         case BCD_SEC:
384         case BCD_MIN:
385         case BCD_HOUR:
386         case BCD_WEEKDAY:
387         case BCD_DAY:
388         case BCD_MON:
389         case BCD_YEAR:
390             s->regs[offset] = value;
391             /* if in disabled mode, do not update the time */
392             if (s->regs[RTC_CON] & RTC_EN) {
393                 s5pc1xx_rtc_set_time(s);
394             }
395             break;
396         default:
397             hw_error("s5pc1xx.rtc: bad write offset " TARGET_FMT_plx "\n",
398                      offset);
399     }
400 }
401
402 /* Memory mapped interface */
403 static CPUReadMemoryFunc * const s5pc1xx_rtc_mm_read[] = {
404     s5pc1xx_rtc_read,
405     s5pc1xx_rtc_read,
406     s5pc1xx_rtc_read
407 };
408
409 static CPUWriteMemoryFunc * const s5pc1xx_rtc_mm_write[] = {
410     s5pc1xx_rtc_write,
411     s5pc1xx_rtc_write,
412     s5pc1xx_rtc_write
413 };
414
415 static void s5pc1xx_rtc_save(QEMUFile *f, void *opaque)
416 {
417     S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
418     int i;
419
420     s5pc1xx_rtc_read_time(s);
421     for (i = INT_PEND; i <= BCD_YEAR; i++) {
422         qemu_put_be32s(f, &s->regs[i]);
423     }
424     qemu_put_timer(f, s->second_timer);
425
426     qemu_put_be64s(f, &s->last_tick);
427     qemu_put_be32s(f, &s->freq_out);
428     qemu_put_timer(f, s->periodic_timer);
429 }
430
431 static int s5pc1xx_rtc_load(QEMUFile *f, void *opaque, int version_id)
432 {
433     S5pc1xxRTCState *s = (S5pc1xxRTCState *)opaque;
434     int i;
435
436     if (version_id != 1) {
437         return -EINVAL;
438     }
439
440     for (i = INT_PEND; i <= BCD_YEAR; i++) {
441         qemu_get_be32s(f, &s->regs[i]);
442     }
443
444     //s5pc1xx_rtc_set_time(s);
445     qemu_get_timedate(&s->current_tm, 0);
446     s5pc1xx_rtc_read_time(s);
447     qemu_get_timer(f, s->second_timer);
448
449     qemu_get_be64s(f, &s->last_tick);
450     qemu_get_be32s(f, &s->freq_out);
451     qemu_get_timer(f, s->periodic_timer);
452
453     return 0;
454 }
455
456 /* initialize and start timers */
457 static int s5pc1xx_rtc_init(SysBusDevice *dev)
458 {
459     int iomemory;
460     S5pc1xxRTCState *s = FROM_SYSBUS(S5pc1xxRTCState, dev);
461
462     sysbus_init_irq(dev, &s->alm_irq);
463     sysbus_init_irq(dev, &s->tick_irq);
464
465     s->periodic_timer =
466         qemu_new_timer(vm_clock, s5pc1xx_rtc_periodic_tick, s);
467     s->second_timer =
468         qemu_new_timer(vm_clock, s5pc1xx_rtc_second_update, s);
469
470     iomemory =
471         cpu_register_io_memory(s5pc1xx_rtc_mm_read, s5pc1xx_rtc_mm_write, s, DEVICE_NATIVE_ENDIAN);
472     sysbus_init_mmio(dev, S5PC1XX_RTC_REG_MEM_SIZE, iomemory);
473
474     s5pc1xx_rtc_reset(s);
475
476     register_savevm(&dev->qdev, "s5pc1xx.rtc", -1, 1,
477                     s5pc1xx_rtc_save, s5pc1xx_rtc_load, s);
478
479     return 0;
480 }
481
482 static void s5pc1xx_rtc_register_devices(void)
483 {
484     sysbus_register_dev("s5pc1xx.rtc", sizeof(S5pc1xxRTCState),
485                         s5pc1xx_rtc_init);
486 }
487
488 device_init(s5pc1xx_rtc_register_devices)