Statistics
| Branch: | Revision:

root / hw / exynos4210_rtc.c @ 0d09e41a

History | View | Annotate | Download (16.1 kB)

1
/*
2
 * Samsung exynos4210 Real Time Clock
3
 *
4
 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
5
 *  Ogurtsov Oleg <o.ogurtsov@samsung.com>
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 "qemu/timer.h"
35
#include "sysemu/sysemu.h"
36

    
37
#include "hw/arm/exynos4210.h"
38

    
39
#define DEBUG_RTC 0
40

    
41
#if DEBUG_RTC
42
#define DPRINTF(fmt, ...) \
43
        do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
44
                ## __VA_ARGS__); } while (0)
45
#else
46
#define DPRINTF(fmt, ...) do {} while (0)
47
#endif
48

    
49
#define     EXYNOS4210_RTC_REG_MEM_SIZE     0x0100
50

    
51
#define     INTP            0x0030
52
#define     RTCCON          0x0040
53
#define     TICCNT          0x0044
54
#define     RTCALM          0x0050
55
#define     ALMSEC          0x0054
56
#define     ALMMIN          0x0058
57
#define     ALMHOUR         0x005C
58
#define     ALMDAY          0x0060
59
#define     ALMMON          0x0064
60
#define     ALMYEAR         0x0068
61
#define     BCDSEC          0x0070
62
#define     BCDMIN          0x0074
63
#define     BCDHOUR         0x0078
64
#define     BCDDAY          0x007C
65
#define     BCDDAYWEEK      0x0080
66
#define     BCDMON          0x0084
67
#define     BCDYEAR         0x0088
68
#define     CURTICNT        0x0090
69

    
70
#define     TICK_TIMER_ENABLE   0x0100
71
#define     TICNT_THRESHHOLD    2
72

    
73

    
74
#define     RTC_ENABLE          0x0001
75

    
76
#define     INTP_TICK_ENABLE    0x0001
77
#define     INTP_ALM_ENABLE     0x0002
78

    
79
#define     ALARM_INT_ENABLE    0x0040
80

    
81
#define     RTC_BASE_FREQ       32768
82

    
83
typedef struct Exynos4210RTCState {
84
    SysBusDevice busdev;
85
    MemoryRegion iomem;
86

    
87
    /* registers */
88
    uint32_t    reg_intp;
89
    uint32_t    reg_rtccon;
90
    uint32_t    reg_ticcnt;
91
    uint32_t    reg_rtcalm;
92
    uint32_t    reg_almsec;
93
    uint32_t    reg_almmin;
94
    uint32_t    reg_almhour;
95
    uint32_t    reg_almday;
96
    uint32_t    reg_almmon;
97
    uint32_t    reg_almyear;
98
    uint32_t    reg_curticcnt;
99

    
100
    ptimer_state    *ptimer;        /* tick timer */
101
    ptimer_state    *ptimer_1Hz;    /* clock timer */
102
    uint32_t        freq;
103

    
104
    qemu_irq        tick_irq;   /* Time Tick Generator irq */
105
    qemu_irq        alm_irq;    /* alarm irq */
106

    
107
    struct tm   current_tm;     /* current time */
108
} Exynos4210RTCState;
109

    
110
#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
111

    
112
/*** VMState ***/
113
static const VMStateDescription vmstate_exynos4210_rtc_state = {
114
    .name = "exynos4210.rtc",
115
    .version_id = 1,
116
    .minimum_version_id = 1,
117
    .minimum_version_id_old = 1,
118
    .fields = (VMStateField[]) {
119
        VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
120
        VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
121
        VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
122
        VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
123
        VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
124
        VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
125
        VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
126
        VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
127
        VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
128
        VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
129
        VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
130
        VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
131
        VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
132
        VMSTATE_UINT32(freq, Exynos4210RTCState),
133
        VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
134
        VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
135
        VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
136
        VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
137
        VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
138
        VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
139
        VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
140
        VMSTATE_END_OF_LIST()
141
    }
142
};
143

    
144
#define BCD3DIGITS(x) \
145
    ((uint32_t)to_bcd((uint8_t)(x % 100)) + \
146
    ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8))
