remove function during multi-function hot-add
[qemu/ar7.git] / hw / timer / exynos4210_rtc.c
blobbf2ee9f80e8931dc1e43a04674a4edb891bdfbdb
1 /*
2 * Samsung exynos4210 Real Time Clock
4 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
5 * Ogurtsov Oleg <o.ogurtsov@samsung.com>
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.
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.
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/>.
22 /* Description:
23 * Register RTCCON:
24 * CLKSEL Bit[1] not used
25 * CLKOUTEN Bit[9] not used
28 #include "hw/sysbus.h"
29 #include "qemu/timer.h"
30 #include "qemu-common.h"
31 #include "hw/ptimer.h"
33 #include "hw/hw.h"
34 #include "sysemu/sysemu.h"
36 #include "hw/arm/exynos4210.h"
38 #define DEBUG_RTC 0
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
48 #define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100
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
69 #define TICK_TIMER_ENABLE 0x0100
70 #define TICNT_THRESHOLD 2
73 #define RTC_ENABLE 0x0001
75 #define INTP_TICK_ENABLE 0x0001
76 #define INTP_ALM_ENABLE 0x0002
78 #define ALARM_INT_ENABLE 0x0040
80 #define RTC_BASE_FREQ 32768
82 #define TYPE_EXYNOS4210_RTC "exynos4210.rtc"
83 #define EXYNOS4210_RTC(obj) \
84 OBJECT_CHECK(Exynos4210RTCState, (obj), TYPE_EXYNOS4210_RTC)
86 typedef struct Exynos4210RTCState {
87 SysBusDevice parent_obj;
89 MemoryRegion iomem;
91 /* registers */
92 uint32_t reg_intp;
93 uint32_t reg_rtccon;
94 uint32_t reg_ticcnt;
95 uint32_t reg_rtcalm;
96 uint32_t reg_almsec;
97 uint32_t reg_almmin;
98 uint32_t reg_almhour;
99 uint32_t reg_almday;
100 uint32_t reg_almmon;
101 uint32_t reg_almyear;
102 uint32_t reg_curticcnt;
104 ptimer_state *ptimer; /* tick timer */
105 ptimer_state *ptimer_1Hz; /* clock timer */
106 uint32_t freq;
108 qemu_irq tick_irq; /* Time Tick Generator irq */
109 qemu_irq alm_irq; /* alarm irq */
111 struct tm current_tm; /* current time */
112 } Exynos4210RTCState;
114 #define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
116 /*** VMState ***/
117 static const VMStateDescription vmstate_exynos4210_rtc_state = {
118 .name = "exynos4210.rtc",
119 .version_id = 1,
120 .minimum_version_id = 1,
121 .fields = (VMStateField[]) {
122 VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
123 VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
124 VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
125 VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
126 VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
127 VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
128 VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
129 VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
130 VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
131 VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
132 VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
133 VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
134 VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
135 VMSTATE_UINT32(freq, Exynos4210RTCState),
136 VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
137 VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
138 VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
139 VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
140 VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
141 VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
142 VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
143 VMSTATE_END_OF_LIST()
147 #define BCD3DIGITS(x) \
148 ((uint32_t)to_bcd((uint8_t)(x % 100)) + \
149 ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8))
151 static void check_alarm_raise(Exynos4210RTCState *s)
153 unsigned int alarm_raise = 0;
154 struct tm stm = s->current_tm;
156 if ((s->reg_rtcalm & 0x01) &&
157 (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) {
158 alarm_raise = 1;
160 if ((s->reg_rtcalm & 0x02) &&
161 (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) {
162 alarm_raise = 1;
164 if ((s->reg_rtcalm & 0x04) &&
165 (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) {
166 alarm_raise = 1;
168 if ((s->reg_rtcalm & 0x08) &&
169 (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) {
170 alarm_raise = 1;
172 if ((s->reg_rtcalm & 0x10) &&
173 (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) {
174 alarm_raise = 1;
176 if ((s->reg_rtcalm & 0x20) &&
177 (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) {
178 alarm_raise = 1;
181 if (alarm_raise) {
182 DPRINTF("ALARM IRQ\n");
183 /* set irq status */
184 s->reg_intp |= INTP_ALM_ENABLE;
185 qemu_irq_raise(s->alm_irq);
190 * RTC update frequency
191 * Parameters:
192 * reg_value - current RTCCON register or his new value
194 static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
195 uint32_t reg_value)
197 uint32_t freq;
199 freq = s->freq;
200 /* set frequncy for time generator */
201 s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
203 if (freq != s->freq) {
204 ptimer_set_freq(s->ptimer, s->freq);
205 DPRINTF("freq=%dHz\n", s->freq);
209 /* month is between 0 and 11. */
210 static int get_days_in_month(int month, int year)
212 static const int days_tab[12] = {
213 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
215 int d;
216 if ((unsigned)month >= 12) {
217 return 31;
219 d = days_tab[month];
220 if (month == 1) {
221 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
222 d++;
225 return d;
228 /* update 'tm' to the next second */
229 static void rtc_next_second(struct tm *tm)
231 int days_in_month;
233 tm->tm_sec++;
234 if ((unsigned)tm->tm_sec >= 60) {
235 tm->tm_sec = 0;
236 tm->tm_min++;
237 if ((unsigned)tm->tm_min >= 60) {
238 tm->tm_min = 0;
239 tm->tm_hour++;
240 if ((unsigned)tm->tm_hour >= 24) {
241 tm->tm_hour = 0;
242 /* next day */
243 tm->tm_wday++;
244 if ((unsigned)tm->tm_wday >= 7) {
245 tm->tm_wday = 0;
247 days_in_month = get_days_in_month(tm->tm_mon,
248 tm->tm_year + 1900);
249 tm->tm_mday++;
250 if (tm->tm_mday < 1) {
251 tm->tm_mday = 1;
252 } else if (tm->tm_mday > days_in_month) {
253 tm->tm_mday = 1;
254 tm->tm_mon++;
255 if (tm->tm_mon >= 12) {
256 tm->tm_mon = 0;
257 tm->tm_year++;
266 * tick handler
268 static void exynos4210_rtc_tick(void *opaque)
270 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
272 DPRINTF("TICK IRQ\n");
273 /* set irq status */
274 s->reg_intp |= INTP_TICK_ENABLE;
275 /* raise IRQ */
276 qemu_irq_raise(s->tick_irq);
278 /* restart timer */
279 ptimer_set_count(s->ptimer, s->reg_ticcnt);
280 ptimer_run(s->ptimer, 1);
284 * 1Hz clock handler
286 static void exynos4210_rtc_1Hz_tick(void *opaque)
288 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
290 rtc_next_second(&s->current_tm);
291 /* DPRINTF("1Hz tick\n"); */
293 /* raise IRQ */
294 if (s->reg_rtcalm & ALARM_INT_ENABLE) {
295 check_alarm_raise(s);
298 ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
299 ptimer_run(s->ptimer_1Hz, 1);
303 * RTC Read
305 static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset,
306 unsigned size)
308 uint32_t value = 0;
309 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
311 switch (offset) {
312 case INTP:
313 value = s->reg_intp;
314 break;
315 case RTCCON:
316 value = s->reg_rtccon;
317 break;
318 case TICCNT:
319 value = s->reg_ticcnt;
320 break;
321 case RTCALM:
322 value = s->reg_rtcalm;
323 break;
324 case ALMSEC:
325 value = s->reg_almsec;
326 break;
327 case ALMMIN:
328 value = s->reg_almmin;
329 break;
330 case ALMHOUR:
331 value = s->reg_almhour;
332 break;
333 case ALMDAY:
334 value = s->reg_almday;
335 break;
336 case ALMMON:
337 value = s->reg_almmon;
338 break;
339 case ALMYEAR:
340 value = s->reg_almyear;
341 break;
343 case BCDSEC:
344 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec);
345 break;
346 case BCDMIN:
347 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min);
348 break;
349 case BCDHOUR:
350 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour);
351 break;
352 case BCDDAYWEEK:
353 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday);
354 break;
355 case BCDDAY:
356 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday);
357 break;
358 case BCDMON:
359 value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1);
360 break;
361 case BCDYEAR:
362 value = BCD3DIGITS(s->current_tm.tm_year);
363 break;
365 case CURTICNT:
366 s->reg_curticcnt = ptimer_get_count(s->ptimer);
367 value = s->reg_curticcnt;
368 break;
370 default:
371 fprintf(stderr,
372 "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
373 offset);
374 break;
376 return value;
380 * RTC Write
382 static void exynos4210_rtc_write(void *opaque, hwaddr offset,
383 uint64_t value, unsigned size)
385 Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
387 switch (offset) {
388 case INTP:
389 if (value & INTP_ALM_ENABLE) {
390 qemu_irq_lower(s->alm_irq);
391 s->reg_intp &= (~INTP_ALM_ENABLE);
393 if (value & INTP_TICK_ENABLE) {
394 qemu_irq_lower(s->tick_irq);
395 s->reg_intp &= (~INTP_TICK_ENABLE);
397 break;
398 case RTCCON:
399 if (value & RTC_ENABLE) {
400 exynos4210_rtc_update_freq(s, value);
402 if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
403 /* clock timer */
404 ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
405 ptimer_run(s->ptimer_1Hz, 1);
406 DPRINTF("run clock timer\n");
408 if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
409 /* tick timer */
410 ptimer_stop(s->ptimer);
411 /* clock timer */
412 ptimer_stop(s->ptimer_1Hz);
413 DPRINTF("stop all timers\n");
415 if (value & RTC_ENABLE) {
416 if ((value & TICK_TIMER_ENABLE) >
417 (s->reg_rtccon & TICK_TIMER_ENABLE) &&
418 (s->reg_ticcnt)) {
419 ptimer_set_count(s->ptimer, s->reg_ticcnt);
420 ptimer_run(s->ptimer, 1);
421 DPRINTF("run tick timer\n");
423 if ((value & TICK_TIMER_ENABLE) <
424 (s->reg_rtccon & TICK_TIMER_ENABLE)) {
425 ptimer_stop(s->ptimer);
428 s->reg_rtccon = value;
429 break;
430 case TICCNT:
431 if (value > TICNT_THRESHOLD) {
432 s->reg_ticcnt = value;
433 } else {
434 fprintf(stderr,
435 "[exynos4210.rtc: bad TICNT value %u ]\n",
436 (uint32_t)value);
438 break;
440 case RTCALM:
441 s->reg_rtcalm = value;
442 break;
443 case ALMSEC:
444 s->reg_almsec = (value & 0x7f);
445 break;
446 case ALMMIN:
447 s->reg_almmin = (value & 0x7f);
448 break;
449 case ALMHOUR:
450 s->reg_almhour = (value & 0x3f);
451 break;
452 case ALMDAY:
453 s->reg_almday = (value & 0x3f);
454 break;
455 case ALMMON:
456 s->reg_almmon = (value & 0x1f);
457 break;
458 case ALMYEAR:
459 s->reg_almyear = (value & 0x0fff);
460 break;
462 case BCDSEC:
463 if (s->reg_rtccon & RTC_ENABLE) {
464 s->current_tm.tm_sec = (int)from_bcd((uint8_t)value);
466 break;
467 case BCDMIN:
468 if (s->reg_rtccon & RTC_ENABLE) {
469 s->current_tm.tm_min = (int)from_bcd((uint8_t)value);
471 break;
472 case BCDHOUR:
473 if (s->reg_rtccon & RTC_ENABLE) {
474 s->current_tm.tm_hour = (int)from_bcd((uint8_t)value);
476 break;
477 case BCDDAYWEEK:
478 if (s->reg_rtccon & RTC_ENABLE) {
479 s->current_tm.tm_wday = (int)from_bcd((uint8_t)value);
481 break;
482 case BCDDAY:
483 if (s->reg_rtccon & RTC_ENABLE) {
484 s->current_tm.tm_mday = (int)from_bcd((uint8_t)value);
486 break;
487 case BCDMON:
488 if (s->reg_rtccon & RTC_ENABLE) {
489 s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1;
491 break;
492 case BCDYEAR:
493 if (s->reg_rtccon & RTC_ENABLE) {
494 /* 3 digits */
495 s->current_tm.tm_year = (int)from_bcd((uint8_t)value) +
496 (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100;
498 break;
500 default:
501 fprintf(stderr,
502 "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
503 offset);
504 break;
510 * Set default values to timer fields and registers
512 static void exynos4210_rtc_reset(DeviceState *d)
514 Exynos4210RTCState *s = EXYNOS4210_RTC(d);
516 qemu_get_timedate(&s->current_tm, 0);
518 DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
519 s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
520 s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
522 s->reg_intp = 0;
523 s->reg_rtccon = 0;
524 s->reg_ticcnt = 0;
525 s->reg_rtcalm = 0;
526 s->reg_almsec = 0;
527 s->reg_almmin = 0;
528 s->reg_almhour = 0;
529 s->reg_almday = 0;
530 s->reg_almmon = 0;
531 s->reg_almyear = 0;
533 s->reg_curticcnt = 0;
535 exynos4210_rtc_update_freq(s, s->reg_rtccon);
536 ptimer_stop(s->ptimer);
537 ptimer_stop(s->ptimer_1Hz);
540 static const MemoryRegionOps exynos4210_rtc_ops = {
541 .read = exynos4210_rtc_read,
542 .write = exynos4210_rtc_write,
543 .endianness = DEVICE_NATIVE_ENDIAN,
547 * RTC timer initialization
549 static int exynos4210_rtc_init(SysBusDevice *dev)
551 Exynos4210RTCState *s = EXYNOS4210_RTC(dev);
552 QEMUBH *bh;
554 bh = qemu_bh_new(exynos4210_rtc_tick, s);
555 s->ptimer = ptimer_init(bh);
556 ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
557 exynos4210_rtc_update_freq(s, 0);
559 bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
560 s->ptimer_1Hz = ptimer_init(bh);
561 ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
563 sysbus_init_irq(dev, &s->alm_irq);
564 sysbus_init_irq(dev, &s->tick_irq);
566 memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_rtc_ops, s,
567 "exynos4210-rtc", EXYNOS4210_RTC_REG_MEM_SIZE);
568 sysbus_init_mmio(dev, &s->iomem);
570 return 0;
573 static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
575 DeviceClass *dc = DEVICE_CLASS(klass);
576 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
578 k->init = exynos4210_rtc_init;
579 dc->reset = exynos4210_rtc_reset;
580 dc->vmsd = &vmstate_exynos4210_rtc_state;
583 static const TypeInfo exynos4210_rtc_info = {
584 .name = TYPE_EXYNOS4210_RTC,
585 .parent = TYPE_SYS_BUS_DEVICE,
586 .instance_size = sizeof(Exynos4210RTCState),
587 .class_init = exynos4210_rtc_class_init,
590 static void exynos4210_rtc_register_types(void)
592 type_register_static(&exynos4210_rtc_info);
595 type_init(exynos4210_rtc_register_types)