]> Git Repo - qemu.git/blob - hw/timer/exynos4210_rtc.c
Merge remote-tracking branch 'bonzini/iommu-for-anthony' into staging
[qemu.git] / hw / timer / exynos4210_rtc.c
1 /*
2  * Samsung exynos4210 Real Time Clock
3  *
4  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
5  *  Ogurtsov Oleg <[email protected]>
6  *
7  *  This program is free software; you can redistribute it and/or modify it
8  *  under the terms of the GNU General Public License as published by the
9  *  Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful, but WITHOUT
13  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15  *  for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21
22 /* Description:
23  * Register RTCCON:
24  *  CLKSEL Bit[1] not used
25  *  CLKOUTEN Bit[9] not used
26  */
27
28 #include "hw/sysbus.h"
29 #include "qemu/timer.h"
30 #include "qemu-common.h"
31 #include "hw/ptimer.h"
32
33 #include "hw/hw.h"
34 #include "sysemu/sysemu.h"
35
36 #include "hw/arm/exynos4210.h"
37
38 #define DEBUG_RTC 0
39
40 #if DEBUG_RTC
41 #define DPRINTF(fmt, ...) \
42         do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
43                 ## __VA_ARGS__); } while (0)
44 #else
45 #define DPRINTF(fmt, ...) do {} while (0)
46 #endif
47
48 #define     EXYNOS4210_RTC_REG_MEM_SIZE     0x0100
49
50 #define     INTP            0x0030
51 #define     RTCCON          0x0040
52 #define     TICCNT          0x0044
53 #define     RTCALM          0x0050
54 #define     ALMSEC          0x0054
55 #define     ALMMIN          0x0058
56 #define     ALMHOUR         0x005C
57 #define     ALMDAY          0x0060
58 #define     ALMMON          0x0064
59 #define     ALMYEAR         0x0068
60 #define     BCDSEC          0x0070
61 #define     BCDMIN          0x0074
62 #define     BCDHOUR         0x0078
63 #define     BCDDAY          0x007C
64 #define     BCDDAYWEEK      0x0080
65 #define     BCDMON          0x0084
66 #define     BCDYEAR         0x0088
67 #define     CURTICNT        0x0090
68
69 #define     TICK_TIMER_ENABLE   0x0100
70 #define     TICNT_THRESHHOLD    2
71
72
73 #define     RTC_ENABLE          0x0001
74
75 #define     INTP_TICK_ENABLE    0x0001
76 #define     INTP_ALM_ENABLE     0x0002
77
78 #define     ALARM_INT_ENABLE    0x0040
79
80 #define     RTC_BASE_FREQ       32768
81
82 typedef struct Exynos4210RTCState {
83     SysBusDevice busdev;
84     MemoryRegion iomem;
85
86     /* registers */
87     uint32_t    reg_intp;
88     uint32_t    reg_rtccon;
89     uint32_t    reg_ticcnt;
90     uint32_t    reg_rtcalm;
91     uint32_t    reg_almsec;
92     uint32_t    reg_almmin;
93     uint32_t    reg_almhour;
94     uint32_t    reg_almday;
95     uint32_t    reg_almmon;
96     uint32_t    reg_almyear;
97     uint32_t    reg_curticcnt;
98
99     ptimer_state    *ptimer;        /* tick timer */
100     ptimer_state    *ptimer_1Hz;    /* clock timer */
101     uint32_t        freq;
102
103     qemu_irq        tick_irq;   /* Time Tick Generator irq */
104     qemu_irq        alm_irq;    /* alarm irq */
105
106     struct tm   current_tm;     /* current time */
107 } Exynos4210RTCState;
108
109 #define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
110
111 /*** VMState ***/
112 static const VMStateDescription vmstate_exynos4210_rtc_state = {
113     .name = "exynos4210.rtc",
114     .version_id = 1,
115     .minimum_version_id = 1,
116     .minimum_version_id_old = 1,
117     .fields = (VMStateField[]) {
118         VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
119         VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
120         VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
121         VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
122         VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
123         VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
124         VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
125         VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
126         VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
127         VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
128         VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
129         VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
130         VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
131         VMSTATE_UINT32(freq, Exynos4210RTCState),
132         VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
133         VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
134         VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
135         VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
136         VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
137         VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
138         VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
139         VMSTATE_END_OF_LIST()
140     }
141 };
142
143 #define BCD3DIGITS(x) \
144     ((uint32_t)to_bcd((uint8_t)(x % 100)) + \
145     ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8))
146
147 static void check_alarm_raise(Exynos4210RTCState *s)
148 {
149     unsigned int alarm_raise = 0;
150     struct tm stm = s->current_tm;
151
152     if ((s->reg_rtcalm & 0x01) &&
153         (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) {
154         alarm_raise = 1;
155     }
156     if ((s->reg_rtcalm & 0x02) &&
157         (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) {
158         alarm_raise = 1;
159     }
160     if ((s->reg_rtcalm & 0x04) &&
161         (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) {
162         alarm_raise = 1;
163     }
164     if ((s->reg_rtcalm & 0x08) &&
165         (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) {
166         alarm_raise = 1;
167     }
168     if ((s->reg_rtcalm & 0x10) &&
169          (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) {
170         alarm_raise = 1;
171     }
172     if ((s->reg_rtcalm & 0x20) &&
173         (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) {
174         alarm_raise = 1;
175     }
176
177     if (alarm_raise) {
178         DPRINTF("ALARM IRQ\n");
179         /* set irq status */
180         s->reg_intp |= INTP_ALM_ENABLE;
181         qemu_irq_raise(s->alm_irq);
182     }
183 }
184
185 /*
186  * RTC update frequency
187  * Parameters:
188  *     reg_value - current RTCCON register or his new value
189  */
190 static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
191                                        uint32_t reg_value)
192 {
193     uint32_t freq;
194
195     freq = s->freq;
196     /* set frequncy for time generator */
197     s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
198
199     if (freq != s->freq) {
200         ptimer_set_freq(s->ptimer, s->freq);
201         DPRINTF("freq=%dHz\n", s->freq);
202     }
203 }
204
205 /* month is between 0 and 11. */
206 static int get_days_in_month(int month, int year)
207 {
208     static const int days_tab[12] = {
209         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
210     };
211     int d;
212     if ((unsigned)month >= 12) {
213         return 31;
214     }
215     d = days_tab[month];
216     if (month == 1) {
217         if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
218             d++;
219         }
220     }
221     return d;
222 }
223
224 /* update 'tm' to the next second */
225 static void rtc_next_second(struct tm *tm)
226 {
227     int days_in_month;
228
229     tm->tm_sec++;
230     if ((unsigned)tm->tm_sec >= 60) {
231         tm->tm_sec = 0;
232         tm->tm_min++;
233         if ((unsigned)tm->tm_min >= 60) {
234             tm->tm_min = 0;
235             tm->tm_hour++;
236             if ((unsigned)tm->tm_hour >= 24) {
237                 tm->tm_hour = 0;
238                 /* next day */
239                 tm->tm_wday++;
240                 if ((unsigned)tm->tm_wday >= 7) {
241                     tm->tm_wday = 0;
242                 }
243                 days_in_month = get_days_in_month(tm->tm_mon,
244                                                   tm->tm_year + 1900);
245                 tm->tm_mday++;
246                 if (tm->tm_mday < 1) {
247                     tm->tm_mday = 1;
248                 } else if (tm->tm_mday > days_in_month) {
249                     tm->tm_mday = 1;
250                     tm->tm_mon++;
251                     if (tm->tm_mon >= 12) {
252                         tm->tm_mon = 0;
253                         tm->tm_year++;
254                     }
255                 }
256             }
257         }
258     }
259 }
260
261 /*
262  * tick handler
263  */
264 static void exynos4210_rtc_tick(void *opaque)
265 {
266     Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
267
268     DPRINTF("TICK IRQ\n");
269     /* set irq status */
270     s->reg_intp |= INTP_TICK_ENABLE;
271     /* raise IRQ */
272     qemu_irq_raise(s->tick_irq);
273
274     /* restart timer */
275     ptimer_set_count(s->ptimer, s->reg_ticcnt);
276     ptimer_run(s->ptimer, 1);
277 }
278
279 /*
280  * 1Hz clock handler
281  */
282 static void exynos4210_rtc_1Hz_tick(void *opaque)
283 {
284     Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
285
286     rtc_next_second(&s->current_tm);
287     /* DPRINTF("1Hz tick\n"); */
288
289     /* raise IRQ */
290     if (s->reg_rtcalm & ALARM_INT_ENABLE) {
291         check_alarm_raise(s);
292     }
293
294     ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
295     ptimer_run(s->ptimer_1Hz, 1);
296 }
297
298 /*
299  * RTC Read
300  */
301 static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset,
302         unsigned size)
303 {
304     uint32_t value = 0;
305     Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
306
307     switch (offset) {
308     case INTP:
309         value = s->reg_intp;
310         break;
311     case RTCCON:
312         value = s->reg_rtccon;
313         break;
314     case TICCNT:
315         value = s->reg_ticcnt;
316         break;
317     case RTCALM:
318         value = s->reg_rtcalm;
319         break;
320     case ALMSEC:
321         value = s->reg_almsec;
322         break;
323     case ALMMIN:
324         value = s->reg_almmin;
325         break;
326     case ALMHOUR:
327         value = s->reg_almhour;
328         break;
329     case ALMDAY:
330         value = s->reg_almday;
331         break;
332     case ALMMON:
333         value = s->reg_almmon;
334         break;
335     case ALMYEAR:
336         value = s->reg_almyear;
337         break;
338
339     case BCDSEC:
340         value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec);
341         break;
342     case BCDMIN:
343         value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min);
344         break;
345     case BCDHOUR:
346         value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour);
347         break;
348     case BCDDAYWEEK:
349         value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday);
350         break;
351     case BCDDAY:
352         value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday);
353         break;
354     case BCDMON:
355         value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1);
356         break;
357     case BCDYEAR:
358         value = BCD3DIGITS(s->current_tm.tm_year);
359         break;
360
361     case CURTICNT:
362         s->reg_curticcnt = ptimer_get_count(s->ptimer);
363         value = s->reg_curticcnt;
364         break;
365
366     default:
367         fprintf(stderr,
368                 "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
369                 offset);
370         break;
371     }
372     return value;
373 }
374
375 /*
376  * RTC Write
377  */
378 static void exynos4210_rtc_write(void *opaque, hwaddr offset,
379         uint64_t value, unsigned size)
380 {
381     Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
382
383     switch (offset) {
384     case INTP:
385         if (value & INTP_ALM_ENABLE) {
386             qemu_irq_lower(s->alm_irq);
387             s->reg_intp &= (~INTP_ALM_ENABLE);
388         }
389         if (value & INTP_TICK_ENABLE) {
390             qemu_irq_lower(s->tick_irq);
391             s->reg_intp &= (~INTP_TICK_ENABLE);
392         }
393         break;
394     case RTCCON:
395         if (value & RTC_ENABLE) {
396             exynos4210_rtc_update_freq(s, value);
397         }
398         if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
399             /* clock timer */
400             ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
401             ptimer_run(s->ptimer_1Hz, 1);
402             DPRINTF("run clock timer\n");
403         }
404         if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
405             /* tick timer */
406             ptimer_stop(s->ptimer);
407             /* clock timer */
408             ptimer_stop(s->ptimer_1Hz);
409             DPRINTF("stop all timers\n");
410         }
411         if (value & RTC_ENABLE) {
412             if ((value & TICK_TIMER_ENABLE) >
413                 (s->reg_rtccon & TICK_TIMER_ENABLE) &&
414                 (s->reg_ticcnt)) {
415                 ptimer_set_count(s->ptimer, s->reg_ticcnt);
416                 ptimer_run(s->ptimer, 1);
417                 DPRINTF("run tick timer\n");
418             }
419             if ((value & TICK_TIMER_ENABLE) <
420                 (s->reg_rtccon & TICK_TIMER_ENABLE)) {
421                 ptimer_stop(s->ptimer);
422             }
423         }
424         s->reg_rtccon = value;
425         break;
426     case TICCNT:
427         if (value > TICNT_THRESHHOLD) {
428             s->reg_ticcnt = value;
429         } else {
430             fprintf(stderr,
431                     "[exynos4210.rtc: bad TICNT value %u ]\n",
432                     (uint32_t)value);
433         }
434         break;
435
436     case RTCALM:
437         s->reg_rtcalm = value;
438         break;
439     case ALMSEC:
440         s->reg_almsec = (value & 0x7f);
441         break;
442     case ALMMIN:
443         s->reg_almmin = (value & 0x7f);
444         break;
445     case ALMHOUR:
446         s->reg_almhour = (value & 0x3f);
447         break;
448     case ALMDAY:
449         s->reg_almday = (value & 0x3f);
450         break;
451     case ALMMON:
452         s->reg_almmon = (value & 0x1f);
453         break;
454     case ALMYEAR:
455         s->reg_almyear = (value & 0x0fff);
456         break;
457
458     case BCDSEC:
459         if (s->reg_rtccon & RTC_ENABLE) {
460             s->current_tm.tm_sec = (int)from_bcd((uint8_t)value);
461         }
462         break;
463     case BCDMIN:
464         if (s->reg_rtccon & RTC_ENABLE) {
465             s->current_tm.tm_min = (int)from_bcd((uint8_t)value);
466         }
467         break;
468     case BCDHOUR:
469         if (s->reg_rtccon & RTC_ENABLE) {
470             s->current_tm.tm_hour = (int)from_bcd((uint8_t)value);
471         }
472         break;
473     case BCDDAYWEEK:
474         if (s->reg_rtccon & RTC_ENABLE) {
475             s->current_tm.tm_wday = (int)from_bcd((uint8_t)value);
476         }
477         break;
478     case BCDDAY:
479         if (s->reg_rtccon & RTC_ENABLE) {
480             s->current_tm.tm_mday = (int)from_bcd((uint8_t)value);
481         }
482         break;
483     case BCDMON:
484         if (s->reg_rtccon & RTC_ENABLE) {
485             s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1;
486         }
487         break;
488     case BCDYEAR:
489         if (s->reg_rtccon & RTC_ENABLE) {
490             /* 3 digits */
491             s->current_tm.tm_year = (int)from_bcd((uint8_t)value) +
492                     (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100;
493         }
494         break;
495
496     default:
497         fprintf(stderr,
498                 "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
499                 offset);
500         break;
501
502     }
503 }
504
505 /*
506  * Set default values to timer fields and registers
507  */
508 static void exynos4210_rtc_reset(DeviceState *d)
509 {
510     Exynos4210RTCState *s = (Exynos4210RTCState *)d;
511
512     qemu_get_timedate(&s->current_tm, 0);
513
514     DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
515             s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
516             s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
517
518     s->reg_intp = 0;
519     s->reg_rtccon = 0;
520     s->reg_ticcnt = 0;
521     s->reg_rtcalm = 0;
522     s->reg_almsec = 0;
523     s->reg_almmin = 0;
524     s->reg_almhour = 0;
525     s->reg_almday = 0;
526     s->reg_almmon = 0;
527     s->reg_almyear = 0;
528
529     s->reg_curticcnt = 0;
530
531     exynos4210_rtc_update_freq(s, s->reg_rtccon);
532     ptimer_stop(s->ptimer);
533     ptimer_stop(s->ptimer_1Hz);
534 }
535
536 static const MemoryRegionOps exynos4210_rtc_ops = {
537     .read = exynos4210_rtc_read,
538     .write = exynos4210_rtc_write,
539     .endianness = DEVICE_NATIVE_ENDIAN,
540 };
541
542 /*
543  * RTC timer initialization
544  */
545 static int exynos4210_rtc_init(SysBusDevice *dev)
546 {
547     Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev);
548     QEMUBH *bh;
549
550     bh = qemu_bh_new(exynos4210_rtc_tick, s);
551     s->ptimer = ptimer_init(bh);
552     ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
553     exynos4210_rtc_update_freq(s, 0);
554
555     bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
556     s->ptimer_1Hz = ptimer_init(bh);
557     ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
558
559     sysbus_init_irq(dev, &s->alm_irq);
560     sysbus_init_irq(dev, &s->tick_irq);
561
562     memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc",
563             EXYNOS4210_RTC_REG_MEM_SIZE);
564     sysbus_init_mmio(dev, &s->iomem);
565
566     return 0;
567 }
568
569 static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
570 {
571     DeviceClass *dc = DEVICE_CLASS(klass);
572     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
573
574     k->init = exynos4210_rtc_init;
575     dc->reset = exynos4210_rtc_reset;
576     dc->vmsd = &vmstate_exynos4210_rtc_state;
577 }
578
579 static const TypeInfo exynos4210_rtc_info = {
580     .name          = "exynos4210.rtc",
581     .parent        = TYPE_SYS_BUS_DEVICE,
582     .instance_size = sizeof(Exynos4210RTCState),
583     .class_init    = exynos4210_rtc_class_init,
584 };
585
586 static void exynos4210_rtc_register_types(void)
587 {
588     type_register_static(&exynos4210_rtc_info);
589 }
590
591 type_init(exynos4210_rtc_register_types)
This page took 0.057933 seconds and 4 git commands to generate.