147

    
148
static void check_alarm_raise(Exynos4210RTCState *s)
149
{
150
    unsigned int alarm_raise = 0;
151
    struct tm stm = s->current_tm;
152

    
153
    if ((s->reg_rtcalm & 0x01) &&
154
        (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) {
155
        alarm_raise = 1;
156
    }
157
    if ((s->reg_rtcalm & 0x02) &&
158
        (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) {
159
        alarm_raise = 1;
160
    }
161
    if ((s->reg_rtcalm & 0x04) &&
162
        (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) {
163
        alarm_raise = 1;
164
    }
165
    if ((s->reg_rtcalm & 0x08) &&
166
        (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) {
167
        alarm_raise = 1;
168
    }
169
    if ((s->reg_rtcalm & 0x10) &&
170
         (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) {
171
        alarm_raise = 1;
172
    }
173
    if ((s->reg_rtcalm & 0x20) &&
174
        (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) {
175
        alarm_raise = 1;
176
    }
177

    
178
    if (alarm_raise) {
179
        DPRINTF("ALARM IRQ\n");
180
        /* set irq status */
181
        s->reg_intp |= INTP_ALM_ENABLE;
182
        qemu_irq_raise(s->alm_irq);
183
    }
184
}
185

    
186
/*
187
 * RTC update frequency
188
 * Parameters:
189
 *     reg_value - current RTCCON register or his new value
190
 */
191
static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
192
                                       uint32_t reg_value)
193
{
194
    uint32_t freq;
195

    
196
    freq = s->freq;
197
    /* set frequncy for time generator */
198
    s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
199

    
200
    if (freq != s->freq) {
201
        ptimer_set_freq(s->ptimer, s->freq);
202
        DPRINTF("freq=%dHz\n", s->freq);
203
    }
204
}
205

    
206
/* month is between 0 and 11. */
207
static int get_days_in_month(int month, int year)
208
{
209
    static const int days_tab[12] = {
210
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
211
    };
212
    int d;
213
    if ((unsigned)month >= 12) {
214
        return 31;
215
    }
216
    d = days_tab[month];
217
    if (month == 1) {
218
        if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
219
            d++;
220
        }
221
    }
222
    return d;
223
}
224

    
225
/* update 'tm' to the next second */
226
static void rtc_next_second(struct tm *tm)
227
{
228
    int days_in_month;
229

    
230
    tm->tm_sec++;
231
    if ((unsigned)tm->tm_sec >= 60) {
232
        tm->tm_sec = 0;
233
        tm->tm_min++;
234
        if ((unsigned)tm->tm_min >= 60) {
235
            tm->tm_min = 0;
236
            tm->tm_hour++;
237
            if ((unsigned)tm->tm_hour >= 24) {
238
                tm->tm_hour = 0;
239
                /* next day */
240
                tm->tm_wday++;
241
                if ((unsigned)tm->tm_wday >= 7) {
242
                    tm->tm_wday = 0;
243
                }
244
                days_in_month = get_days_in_month(tm->tm_mon,
245
                                                  tm->tm_year + 1900);
246
                tm->tm_mday++;
247
                if (tm->tm_mday < 1) {
248
                    tm->tm_mday = 1;
249
                } else if (tm->tm_mday > days_in_month) {
250
                    tm->tm_mday = 1;
251
                    tm->tm_mon++;
252
                    if (tm->tm_mon >= 12) {
253
                        tm->tm_mon = 0;
254
                        tm->tm_year++;
255
                    }
256
                }
257
            }
258
        }
259
    }
260
}
261

    
262
/*
263
 * tick handler
264
 */
265
static void exynos4210_rtc_tick(void *opaque)
266
{
267
    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
268

    
269
    DPRINTF("TICK IRQ\n");
270
    /* set irq status */
271
    s->reg_intp |= INTP_TICK_ENABLE;
272
    /* raise IRQ */
273
    qemu_irq_raise(s->tick_irq);
274

    
275
    /* restart timer */
276
    ptimer_set_count(s->ptimer, s->reg_ticcnt);
277
    ptimer_run(s->ptimer, 1);
278
}
279

    
280
/*
281
 * 1Hz clock handler
282
 */
283
static void exynos4210_rtc_1Hz_tick(void *opaque)
284
{
285
    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
286

    
287
    rtc_next_second(&s->current_tm);
288
    /* DPRINTF("1Hz tick\n"); */
289

    
290
    /* raise IRQ */
291
    if (s->reg_rtcalm & ALARM_INT_ENABLE) {
292
        check_alarm_raise(s);
293
    }
294

    
295
    ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
296
    ptimer_run(s->ptimer_1Hz, 1);
297
}
298

    
299
/*
300
 * RTC Read
301
 */
302
static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset,
303
        unsigned size)
304
{
305
    uint32_t value = 0;
306
    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
307

    
308
    switch (offset) {
309
    case INTP:
310
        value = s->reg_intp;
311
        break;
312
    case RTCCON:
313
        value = s->reg_rtccon;
314
        break;
315
    case TICCNT:
316
        value = s->reg_ticcnt;
317
        break;
318
    case RTCALM:
319
        value = s->reg_rtcalm;
320
        break;
321
    case ALMSEC:
322
        value = s->reg_almsec;
323
        break;
324
    case ALMMIN:
325
        value = s->reg_almmin;
326
        break;
327
    case ALMHOUR:
328
        value = s->reg_almhour;
329
        break;
330
    case ALMDAY:
331
        value = s->reg_almday;
332
        break;
333
    case ALMMON:
334
        value = s->reg_almmon;
335
        break;
336
    case ALMYEAR:
337
        value = s->reg_almyear;
338
        break;
339

    
340
    case BCDSEC:
341
        value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec);
342
        break;
343
    case BCDMIN:
344
        value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min);
345
        break;
346
    case BCDHOUR:
347
        value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour);
348
        break;
349
    case BCDDAYWEEK:
350
        value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday);
351
        break;
352
    case BCDDAY:
353
        value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday);
354
        break;
355
    case BCDMON:
356
        value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1);
357
        break;
358
    case BCDYEAR:
359
        value = BCD3DIGITS(s->current_tm.tm_year);
360
        break;
361

    
362
    case CURTICNT:
363
        s->reg_curticcnt = ptimer_get_count(s->ptimer);
364
        value = s->reg_curticcnt;
365
        break;
366

    
367
    default:
368
        fprintf(stderr,
369
                "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
370
                offset);
371
        break;
372
    }
373
    return value;
374
}
375

    
376
/*
377
 * RTC Write
378
 */
379
static void exynos4210_rtc_write(void *opaque, hwaddr offset,
380
        uint64_t value, unsigned size)
381
{
382
    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
383

    
384
    switch (offset) {
385
    case INTP:
386
        if (value & INTP_ALM_ENABLE) {
387
            qemu_irq_lower(s->alm_irq);
388
            s->reg_intp &= (~INTP_ALM_ENABLE);
389
        }
390
        if (value & INTP_TICK_ENABLE) {
391
            qemu_irq_lower(s->tick_irq);
392
            s->reg_intp &= (~INTP_TICK_ENABLE);
393
        }
394
        break;
395
    case RTCCON:
396
        if (value & RTC_ENABLE) {
397
            exynos4210_rtc_update_freq(s, value);
398
        }
399
        if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
400
            /* clock timer */
401
            ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
402
            ptimer_run(s->ptimer_1Hz, 1);
403
            DPRINTF("run clock timer\n");
404
        }
405
        if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
406
            /* tick timer */
407
            ptimer_stop(s->ptimer);
408
            /* clock timer */
409
            ptimer_stop(s->ptimer_1Hz);
410
            DPRINTF("stop all timers\n");
411
        }
412
        if (value & RTC_ENABLE) {
413
            if ((value & TICK_TIMER_ENABLE) >
414
                (s->reg_rtccon & TICK_TIMER_ENABLE) &&
415
                (s->reg_ticcnt)) {
416
                ptimer_set_count(s->ptimer, s->reg_ticcnt);
417
                ptimer_run(s->ptimer, 1);
418
                DPRINTF("run tick timer\n");
419
            }
420
            if ((value & TICK_TIMER_ENABLE) <
421
                (s->reg_rtccon & TICK_TIMER_ENABLE)) {
422
                ptimer_stop(s->ptimer);
423
            }
424
        }
425
        s->reg_rtccon = value;
426
        break;
427
    case TICCNT:
428
        if (value > TICNT_THRESHHOLD) {
429
            s->reg_ticcnt = value;
430
        } else {
431
            fprintf(stderr,
432
                    "[exynos4210.rtc: bad TICNT value %u ]\n",
433
                    (uint32_t)value);
434
        }
435
        break;
436

    
437
    case RTCALM:
438
        s->reg_rtcalm = value;
439
        break;
440
    case ALMSEC:
441
        s->reg_almsec = (value & 0x7f);
442
        break;
443
    case ALMMIN:
444
        s->reg_almmin = (value & 0x7f);
445
        break;
446
    case ALMHOUR:
447
        s->reg_almhour = (value & 0x3f);
448
        break;
449
    case ALMDAY:
450
        s->reg_almday = (value & 0x3f);
451
        break;
452
    case ALMMON:
453
        s->reg_almmon = (value & 0x1f);
454
        break;
455
    case ALMYEAR:
456
        s->reg_almyear = (value & 0x0fff);
457
        break;
458

    
459
    case BCDSEC:
460
        if (s->reg_rtccon & RTC_ENABLE) {
461
            s->current_tm.tm_sec = (int)from_bcd((uint8_t)value);
462
        }
463
        break;
464
    case BCDMIN:
465
        if (s->reg_rtccon & RTC_ENABLE) {
466
            s->current_tm.tm_min = (int)from_bcd((uint8_t)value);
467
        }
468
        break;
469
    case BCDHOUR:
470
        if (s->reg_rtccon & RTC_ENABLE) {
471
            s->current_tm.tm_hour = (int)from_bcd((uint8_t)value);
472
        }
473
        break;
474
    case BCDDAYWEEK:
475
        if (s->reg_rtccon & RTC_ENABLE) {
476
            s->current_tm.tm_wday = (int)from_bcd((uint8_t)value);
477
        }
478
        break;
479
    case BCDDAY:
480
        if (s->reg_rtccon & RTC_ENABLE) {
481
            s->current_tm.tm_mday = (int)from_bcd((uint8_t)value);
482
        }
483
        break;
484
    case BCDMON:
485
        if (s->reg_rtccon & RTC_ENABLE) {
486
            s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1;
487
        }
488
        break;
489
    case BCDYEAR:
490
        if (s->reg_rtccon & RTC_ENABLE) {
491
            /* 3 digits */
492
            s->current_tm.tm_year = (int)from_bcd((uint8_t)value) +
493
                    (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100;
494
        }
495
        break;
496

    
497
    default:
498
        fprintf(stderr,
499
                "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
500
                offset);
501
        break;
502

    
503
    }
504
}
505

    
506
/*
507
 * Set default values to timer fields and registers
508
 */
509
static void exynos4210_rtc_reset(DeviceState *d)
510
{
511
    Exynos4210RTCState *s = (Exynos4210RTCState *)d;
512

    
513
    qemu_get_timedate(&s->current_tm, 0);
514

    
515
    DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
516
            s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
517
            s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
518

    
519
    s->reg_intp = 0;
520
    s->reg_rtccon = 0;
521
    s->reg_ticcnt = 0;
522
    s->reg_rtcalm = 0;
523
    s->reg_almsec = 0;
524
    s->reg_almmin = 0;
525
    s->reg_almhour = 0;
526
    s->reg_almday = 0;
527
    s->reg_almmon = 0;
528
    s->reg_almyear = 0;
529

    
530
    s->reg_curticcnt = 0;
531

    
532
    exynos4210_rtc_update_freq(s, s->reg_rtccon);
533
    ptimer_stop(s->ptimer);
534
    ptimer_stop(s->ptimer_1Hz);
535
}
536

    
537
static const MemoryRegionOps exynos4210_rtc_ops = {
538
    .read = exynos4210_rtc_read,
539
    .write = exynos4210_rtc_write,
540
    .endianness = DEVICE_NATIVE_ENDIAN,
541
};
542

    
543
/*
544
 * RTC timer initialization
545
 */
546
static int exynos4210_rtc_init(SysBusDevice *dev)
547
{
548
    Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev);
549
    QEMUBH *bh;
550

    
551
    bh = qemu_bh_new(exynos4210_rtc_tick, s);
552
    s->ptimer = ptimer_init(bh);
553
    ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
554
    exynos4210_rtc_update_freq(s, 0);
555

    
556
    bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
557
    s->ptimer_1Hz = ptimer_init(bh);
558
    ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
559

    
560
    sysbus_init_irq(dev, &s->alm_irq);
561
    sysbus_init_irq(dev, &s->tick_irq);
562

    
563
    memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc",
564
            EXYNOS4210_RTC_REG_MEM_SIZE);
565
    sysbus_init_mmio(dev, &s->iomem);
566

    
567
    return 0;
568
}
569

    
570
static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
571
{
572
    DeviceClass *dc = DEVICE_CLASS(klass);
573
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
574

    
575
    k->init = exynos4210_rtc_init;
576
    dc->reset = exynos4210_rtc_reset;
577
    dc->vmsd = &vmstate_exynos4210_rtc_state;
578
}
579

    
580
static const TypeInfo exynos4210_rtc_info = {
581
    .name          = "exynos4210.rtc",
582
    .parent        = TYPE_SYS_BUS_DEVICE,
583
    .instance_size = sizeof(Exynos4210RTCState),
584
    .class_init    = exynos4210_rtc_class_init,
585
};
586

    
587
static void exynos4210_rtc_register_types(void)
588
{
589
    type_register_static(&exynos4210_rtc_info);
590
}
591

    
592
type_init(exynos4210_rtc_register_types)