diff mbox

Exynos4: added RTC device

Message ID 1340610940-923-2-git-send-email-o.ogurtsov@samsung.com
State New
Headers show

Commit Message

Oleg Ogurtsov June 25, 2012, 7:55 a.m. UTC
Signed-off-by: Oleg Ogurtsov <o.ogurtsov@samsung.com>
---
 hw/arm/Makefile.objs |    1 +
 hw/exynos4210.c      |    8 +
 hw/exynos4210_rtc.c  |  607 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 616 insertions(+), 0 deletions(-)
 create mode 100644 hw/exynos4210_rtc.c

Comments

Evgeny Voevodin June 25, 2012, 8:52 a.m. UTC | #1
On 25.06.2012 11:55, Oleg Ogurtsov wrote:
> Signed-off-by: Oleg Ogurtsov<o.ogurtsov@samsung.com>
> ---
>   hw/arm/Makefile.objs |    1 +
>   hw/exynos4210.c      |    8 +
>   hw/exynos4210_rtc.c  |  607 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 616 insertions(+), 0 deletions(-)
>   create mode 100644 hw/exynos4210_rtc.c
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 88ff47d..0fdb832 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -11,6 +11,7 @@ obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
>   obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
>   obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
>   obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
> +obj-y += exynos4210_rtc.o
>   obj-y += arm_l2x0.o
>   obj-y += arm_mptimer.o a15mpcore.o
>   obj-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
> diff --git a/hw/exynos4210.c b/hw/exynos4210.c
> index 9c20b3f..6e105e0 100644
> --- a/hw/exynos4210.c
> +++ b/hw/exynos4210.c
> @@ -33,6 +33,9 @@
>   /* PWM */
>   #define EXYNOS4210_PWM_BASE_ADDR       0x139D0000
>
> +/* RTC */
> +#define EXYNOS4210_RTC_BASE_ADDR       0x10070000
> +
>   /* MCT */
>   #define EXYNOS4210_MCT_BASE_ADDR       0x10050000
>
> @@ -258,6 +261,11 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
>                             s->irq_table[exynos4210_get_irq(22, 3)],
>                             s->irq_table[exynos4210_get_irq(22, 4)],
>                             NULL);
> +    /* RTC */
> +    sysbus_create_varargs("exynos4210.rtc", EXYNOS4210_RTC_BASE_ADDR,
> +                          s->irq_table[exynos4210_get_irq(23, 0)],
> +                          s->irq_table[exynos4210_get_irq(23, 1)],
> +                          NULL);
>
>       /* Multi Core Timer */
>       dev = qdev_create(NULL, "exynos4210.mct");
> diff --git a/hw/exynos4210_rtc.c b/hw/exynos4210_rtc.c
> new file mode 100644
> index 0000000..2ac6301
> --- /dev/null
> +++ b/hw/exynos4210_rtc.c
> @@ -0,0 +1,607 @@
> +/*
> + * Samsung exynos4210 Real Time Clock
> + *
> + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> + *  Ogurtsov Oleg<o.ogurtsov@samsung.com>
> + *
> + *  This program is free software; you can redistribute it and/or modify it
> + *  under the terms of the GNU General Public License as published by the
> + *  Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful, but WITHOUT
> + *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
> + *  for more details.
> + *
> + *  You should have received a copy of the GNU General Public License along
> + *  with this program; if not, see<http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +/* Description:
> + * Register RTCCON:
> + *  CLKSEL Bit[1] not used
> + *  CLKOUTEN Bit[9] not used
> + */
> +
> +#include "sysbus.h"
> +#include "qemu-timer.h"
> +#include "qemu-common.h"
> +#include "ptimer.h"
> +
> +#include "hw.h"
> +#include "qemu-timer.h"
> +#include "sysemu.h"
> +
> +#include "exynos4210.h"
> +
> +#define DEBUG_RTC 0
> +
> +#if DEBUG_RTC
> +#define DPRINTF(fmt, ...) \
> +        do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
> +                ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...) do {} while (0)
> +#endif
> +
> +#define     EXYNOS4210_RTC_REG_MEM_SIZE     0x0100
> +
> +#define     INTP            0x0030
> +#define     RTCCON          0x0040
> +#define     TICCNT          0x0044
> +#define     RTCALM          0x0050
> +#define     ALMSEC          0x0054
> +#define     ALMMIN          0x0058
> +#define     ALMHOUR         0x005C
> +#define     ALMDAY          0x0060
> +#define     ALMMON          0x0064
> +#define     ALMYEAR         0x0068
> +#define     BCDSEC          0x0070
> +#define     BDCMIN          0x0074
> +#define     BCDHOUR         0x0078
> +#define     BCDDAY          0x007C
> +#define     BCDDAYWEEK      0x0080
> +#define     BCDMON          0x0084
> +#define     BCDYEAR         0x0088
> +#define     CURTICNT        0x0090
> +
> +#define     TICK_TIMER_ENABLE   0x0100
> +#define     TICNT_THRESHHOLD    2
> +
> +
> +#define     RTC_ENABLE          0x0001
> +
> +#define     INTP_TICK_ENABLE    0x0001
> +#define     INTP_ALM_ENABLE     0x0002
> +
> +#define     ALARM_INT_ENABLE    0x0040
> +
> +#define     RTC_BASE_FREQ       32768
> +
> +typedef struct Exynos4210RTCState {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +
> +    /* registers */
> +    uint32_t    reg_intp;
> +    uint32_t    reg_rtccon;
> +    uint32_t    reg_ticcnt;
> +    uint32_t    reg_rtcalm;
> +    uint32_t    reg_almsec;
> +    uint32_t    reg_almmin;
> +    uint32_t    reg_almhour;
> +    uint32_t    reg_almday;
> +    uint32_t    reg_almmon;
> +    uint32_t    reg_almyear;
> +    uint32_t    reg_curticcnt;
> +
> +    ptimer_state    *ptimer;        /* tick timer */
> +    ptimer_state    *ptimer_1Hz;    /* clock timer */
> +    uint32_t        freq;
> +
> +    qemu_irq        tick_irq;   /* Time Tick Generator irq */
> +    qemu_irq        alm_irq;    /* alarm irq */
> +
> +    struct tm   current_tm;     /* current time */
> +} Exynos4210RTCState;
> +
> +#define TICCKSEL(value) ((value&  (0x0F<<  4))>>  4)
> +
> +/*** VMState ***/
> +static const VMStateDescription vmstate_exynos4210_rtc_state = {
> +    .name = "exynos4210.rtc",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
> +        VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
> +        VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
> +        VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
> +        VMSTATE_UINT32(freq, Exynos4210RTCState),
> +        VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
> +        VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
> +        VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
> +        VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
> +        VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
> +        VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
> +        VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static inline int rtc_to_bcd(int a, int count)
> +{
> +    int ret = (((a % 100) / 10)<<  4) | (a % 10);
> +    if (count == 3) {
> +        ret |= ((a % 100)<<  8);
> +    }
> +    return ret;
> +}
> +
> +static inline int rtc_from_bcd(uint32_t a, int count)
> +{
> +    int ret = (((a>>  4)&  0x0f) * 10) + (a&  0x0f);
> +    if (count == 3) {
> +        ret += (((a>>  8)&  0x0f) * 100);
> +    }
> +    return ret;
> +}
> +
> +static void check_alarm_raise(Exynos4210RTCState *s)
> +{
> +    unsigned int alarm_raise = 0;
> +    struct tm stm = s->current_tm;
> +
> +    if ((s->reg_rtcalm&  0x01)&&
> +        (rtc_to_bcd(stm.tm_sec, 2) == s->reg_almsec)) {
> +        alarm_raise = 1;
> +    }
> +    if ((s->reg_rtcalm&  0x02)&&
> +        (rtc_to_bcd(stm.tm_min, 2) == s->reg_almmin)) {
> +        alarm_raise = 1;
> +    }
> +    if ((s->reg_rtcalm&  0x04)&&
> +        (rtc_to_bcd(stm.tm_hour, 2) == s->reg_almhour)) {
> +        alarm_raise = 1;
> +    }
> +    if ((s->reg_rtcalm&  0x08)&&
> +        (rtc_to_bcd(stm.tm_mday, 2) == s->reg_almday)) {
> +        alarm_raise = 1;
> +    }
> +    if ((s->reg_rtcalm&  0x10)&&
> +         (rtc_to_bcd(stm.tm_mon, 2) == s->reg_almmon)) {
> +        alarm_raise = 1;
> +    }
> +    if ((s->reg_rtcalm&  0x20)&&
> +        (rtc_to_bcd(stm.tm_year, 3) == s->reg_almyear)) {
> +        alarm_raise = 1;
> +    }
> +
> +    if (alarm_raise) {
> +        DPRINTF("ALARM IRQ\n");
> +        /* set irq status */
> +        s->reg_intp |= INTP_ALM_ENABLE;
> +        qemu_irq_raise(s->alm_irq);
> +    }
> +}
> +
> +/*
> + * RTC update frequency
> + * Parameters:
> + *     reg_value - current RTCCON register or his new value
> + */
> +static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
> +                                       uint32_t reg_value)
> +{
> +    uint32_t freq;
> +
> +    freq = s->freq;
> +    /* set frequncy for time generator */
> +    s->freq = RTC_BASE_FREQ / (1<<  TICCKSEL(reg_value));
> +
> +    if (freq != s->freq) {
> +        ptimer_set_freq(s->ptimer, s->freq);
> +        DPRINTF("freq=%dHz\n", s->freq);
> +    }
> +}
> +
> +/* month is between 0 and 11. */
> +static int get_days_in_month(int month, int year)
> +{
> +    static const int days_tab[12] = {
> +        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
> +    };
> +    int d;
> +    if ((unsigned)month>= 12) {
> +        return 31;
> +    }
> +    d = days_tab[month];
> +    if (month == 1) {
> +        if ((year % 4) == 0&&  ((year % 100) != 0 || (year % 400) == 0)) {
> +            d++;
> +        }
> +    }
> +    return d;
> +}
> +
> +/* update 'tm' to the next second */
> +static void rtc_next_second(struct tm *tm)
> +{
> +    int days_in_month;
> +
> +    tm->tm_sec++;
> +    if ((unsigned)tm->tm_sec>= 60) {
> +        tm->tm_sec = 0;
> +        tm->tm_min++;
> +        if ((unsigned)tm->tm_min>= 60) {
> +            tm->tm_min = 0;
> +            tm->tm_hour++;
> +            if ((unsigned)tm->tm_hour>= 24) {
> +                tm->tm_hour = 0;
> +                /* next day */
> +                tm->tm_wday++;
> +                if ((unsigned)tm->tm_wday>= 7) {
> +                    tm->tm_wday = 0;
> +                }
> +                days_in_month = get_days_in_month(tm->tm_mon,
> +                                                  tm->tm_year + 1900);
> +                tm->tm_mday++;
> +                if (tm->tm_mday<  1) {
> +                    tm->tm_mday = 1;
> +                } else if (tm->tm_mday>  days_in_month) {
> +                    tm->tm_mday = 1;
> +                    tm->tm_mon++;
> +                    if (tm->tm_mon>= 12) {
> +                        tm->tm_mon = 0;
> +                        tm->tm_year++;
> +                    }
> +                }
> +            }
> +        }
> +    }
> +}
> +
> +/*
> + * tick handler
> + */
> +static void exynos4210_rtc_tick(void *opaque)
> +{
> +    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
> +
> +    DPRINTF("TICK IRQ\n");
> +    /* set irq status */
> +    s->reg_intp |= INTP_TICK_ENABLE;
> +    /* raise IRQ */
> +    qemu_irq_raise(s->tick_irq);
> +
> +    /* restart timer */
> +    ptimer_set_count(s->ptimer, s->reg_ticcnt);
> +    ptimer_run(s->ptimer, 1);
> +}
> +
> +/*
> + * 1Hz clock handler
> + */
> +static void exynos4210_rtc_1Hz_tick(void *opaque)
> +{
> +    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
> +
> +    rtc_next_second(&s->current_tm);
> +    /* DPRINTF("1Hz tick\n"); */
> +
> +    /* raise IRQ */
> +    if (s->reg_rtcalm&  ALARM_INT_ENABLE) {
> +        check_alarm_raise(s);
> +    }
> +
> +    ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
> +    ptimer_run(s->ptimer_1Hz, 1);
> +}
> +
> +/*
> + * RTC Read
> + */
> +static uint64_t exynos4210_rtc_read(void *opaque, target_phys_addr_t offset,
> +        unsigned size)
> +{
> +    uint32_t value = 0;
> +    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
> +
> +    switch (offset) {
> +    case INTP:
> +        value = s->reg_intp;
> +        break;
> +    case RTCCON:
> +        value = s->reg_rtccon;
> +        break;
> +    case TICCNT:
> +        value = s->reg_ticcnt;
> +        break;
> +    case RTCALM:
> +        value = s->reg_rtcalm;
> +        break;
> +    case ALMSEC:
> +        value = s->reg_almsec;
> +        break;
> +    case ALMMIN:
> +        value = s->reg_almmin;
> +        break;
> +    case ALMHOUR:
> +        value = s->reg_almhour;
> +        break;
> +    case ALMDAY:
> +        value = s->reg_almday;
> +        break;
> +    case ALMMON:
> +        value = s->reg_almmon;
> +        break;
> +    case ALMYEAR:
> +        value = s->reg_almyear;
> +        break;
> +
> +    case BCDSEC:
> +        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_sec, 2);
> +        break;
> +    case BDCMIN:
> +        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_min, 2);
> +        break;
> +    case BCDHOUR:
> +        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_hour, 2);
> +        break;
> +    case BCDDAYWEEK:
> +        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_wday, 2);
> +        break;
> +    case BCDDAY:
> +        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_mday, 2);
> +        break;
> +    case BCDMON:
> +        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_mon + 1, 2);
> +        break;
> +    case BCDYEAR:
> +        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_year, 3);
> +        break;
> +
> +    case CURTICNT:
> +        s->reg_curticcnt = ptimer_get_count(s->ptimer);
> +        value = s->reg_curticcnt;
> +        break;
> +
> +    default:
> +        fprintf(stderr,
> +                "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
> +                offset);
> +        break;
> +    }
> +    return value;
> +}
> +
> +/*
> + * RTC Write
> + */
> +static void exynos4210_rtc_write(void *opaque, target_phys_addr_t offset,
> +        uint64_t value, unsigned size)
> +{
> +    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
> +
> +    switch (offset) {
> +    case INTP:
> +        if (value&  INTP_ALM_ENABLE) {
> +            qemu_irq_lower(s->alm_irq);
> +            s->reg_intp&= (~INTP_ALM_ENABLE);
> +        }
> +        if (value&  INTP_TICK_ENABLE) {
> +            qemu_irq_lower(s->tick_irq);
> +            s->reg_intp&= (~INTP_TICK_ENABLE);
> +        }
> +        break;
> +    case RTCCON:
> +        if (value&  RTC_ENABLE) {
> +            exynos4210_rtc_update_freq(s, value);
> +        }
> +        if ((value&  RTC_ENABLE)>  (s->reg_rtccon&  RTC_ENABLE)) {
> +            /* clock timer */
> +            ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
> +            ptimer_run(s->ptimer_1Hz, 1);
> +            DPRINTF("run clock timer\n");
> +        }
> +        if ((value&  RTC_ENABLE)<  (s->reg_rtccon&  RTC_ENABLE)) {
> +            /* tick timer */
> +            ptimer_stop(s->ptimer);
> +            /* clock timer */
> +            ptimer_stop(s->ptimer_1Hz);
> +            DPRINTF("stop all timers\n");
> +        }
> +        if (value&  RTC_ENABLE) {
> +            if ((value&  TICK_TIMER_ENABLE)>
> +                (s->reg_rtccon&  TICK_TIMER_ENABLE)&&
> +                (s->reg_ticcnt)) {
> +                ptimer_set_count(s->ptimer, s->reg_ticcnt);
> +                ptimer_run(s->ptimer, 1);
> +                DPRINTF("run tick timer\n");
> +            }
> +            if ((value&  TICK_TIMER_ENABLE)<
> +                (s->reg_rtccon&  TICK_TIMER_ENABLE)) {
> +                ptimer_stop(s->ptimer);
> +            }
> +        }
> +        s->reg_rtccon = value;
> +        break;
> +    case TICCNT:
> +        if (value>  TICNT_THRESHHOLD) {
> +            s->reg_ticcnt = value;
> +        } else {
> +            fprintf(stderr,
> +                    "[exynos4210.rtc: bad TICNT value %u ]\n",
> +                    (uint32_t)value);
> +        }
> +        break;
> +
> +    case RTCALM:
> +        s->reg_rtcalm = value;
> +        break;
> +    case ALMSEC:
> +        s->reg_almsec = (value&  0x7f);
> +        break;
> +    case ALMMIN:
> +        s->reg_almmin = (value&  0x7f);
> +        break;
> +    case ALMHOUR:
> +        s->reg_almhour = (value&  0x3f);
> +        break;
> +    case ALMDAY:
> +        s->reg_almday = (value&  0x3f);
> +        break;
> +    case ALMMON:
> +        s->reg_almmon = (value&  0x1f);
> +        break;
> +    case ALMYEAR:
> +        s->reg_almyear = (value&  0x0fff);
> +        break;
> +
> +    case BCDSEC:
> +        if (s->reg_rtccon&  RTC_ENABLE) {
> +            s->current_tm.tm_sec = rtc_from_bcd(value, 2);
> +        }
> +        break;
> +    case BDCMIN:
> +        if (s->reg_rtccon&  RTC_ENABLE) {
> +            s->current_tm.tm_min = rtc_from_bcd(value, 2);
> +        }
> +        break;
> +    case BCDHOUR:
> +        if (s->reg_rtccon&  RTC_ENABLE) {
> +            s->current_tm.tm_hour = rtc_from_bcd(value, 2);
> +        }
> +        break;
> +    case BCDDAYWEEK:
> +        if (s->reg_rtccon&  RTC_ENABLE) {
> +            s->current_tm.tm_wday = rtc_from_bcd(value, 2);
> +        }
> +        break;
> +    case BCDDAY:
> +        if (s->reg_rtccon&  RTC_ENABLE) {
> +            s->current_tm.tm_mday = rtc_from_bcd(value, 2);
> +        }
> +        break;
> +    case BCDMON:
> +        if (s->reg_rtccon&  RTC_ENABLE) {
> +            s->current_tm.tm_mon = rtc_from_bcd(value, 2) - 1;
> +        }
> +        break;
> +    case BCDYEAR:
> +        if (s->reg_rtccon&  RTC_ENABLE) {
> +            s->current_tm.tm_year = rtc_from_bcd(value, 3);
> +        }
> +        break;
> +
> +    default:
> +        fprintf(stderr,
> +                "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
> +                offset);
> +        break;
> +
> +    }
> +}
> +
> +/*
> + * Set default values to timer fields and registers
> + */
> +static void exynos4210_rtc_reset(DeviceState *d)
> +{
> +    Exynos4210RTCState *s = (Exynos4210RTCState *)d;
> +
> +    struct tm tm;
> +
> +    qemu_get_timedate(&tm, 0);
> +    s->current_tm = tm;
> +
> +    DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
> +            s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
> +            s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
> +
> +    s->reg_intp = 0;
> +    s->reg_rtccon = 0;
> +    s->reg_ticcnt = 0;
> +    s->reg_rtcalm = 0;
> +    s->reg_almsec = 0;
> +    s->reg_almmin = 0;
> +    s->reg_almhour = 0;
> +    s->reg_almday = 0;
> +    s->reg_almmon = 0;
> +    s->reg_almyear = 0;
> +
> +    s->reg_curticcnt = 0;
> +
> +    exynos4210_rtc_update_freq(s, s->reg_rtccon);
> +    ptimer_stop(s->ptimer);
> +    ptimer_stop(s->ptimer_1Hz);
> +}
> +
> +static const MemoryRegionOps exynos4210_rtc_ops = {
> +    .read = exynos4210_rtc_read,
> +    .write = exynos4210_rtc_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +/*
> + * RTC timer initialization
> + */
> +static int exynos4210_rtc_init(SysBusDevice *dev)
> +{
> +    Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev);
> +    QEMUBH *bh;
> +
> +    bh = qemu_bh_new(exynos4210_rtc_tick, s);
> +    s->ptimer = ptimer_init(bh);
> +    ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
> +    exynos4210_rtc_update_freq(s, 0);
> +
> +    bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
> +    s->ptimer_1Hz = ptimer_init(bh);
> +    ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
> +
> +    sysbus_init_irq(dev,&s->alm_irq);
> +    sysbus_init_irq(dev,&s->tick_irq);
> +
> +    memory_region_init_io(&s->iomem,&exynos4210_rtc_ops, s, "exynos4210-rtc",
> +            EXYNOS4210_RTC_REG_MEM_SIZE);
> +    sysbus_init_mmio(dev,&s->iomem);
> +
> +    return 0;
> +}
> +
> +static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = exynos4210_rtc_init;
> +    dc->reset = exynos4210_rtc_reset;
> +    dc->vmsd =&vmstate_exynos4210_rtc_state;
> +}
> +
> +static const TypeInfo exynos4210_rtc_info = {
> +    .name          = "exynos4210.rtc",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(Exynos4210RTCState),
> +    .class_init    = exynos4210_rtc_class_init,
> +};
> +
> +static void exynos4210_rtc_register_types(void)
> +{
> +    type_register_static(&exynos4210_rtc_info);
> +}
> +
> +type_init(exynos4210_rtc_register_types)

Reviewed-by: Evgeny Voevodin <e.voevodin@samsung.com>
Andreas Färber June 25, 2012, 9:24 a.m. UTC | #2
Am 25.06.2012 09:55, schrieb Oleg Ogurtsov:
> Signed-off-by: Oleg Ogurtsov <o.ogurtsov@samsung.com>
> ---
>  hw/arm/Makefile.objs |    1 +
>  hw/exynos4210.c      |    8 +
>  hw/exynos4210_rtc.c  |  607 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 616 insertions(+), 0 deletions(-)
>  create mode 100644 hw/exynos4210_rtc.c

This RTC like many other Exynos devices has no dependency on the CPU. I
have a patch in preparation that moves such devices from
hw/arm/Makefile.objs to hw/Makefile.objs.
I don't object to this patch, not even minor style nits spotted,
compliment, but if you have to respin for some reason, it would be nice
if you could consider that improvement.

One thing I noted though, is it time to share the static BCD conversion
helpers used in the various RTCs? (bcd.c? qemu-common.h?) Or are they
slightly different?

Regards,
Andreas
Peter Maydell June 25, 2012, 9:45 a.m. UTC | #3
On 25 June 2012 08:55, Oleg Ogurtsov <o.ogurtsov@samsung.com> wrote:
> Subject:  [PATCH] Exynos4: added RTC device

"add", please.

>
> Signed-off-by: Oleg Ogurtsov <o.ogurtsov@samsung.com>

(You don't need to send a cover letter for a single patch, by the way.)

> +#define     BCDSEC          0x0070
> +#define     BDCMIN          0x0074

typo, should be BCDMIN I assume.

> +#define     BCDHOUR         0x0078
> +#define     BCDDAY          0x007C
> +#define     BCDDAYWEEK      0x0080
> +#define     BCDMON          0x0084
> +#define     BCDYEAR         0x0088

> +/*
> + * 1Hz clock handler
> + */
> +static void exynos4210_rtc_1Hz_tick(void *opaque)
> +{
> +    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
> +
> +    rtc_next_second(&s->current_tm);
> +    /* DPRINTF("1Hz tick\n"); */
> +
> +    /* raise IRQ */
> +    if (s->reg_rtcalm & ALARM_INT_ENABLE) {
> +        check_alarm_raise(s);
> +    }
> +
> +    ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
> +    ptimer_run(s->ptimer_1Hz, 1);
> +}

You could I think structure this so that instead of
running the timer every second you only have it go
off at the alarm time (you then recalculate the right
values etc if the guest reads/writes the registers).
That would be more complicated though and I guess one
timer fire every second isn't a big deal. We seem to
do it this way in other RTCs too, so this code is OK.

-- PMM
Evgeny Voevodin June 25, 2012, 11:46 a.m. UTC | #4
On 25.06.2012 13:24, Andreas Färber wrote:
> Am 25.06.2012 09:55, schrieb Oleg Ogurtsov:
>> Signed-off-by: Oleg Ogurtsov<o.ogurtsov@samsung.com>
>> ---
>>   hw/arm/Makefile.objs |    1 +
>>   hw/exynos4210.c      |    8 +
>>   hw/exynos4210_rtc.c  |  607 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 616 insertions(+), 0 deletions(-)
>>   create mode 100644 hw/exynos4210_rtc.c
> This RTC like many other Exynos devices has no dependency on the CPU. I
> have a patch in preparation that moves such devices from
> hw/arm/Makefile.objs to hw/Makefile.objs.
> I don't object to this patch, not even minor style nits spotted,
> compliment, but if you have to respin for some reason, it would be nice
> if you could consider that improvement.

These devices are SOC specific and this SOC is based on ARM only.
Do we really need to move them?
Andreas Färber June 25, 2012, noon UTC | #5
Am 25.06.2012 13:46, schrieb Evgeny Voevodin:
> On 25.06.2012 13:24, Andreas Färber wrote:
>> Am 25.06.2012 09:55, schrieb Oleg Ogurtsov:
>>> Signed-off-by: Oleg Ogurtsov<o.ogurtsov@samsung.com>
>>> ---
>>>   hw/arm/Makefile.objs |    1 +
>>>   hw/exynos4210.c      |    8 +
>>>   hw/exynos4210_rtc.c  |  607
>>> ++++++++++++++++++++++++++++++++++++++++++++++++++
>>>   3 files changed, 616 insertions(+), 0 deletions(-)
>>>   create mode 100644 hw/exynos4210_rtc.c
>> This RTC like many other Exynos devices has no dependency on the CPU. I
>> have a patch in preparation that moves such devices from
>> hw/arm/Makefile.objs to hw/Makefile.objs.
>> I don't object to this patch, not even minor style nits spotted,
>> compliment, but if you have to respin for some reason, it would be nice
>> if you could consider that improvement.
> 
> These devices are SOC specific and this SOC is based on ARM only.
> Do we really need to move them?

For one, they do not need to be rebuilt when cpu.h changes and they
should get the usual device poisoning for proper modeling.
For another, someone on IRC started work on an armeb-softmmu, for which
we would probably not want to compile in the Exynos devices. Or if we
do, we certainly don't want to compile everything twice (cf. xilinx).

If devices are ARM-specific and need access to the CPU (e.g., machines,
PICs) then according to Paolo they should be placed in hw/arm/ with the
new scheme. I'm trying to stay away from moving other people's files
around, but the Makefile changes are pretty non-intrusive and can go
through arm-devs.next.

Cheers,
Andreas
Evgeny Voevodin June 26, 2012, 3:18 a.m. UTC | #6
On 25.06.2012 16:00, Andreas Färber wrote:
> Am 25.06.2012 13:46, schrieb Evgeny Voevodin:
>> On 25.06.2012 13:24, Andreas Färber wrote:
>>> Am 25.06.2012 09:55, schrieb Oleg Ogurtsov:
>>>> Signed-off-by: Oleg Ogurtsov<o.ogurtsov@samsung.com>
>>>> ---
>>>>    hw/arm/Makefile.objs |    1 +
>>>>    hw/exynos4210.c      |    8 +
>>>>    hw/exynos4210_rtc.c  |  607
>>>> ++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    3 files changed, 616 insertions(+), 0 deletions(-)
>>>>    create mode 100644 hw/exynos4210_rtc.c
>>> This RTC like many other Exynos devices has no dependency on the CPU. I
>>> have a patch in preparation that moves such devices from
>>> hw/arm/Makefile.objs to hw/Makefile.objs.
>>> I don't object to this patch, not even minor style nits spotted,
>>> compliment, but if you have to respin for some reason, it would be nice
>>> if you could consider that improvement.
>> These devices are SOC specific and this SOC is based on ARM only.
>> Do we really need to move them?
> For one, they do not need to be rebuilt when cpu.h changes and they
> should get the usual device poisoning for proper modeling.
> For another, someone on IRC started work on an armeb-softmmu, for which
> we would probably not want to compile in the Exynos devices. Or if we
> do, we certainly don't want to compile everything twice (cf. xilinx).
>
> If devices are ARM-specific and need access to the CPU (e.g., machines,
> PICs) then according to Paolo they should be placed in hw/arm/ with the
> new scheme. I'm trying to stay away from moving other people's files
> around, but the Makefile changes are pretty non-intrusive and can go
> through arm-devs.next.
>
> Cheers,
> Andreas
>

Oh, I see. Should we place this device to hw/Makefile.objs in v2?
Andreas Färber June 29, 2012, 12:26 p.m. UTC | #7
Am 26.06.2012 05:18, schrieb Evgeny Voevodin:
> On 25.06.2012 16:00, Andreas Färber wrote:
>> Am 25.06.2012 13:46, schrieb Evgeny Voevodin:
>>> On 25.06.2012 13:24, Andreas Färber wrote:
>>>> Am 25.06.2012 09:55, schrieb Oleg Ogurtsov:
>>>>> Signed-off-by: Oleg Ogurtsov<o.ogurtsov@samsung.com>
>>>>> ---
>>>>>    hw/arm/Makefile.objs |    1 +
>>>>>    hw/exynos4210.c      |    8 +
>>>>>    hw/exynos4210_rtc.c  |  607
>>>>> ++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>    3 files changed, 616 insertions(+), 0 deletions(-)
>>>>>    create mode 100644 hw/exynos4210_rtc.c
>>>> This RTC like many other Exynos devices has no dependency on the CPU. I
>>>> have a patch in preparation that moves such devices from
>>>> hw/arm/Makefile.objs to hw/Makefile.objs.
>>>> I don't object to this patch, not even minor style nits spotted,
>>>> compliment, but if you have to respin for some reason, it would be nice
>>>> if you could consider that improvement.
>>> These devices are SOC specific and this SOC is based on ARM only.
>>> Do we really need to move them?
>> For one, they do not need to be rebuilt when cpu.h changes and they
>> should get the usual device poisoning for proper modeling.
>> For another, someone on IRC started work on an armeb-softmmu, for which
>> we would probably not want to compile in the Exynos devices. Or if we
>> do, we certainly don't want to compile everything twice (cf. xilinx).
>>
>> If devices are ARM-specific and need access to the CPU (e.g., machines,
>> PICs) then according to Paolo they should be placed in hw/arm/ with the
>> new scheme. I'm trying to stay away from moving other people's files
>> around, but the Makefile changes are pretty non-intrusive and can go
>> through arm-devs.next.
>>
>> Cheers,
>> Andreas
>>
> 
> Oh, I see. Should we place this device to hw/Makefile.objs in v2?

That would've been nice, but I'll do it as a follow-up now.

Regards,
Andreas
Peter Maydell June 29, 2012, 1:06 p.m. UTC | #8
On 29 June 2012 13:26, Andreas Färber <afaerber@suse.de> wrote:
> Am 26.06.2012 05:18, schrieb Evgeny Voevodin:
>> Oh, I see. Should we place this device to hw/Makefile.objs in v2?
>
> That would've been nice, but I'll do it as a follow-up now.

It's not clear to me that we actually have consensus on what
goes in hw/arm/ and what doesn't. I'd like to (a) have a clear
and agreed layout of what devices live where and (b) have all
the ARM devices moved from one makefile to another in one lump,
which is why I didn't want just this RTC device in hw/Makefile.objs.

-- PMM
Andreas Färber June 29, 2012, 1:20 p.m. UTC | #9
Am 29.06.2012 15:06, schrieb Peter Maydell:
> On 29 June 2012 13:26, Andreas Färber <afaerber@suse.de> wrote:
>> Am 26.06.2012 05:18, schrieb Evgeny Voevodin:
>>> Oh, I see. Should we place this device to hw/Makefile.objs in v2?
>>
>> That would've been nice, but I'll do it as a follow-up now.
> 
> It's not clear to me that we actually have consensus on what
> goes in hw/arm/ and what doesn't. I'd like to (a) have a clear
> and agreed layout of what devices live where and (b) have all
> the ARM devices moved from one makefile to another in one lump,
> which is why I didn't want just this RTC device in hw/Makefile.objs.

Note this discussion here was only about through which Makefile to
compile files, not where to place files as in the kvm/ discussion.

Andreas
Paolo Bonzini July 1, 2012, 3:26 p.m. UTC | #10
Il 29/06/2012 14:26, Andreas Färber ha scritto:
>> > 
>> > Oh, I see. Should we place this device to hw/Makefile.objs in v2?
> That would've been nice, but I'll do it as a follow-up now.

Yes, so we can also use Anthony's new CONFIG_ARCH_ARM (introducing
CONFIG_EXYNOS can be done later).

Paolo
Mitsyanko Igor July 2, 2012, 9:08 a.m. UTC | #11
On 07/01/2012 07:26 PM, Paolo Bonzini wrote:
> Il 29/06/2012 14:26, Andreas Färber ha scritto:
>>>>
>>>> Oh, I see. Should we place this device to hw/Makefile.objs in v2?
>> That would've been nice, but I'll do it as a follow-up now.
>
> Yes, so we can also use Anthony's new CONFIG_ARCH_ARM (introducing
> CONFIG_EXYNOS can be done later).
>
> Paolo
>
>
>

So, what's the consensus here, for now new devices go to 
hw/arm/Makefile.objs while somebody moves all new and old not 
cpu-specific devices (not just exynos-related) from hw/arm/Makefile.objs 
to hw/Makefile.objs with one commit? Because having one RTC device 
compile through hw/Makefile.objs while all other exynos devices are 
compiled through hw/arm/Makefile.objs doesn't makes much sense.

And also its not clear whether you're planning to compile these devices 
through obj-y or through hw-obj-y, because we still have 
target_phys_addr_t in our memory API.
Peter Maydell July 2, 2012, 9:31 a.m. UTC | #12
On 2 July 2012 10:08, Igor Mitsyanko <i.mitsyanko@samsung.com> wrote:
> So, what's the consensus here, for now new devices go to
> hw/arm/Makefile.objs while somebody moves all new and old not cpu-specific
> devices (not just exynos-related) from hw/arm/Makefile.objs to
> hw/Makefile.objs with one commit? Because having one RTC device compile
> through hw/Makefile.objs while all other exynos devices are compiled through
> hw/arm/Makefile.objs doesn't makes much sense.

I don't want things moved piecemeal, especially not one file from
a whole board model.

I'd also like to see a nice clear summary of the ground rules first
(ie how you decide which makefile / target / whatever a file should
be in). At the moment I'm not really sure what the rules are, which
means I can't properly review those bits of patches.

-- PMM
Paolo Bonzini July 2, 2012, 9:37 a.m. UTC | #13
Il 02/07/2012 11:31, Peter Maydell ha scritto:
> On 2 July 2012 10:08, Igor Mitsyanko <i.mitsyanko@samsung.com> wrote:
>> So, what's the consensus here, for now new devices go to
>> hw/arm/Makefile.objs while somebody moves all new and old not cpu-specific
>> devices (not just exynos-related) from hw/arm/Makefile.objs to
>> hw/Makefile.objs with one commit? Because having one RTC device compile
>> through hw/Makefile.objs while all other exynos devices are compiled through
>> hw/arm/Makefile.objs doesn't makes much sense.
> 
> I don't want things moved piecemeal, especially not one file from
> a whole board model.
> 
> I'd also like to see a nice clear summary of the ground rules first
> (ie how you decide which makefile / target / whatever a file should
> be in). At the moment I'm not really sure what the rules are, which
> means I can't properly review those bits of patches.

For now nothing should change compared to the past, except that obj-y
should appear in hw/ARCH/Makefile.objs rather than Makefile.target.
This is because there is still no mechanism to guard the build of
ARM-only devices from hw/Makefile.objs.  When Anthony's patch lands, we
can start moving files to hw/Makefile.objs using CONFIG_ARCH_ARM; I
understood Andreas is going to do that.

For new boards, you may want to request a new CONFIG_* symbol (e.g.
CONFIG_EXYNOS) so that new boards can already compile their devices from
hw/Makefile.objs.  However, that's up to you and definitely shouldn't be
done for one file from a whole board model, as you said.

Paolo
Paolo Bonzini July 2, 2012, 9:42 a.m. UTC | #14
Il 02/07/2012 11:37, Paolo Bonzini ha scritto:
> Il 02/07/2012 11:31, Peter Maydell ha scritto:
>> On 2 July 2012 10:08, Igor Mitsyanko <i.mitsyanko@samsung.com> wrote:
>>> So, what's the consensus here, for now new devices go to
>>> hw/arm/Makefile.objs while somebody moves all new and old not cpu-specific
>>> devices (not just exynos-related) from hw/arm/Makefile.objs to
>>> hw/Makefile.objs with one commit? Because having one RTC device compile
>>> through hw/Makefile.objs while all other exynos devices are compiled through
>>> hw/arm/Makefile.objs doesn't makes much sense.
>>
>> I don't want things moved piecemeal, especially not one file from
>> a whole board model.
>>
>> I'd also like to see a nice clear summary of the ground rules first
>> (ie how you decide which makefile / target / whatever a file should
>> be in). At the moment I'm not really sure what the rules are, which
>> means I can't properly review those bits of patches.
> 
> For now nothing should change compared to the past, except that obj-y
> should appear in hw/ARCH/Makefile.objs rather than Makefile.target.
> This is because there is still no mechanism to guard the build of
> ARM-only devices from hw/Makefile.objs.  When Anthony's patch lands, we
> can start moving files to hw/Makefile.objs using CONFIG_ARCH_ARM; I
> understood Andreas is going to do that.

Small addendum: I think the idea should be to put source in hw/ARCH if
it has a hard dependency on target-ARCH, otherwise we can leave it in
hw/ and decide on a case-by-case basis.

Personally, I believe it'd be best if board descriptions were moved to
hw/ARCH, even if all the required hardware is in hw/ and even if the
file can be moved from obj-y to hw-obj-y.  However, this can be left
open to later discussion, and is complicated by the fact that some
boards (e.g. musicpal) include devices and machine models in the same file.

Paolo
Peter Maydell July 2, 2012, 9:44 a.m. UTC | #15
On 2 July 2012 10:42, Paolo Bonzini <pbonzini@redhat.com> wrote:
> Personally, I believe it'd be best if board descriptions were moved to
> hw/ARCH, even if all the required hardware is in hw/ and even if the
> file can be moved from obj-y to hw-obj-y.  However, this can be left
> open to later discussion, and is complicated by the fact that some
> boards (e.g. musicpal) include devices and machine models in the same file.

I don't personally see the distinction between "device model" and
"machine model" as particularly interesting, and I hope/think that
with increased QOMification the implementation differences beween
the two will tend go away.

-- PMM
Paolo Bonzini July 2, 2012, 9:46 a.m. UTC | #16
Il 02/07/2012 11:44, Peter Maydell ha scritto:
> On 2 July 2012 10:42, Paolo Bonzini <pbonzini@redhat.com> wrote:
>> > Personally, I believe it'd be best if board descriptions were moved to
>> > hw/ARCH, even if all the required hardware is in hw/ and even if the
>> > file can be moved from obj-y to hw-obj-y.  However, this can be left
>> > open to later discussion, and is complicated by the fact that some
>> > boards (e.g. musicpal) include devices and machine models in the same file.
> I don't personally see the distinction between "device model" and
> "machine model" as particularly interesting, and I hope/think that
> with increased QOMification the implementation differences beween
> the two will tend go away.

I agree.  The distinction is just that machine models often have a
dependency on target-ARCH/ files, and that would be (for me) the line
between hw/ and hw/ARCH.

Paolo
Andreas Färber July 2, 2012, 11:44 a.m. UTC | #17
Am 01.07.2012 17:26, schrieb Paolo Bonzini:
> Il 29/06/2012 14:26, Andreas Färber ha scritto:
>>>>
>>>> Oh, I see. Should we place this device to hw/Makefile.objs in v2?
>> That would've been nice, but I'll do it as a follow-up now.
> 
> Yes, so we can also use Anthony's new CONFIG_ARCH_ARM (introducing
> CONFIG_EXYNOS can be done later).

I have a patch in the works, but exynos turned out more difficult than
other devices (also OMAP and PXA2xx) because it uses a shared
<socname>.h header that uses CPU. For tegra I fixed that by adding an
#ifdef NEED_CPU_H for the appropriate parts.

Andreas
Andreas Färber July 2, 2012, 12:04 p.m. UTC | #18
Am 02.07.2012 11:31, schrieb Peter Maydell:
> On 2 July 2012 10:08, Igor Mitsyanko <i.mitsyanko@samsung.com> wrote:
>> So, what's the consensus here, for now new devices go to
>> hw/arm/Makefile.objs while somebody moves all new and old not cpu-specific
>> devices (not just exynos-related) from hw/arm/Makefile.objs to
>> hw/Makefile.objs with one commit? Because having one RTC device compile
>> through hw/Makefile.objs while all other exynos devices are compiled through
>> hw/arm/Makefile.objs doesn't makes much sense.
> 
> I don't want things moved piecemeal, especially not one file from
> a whole board model.
> 
> I'd also like to see a nice clear summary of the ground rules first
> (ie how you decide which makefile / target / whatever a file should
> be in). At the moment I'm not really sure what the rules are, which
> means I can't properly review those bits of patches.

We've been through that like three times lately, not sure what's left to
discuss there?

There's two things to review in the migration we're discussing:
1) Are files compiled before the conversion still compiled afterwards?
2) Does the build break due to use of CPU or explicit swapping?

For new patches you should keep on eye on whether a new device really
must go into hw/arm/Makefile.objs or whether it can go in
hw/Makefile.objs instead. I.e., hw/Makefile.objs (libhwX/) should be the
rule for devices, not the exception. Machines today are tightly coupled
to the CPU and need to go into hw/arch/Makefile.objs always. Whether the
files get placed into hw/arch/ or hw/ is a matter of taste.

Andreas
Mitsyanko Igor July 2, 2012, 12:18 p.m. UTC | #19
On 07/02/2012 03:44 PM, Andreas Färber wrote:
> Am 01.07.2012 17:26, schrieb Paolo Bonzini:
>> Il 29/06/2012 14:26, Andreas Färber ha scritto:
>>>>>
>>>>> Oh, I see. Should we place this device to hw/Makefile.objs in v2?
>>> That would've been nice, but I'll do it as a follow-up now.
>>
>> Yes, so we can also use Anthony's new CONFIG_ARCH_ARM (introducing
>> CONFIG_EXYNOS can be done later).
>
> I have a patch in the works, but exynos turned out more difficult than
> other devices (also OMAP and PXA2xx) because it uses a shared
> <socname>.h header that uses CPU. For tegra I fixed that by adding an
> #ifdef NEED_CPU_H for the appropriate parts.
>
> Andreas
>

I think we can drop ARMCPU *cpu[] member from Exynos4210State and just 
use local variable in exynos4210_init() instead, like everyone else does.
exynos4210_write_secondary() prototype can be moved from exynos4210.h 
header to exynos4_boards.c, or we can pass arm_boot_info 
exynos4_board_binfo to Exynos4210 SoC init function exynos4210_init() to 
initialize .write_secondary_boot field there.
Peter Maydell July 2, 2012, 12:19 p.m. UTC | #20
On 2 July 2012 13:04, Andreas Färber <afaerber@suse.de> wrote:
> Am 02.07.2012 11:31, schrieb Peter Maydell:
>> On 2 July 2012 10:08, Igor Mitsyanko <i.mitsyanko@samsung.com> wrote:
>>> So, what's the consensus here, for now new devices go to
>>> hw/arm/Makefile.objs while somebody moves all new and old not cpu-specific
>>> devices (not just exynos-related) from hw/arm/Makefile.objs to
>>> hw/Makefile.objs with one commit? Because having one RTC device compile
>>> through hw/Makefile.objs while all other exynos devices are compiled through
>>> hw/arm/Makefile.objs doesn't makes much sense.
>>
>> I don't want things moved piecemeal, especially not one file from
>> a whole board model.
>>
>> I'd also like to see a nice clear summary of the ground rules first
>> (ie how you decide which makefile / target / whatever a file should
>> be in). At the moment I'm not really sure what the rules are, which
>> means I can't properly review those bits of patches.
>
> We've been through that like three times lately, not sure what's left to
> discuss there?

It's not in docs/ or in some makefile as a comment. I don't mean
"I want another mailing list thread"...

-- PMM
Andreas Färber July 2, 2012, 12:27 p.m. UTC | #21
Am 02.07.2012 11:37, schrieb Paolo Bonzini:
> Il 02/07/2012 11:31, Peter Maydell ha scritto:
>> On 2 July 2012 10:08, Igor Mitsyanko <i.mitsyanko@samsung.com> wrote:
>>> So, what's the consensus here, for now new devices go to
>>> hw/arm/Makefile.objs while somebody moves all new and old not cpu-specific
>>> devices (not just exynos-related) from hw/arm/Makefile.objs to
>>> hw/Makefile.objs with one commit? Because having one RTC device compile
>>> through hw/Makefile.objs while all other exynos devices are compiled through
>>> hw/arm/Makefile.objs doesn't makes much sense.
>>
>> I don't want things moved piecemeal, especially not one file from
>> a whole board model.
>>
>> I'd also like to see a nice clear summary of the ground rules first
>> (ie how you decide which makefile / target / whatever a file should
>> be in). At the moment I'm not really sure what the rules are, which
>> means I can't properly review those bits of patches.
> 
> For now nothing should change compared to the past, except that obj-y
> should appear in hw/ARCH/Makefile.objs rather than Makefile.target.
> This is because there is still no mechanism to guard the build of
> ARM-only devices from hw/Makefile.objs.  When Anthony's patch lands, we
> can start moving files to hw/Makefile.objs using CONFIG_ARCH_ARM; I
> understood Andreas is going to do that.

Not quite. What I started long before Anthony's patch is making devices
(first Xilinx then ARM) less dependent on my CPU changes. That does not
need CONFIG_[ARCH_]* in our current system. With armeb in mind I've gone
for SoC-level and more fine-granular for generic devices.

Andreas
diff mbox

Patch

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 88ff47d..0fdb832 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -11,6 +11,7 @@  obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
 obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
 obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
+obj-y += exynos4210_rtc.o
 obj-y += arm_l2x0.o
 obj-y += arm_mptimer.o a15mpcore.o
 obj-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
diff --git a/hw/exynos4210.c b/hw/exynos4210.c
index 9c20b3f..6e105e0 100644
--- a/hw/exynos4210.c
+++ b/hw/exynos4210.c
@@ -33,6 +33,9 @@ 
 /* PWM */
 #define EXYNOS4210_PWM_BASE_ADDR       0x139D0000
 
+/* RTC */
+#define EXYNOS4210_RTC_BASE_ADDR       0x10070000
+
 /* MCT */
 #define EXYNOS4210_MCT_BASE_ADDR       0x10050000
 
@@ -258,6 +261,11 @@  Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
                           s->irq_table[exynos4210_get_irq(22, 3)],
                           s->irq_table[exynos4210_get_irq(22, 4)],
                           NULL);
+    /* RTC */
+    sysbus_create_varargs("exynos4210.rtc", EXYNOS4210_RTC_BASE_ADDR,
+                          s->irq_table[exynos4210_get_irq(23, 0)],
+                          s->irq_table[exynos4210_get_irq(23, 1)],
+                          NULL);
 
     /* Multi Core Timer */
     dev = qdev_create(NULL, "exynos4210.mct");
diff --git a/hw/exynos4210_rtc.c b/hw/exynos4210_rtc.c
new file mode 100644
index 0000000..2ac6301
--- /dev/null
+++ b/hw/exynos4210_rtc.c
@@ -0,0 +1,607 @@ 
+/*
+ * Samsung exynos4210 Real Time Clock
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *  Ogurtsov Oleg <o.ogurtsov@samsung.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Description:
+ * Register RTCCON:
+ *  CLKSEL Bit[1] not used
+ *  CLKOUTEN Bit[9] not used
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "qemu-common.h"
+#include "ptimer.h"
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+
+#include "exynos4210.h"
+
+#define DEBUG_RTC 0
+
+#if DEBUG_RTC
+#define DPRINTF(fmt, ...) \
+        do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
+                ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define     EXYNOS4210_RTC_REG_MEM_SIZE     0x0100
+
+#define     INTP            0x0030
+#define     RTCCON          0x0040
+#define     TICCNT          0x0044
+#define     RTCALM          0x0050
+#define     ALMSEC          0x0054
+#define     ALMMIN          0x0058
+#define     ALMHOUR         0x005C
+#define     ALMDAY          0x0060
+#define     ALMMON          0x0064
+#define     ALMYEAR         0x0068
+#define     BCDSEC          0x0070
+#define     BDCMIN          0x0074
+#define     BCDHOUR         0x0078
+#define     BCDDAY          0x007C
+#define     BCDDAYWEEK      0x0080
+#define     BCDMON          0x0084
+#define     BCDYEAR         0x0088
+#define     CURTICNT        0x0090
+
+#define     TICK_TIMER_ENABLE   0x0100
+#define     TICNT_THRESHHOLD    2
+
+
+#define     RTC_ENABLE          0x0001
+
+#define     INTP_TICK_ENABLE    0x0001
+#define     INTP_ALM_ENABLE     0x0002
+
+#define     ALARM_INT_ENABLE    0x0040
+
+#define     RTC_BASE_FREQ       32768
+
+typedef struct Exynos4210RTCState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    /* registers */
+    uint32_t    reg_intp;
+    uint32_t    reg_rtccon;
+    uint32_t    reg_ticcnt;
+    uint32_t    reg_rtcalm;
+    uint32_t    reg_almsec;
+    uint32_t    reg_almmin;
+    uint32_t    reg_almhour;
+    uint32_t    reg_almday;
+    uint32_t    reg_almmon;
+    uint32_t    reg_almyear;
+    uint32_t    reg_curticcnt;
+
+    ptimer_state    *ptimer;        /* tick timer */
+    ptimer_state    *ptimer_1Hz;    /* clock timer */
+    uint32_t        freq;
+
+    qemu_irq        tick_irq;   /* Time Tick Generator irq */
+    qemu_irq        alm_irq;    /* alarm irq */
+
+    struct tm   current_tm;     /* current time */
+} Exynos4210RTCState;
+
+#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
+
+/*** VMState ***/
+static const VMStateDescription vmstate_exynos4210_rtc_state = {
+    .name = "exynos4210.rtc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
+        VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
+        VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
+        VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
+        VMSTATE_UINT32(freq, Exynos4210RTCState),
+        VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
+        VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
+        VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
+        VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
+        VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
+        VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
+        VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static inline int rtc_to_bcd(int a, int count)
+{
+    int ret = (((a % 100) / 10) << 4) | (a % 10);
+    if (count == 3) {
+        ret |= ((a % 100) << 8);
+    }
+    return ret;
+}
+
+static inline int rtc_from_bcd(uint32_t a, int count)
+{
+    int ret = (((a >> 4) & 0x0f) * 10) + (a & 0x0f);
+    if (count == 3) {
+        ret += (((a >> 8) & 0x0f) * 100);
+    }
+    return ret;
+}
+
+static void check_alarm_raise(Exynos4210RTCState *s)
+{
+    unsigned int alarm_raise = 0;
+    struct tm stm = s->current_tm;
+
+    if ((s->reg_rtcalm & 0x01) &&
+        (rtc_to_bcd(stm.tm_sec, 2) == s->reg_almsec)) {
+        alarm_raise = 1;
+    }
+    if ((s->reg_rtcalm & 0x02) &&
+        (rtc_to_bcd(stm.tm_min, 2) == s->reg_almmin)) {
+        alarm_raise = 1;
+    }
+    if ((s->reg_rtcalm & 0x04) &&
+        (rtc_to_bcd(stm.tm_hour, 2) == s->reg_almhour)) {
+        alarm_raise = 1;
+    }
+    if ((s->reg_rtcalm & 0x08) &&
+        (rtc_to_bcd(stm.tm_mday, 2) == s->reg_almday)) {
+        alarm_raise = 1;
+    }
+    if ((s->reg_rtcalm & 0x10) &&
+         (rtc_to_bcd(stm.tm_mon, 2) == s->reg_almmon)) {
+        alarm_raise = 1;
+    }
+    if ((s->reg_rtcalm & 0x20) &&
+        (rtc_to_bcd(stm.tm_year, 3) == s->reg_almyear)) {
+        alarm_raise = 1;
+    }
+
+    if (alarm_raise) {
+        DPRINTF("ALARM IRQ\n");
+        /* set irq status */
+        s->reg_intp |= INTP_ALM_ENABLE;
+        qemu_irq_raise(s->alm_irq);
+    }
+}
+
+/*
+ * RTC update frequency
+ * Parameters:
+ *     reg_value - current RTCCON register or his new value
+ */
+static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
+                                       uint32_t reg_value)
+{
+    uint32_t freq;
+
+    freq = s->freq;
+    /* set frequncy for time generator */
+    s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
+
+    if (freq != s->freq) {
+        ptimer_set_freq(s->ptimer, s->freq);
+        DPRINTF("freq=%dHz\n", s->freq);
+    }
+}
+
+/* month is between 0 and 11. */
+static int get_days_in_month(int month, int year)
+{
+    static const int days_tab[12] = {
+        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    int d;
+    if ((unsigned)month >= 12) {
+        return 31;
+    }
+    d = days_tab[month];
+    if (month == 1) {
+        if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
+            d++;
+        }
+    }
+    return d;
+}
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct tm *tm)
+{
+    int days_in_month;
+
+    tm->tm_sec++;
+    if ((unsigned)tm->tm_sec >= 60) {
+        tm->tm_sec = 0;
+        tm->tm_min++;
+        if ((unsigned)tm->tm_min >= 60) {
+            tm->tm_min = 0;
+            tm->tm_hour++;
+            if ((unsigned)tm->tm_hour >= 24) {
+                tm->tm_hour = 0;
+                /* next day */
+                tm->tm_wday++;
+                if ((unsigned)tm->tm_wday >= 7) {
+                    tm->tm_wday = 0;
+                }
+                days_in_month = get_days_in_month(tm->tm_mon,
+                                                  tm->tm_year + 1900);
+                tm->tm_mday++;
+                if (tm->tm_mday < 1) {
+                    tm->tm_mday = 1;
+                } else if (tm->tm_mday > days_in_month) {
+                    tm->tm_mday = 1;
+                    tm->tm_mon++;
+                    if (tm->tm_mon >= 12) {
+                        tm->tm_mon = 0;
+                        tm->tm_year++;
+                    }
+                }
+            }
+        }
+    }
+}
+
+/*
+ * tick handler
+ */
+static void exynos4210_rtc_tick(void *opaque)
+{
+    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+    DPRINTF("TICK IRQ\n");
+    /* set irq status */
+    s->reg_intp |= INTP_TICK_ENABLE;
+    /* raise IRQ */
+    qemu_irq_raise(s->tick_irq);
+
+    /* restart timer */
+    ptimer_set_count(s->ptimer, s->reg_ticcnt);
+    ptimer_run(s->ptimer, 1);
+}
+
+/*
+ * 1Hz clock handler
+ */
+static void exynos4210_rtc_1Hz_tick(void *opaque)
+{
+    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+    rtc_next_second(&s->current_tm);
+    /* DPRINTF("1Hz tick\n"); */
+
+    /* raise IRQ */
+    if (s->reg_rtcalm & ALARM_INT_ENABLE) {
+        check_alarm_raise(s);
+    }
+
+    ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
+    ptimer_run(s->ptimer_1Hz, 1);
+}
+
+/*
+ * RTC Read
+ */
+static uint64_t exynos4210_rtc_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    uint32_t value = 0;
+    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+    switch (offset) {
+    case INTP:
+        value = s->reg_intp;
+        break;
+    case RTCCON:
+        value = s->reg_rtccon;
+        break;
+    case TICCNT:
+        value = s->reg_ticcnt;
+        break;
+    case RTCALM:
+        value = s->reg_rtcalm;
+        break;
+    case ALMSEC:
+        value = s->reg_almsec;
+        break;
+    case ALMMIN:
+        value = s->reg_almmin;
+        break;
+    case ALMHOUR:
+        value = s->reg_almhour;
+        break;
+    case ALMDAY:
+        value = s->reg_almday;
+        break;
+    case ALMMON:
+        value = s->reg_almmon;
+        break;
+    case ALMYEAR:
+        value = s->reg_almyear;
+        break;
+
+    case BCDSEC:
+        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_sec, 2);
+        break;
+    case BDCMIN:
+        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_min, 2);
+        break;
+    case BCDHOUR:
+        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_hour, 2);
+        break;
+    case BCDDAYWEEK:
+        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_wday, 2);
+        break;
+    case BCDDAY:
+        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_mday, 2);
+        break;
+    case BCDMON:
+        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_mon + 1, 2);
+        break;
+    case BCDYEAR:
+        value = (uint32_t)rtc_to_bcd(s->current_tm.tm_year, 3);
+        break;
+
+    case CURTICNT:
+        s->reg_curticcnt = ptimer_get_count(s->ptimer);
+        value = s->reg_curticcnt;
+        break;
+
+    default:
+        fprintf(stderr,
+                "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
+                offset);
+        break;
+    }
+    return value;
+}
+
+/*
+ * RTC Write
+ */
+static void exynos4210_rtc_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+    switch (offset) {
+    case INTP:
+        if (value & INTP_ALM_ENABLE) {
+            qemu_irq_lower(s->alm_irq);
+            s->reg_intp &= (~INTP_ALM_ENABLE);
+        }
+        if (value & INTP_TICK_ENABLE) {
+            qemu_irq_lower(s->tick_irq);
+            s->reg_intp &= (~INTP_TICK_ENABLE);
+        }
+        break;
+    case RTCCON:
+        if (value & RTC_ENABLE) {
+            exynos4210_rtc_update_freq(s, value);
+        }
+        if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
+            /* clock timer */
+            ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
+            ptimer_run(s->ptimer_1Hz, 1);
+            DPRINTF("run clock timer\n");
+        }
+        if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
+            /* tick timer */
+            ptimer_stop(s->ptimer);
+            /* clock timer */
+            ptimer_stop(s->ptimer_1Hz);
+            DPRINTF("stop all timers\n");
+        }
+        if (value & RTC_ENABLE) {
+            if ((value & TICK_TIMER_ENABLE) >
+                (s->reg_rtccon & TICK_TIMER_ENABLE) &&
+                (s->reg_ticcnt)) {
+                ptimer_set_count(s->ptimer, s->reg_ticcnt);
+                ptimer_run(s->ptimer, 1);
+                DPRINTF("run tick timer\n");
+            }
+            if ((value & TICK_TIMER_ENABLE) <
+                (s->reg_rtccon & TICK_TIMER_ENABLE)) {
+                ptimer_stop(s->ptimer);
+            }
+        }
+        s->reg_rtccon = value;
+        break;
+    case TICCNT:
+        if (value > TICNT_THRESHHOLD) {
+            s->reg_ticcnt = value;
+        } else {
+            fprintf(stderr,
+                    "[exynos4210.rtc: bad TICNT value %u ]\n",
+                    (uint32_t)value);
+        }
+        break;
+
+    case RTCALM:
+        s->reg_rtcalm = value;
+        break;
+    case ALMSEC:
+        s->reg_almsec = (value & 0x7f);
+        break;
+    case ALMMIN:
+        s->reg_almmin = (value & 0x7f);
+        break;
+    case ALMHOUR:
+        s->reg_almhour = (value & 0x3f);
+        break;
+    case ALMDAY:
+        s->reg_almday = (value & 0x3f);
+        break;
+    case ALMMON:
+        s->reg_almmon = (value & 0x1f);
+        break;
+    case ALMYEAR:
+        s->reg_almyear = (value & 0x0fff);
+        break;
+
+    case BCDSEC:
+        if (s->reg_rtccon & RTC_ENABLE) {
+            s->current_tm.tm_sec = rtc_from_bcd(value, 2);
+        }
+        break;
+    case BDCMIN:
+        if (s->reg_rtccon & RTC_ENABLE) {
+            s->current_tm.tm_min = rtc_from_bcd(value, 2);
+        }
+        break;
+    case BCDHOUR:
+        if (s->reg_rtccon & RTC_ENABLE) {
+            s->current_tm.tm_hour = rtc_from_bcd(value, 2);
+        }
+        break;
+    case BCDDAYWEEK:
+        if (s->reg_rtccon & RTC_ENABLE) {
+            s->current_tm.tm_wday = rtc_from_bcd(value, 2);
+        }
+        break;
+    case BCDDAY:
+        if (s->reg_rtccon & RTC_ENABLE) {
+            s->current_tm.tm_mday = rtc_from_bcd(value, 2);
+        }
+        break;
+    case BCDMON:
+        if (s->reg_rtccon & RTC_ENABLE) {
+            s->current_tm.tm_mon = rtc_from_bcd(value, 2) - 1;
+        }
+        break;
+    case BCDYEAR:
+        if (s->reg_rtccon & RTC_ENABLE) {
+            s->current_tm.tm_year = rtc_from_bcd(value, 3);
+        }
+        break;
+
+    default:
+        fprintf(stderr,
+                "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
+                offset);
+        break;
+
+    }
+}
+
+/*
+ * Set default values to timer fields and registers
+ */
+static void exynos4210_rtc_reset(DeviceState *d)
+{
+    Exynos4210RTCState *s = (Exynos4210RTCState *)d;
+
+    struct tm tm;
+
+    qemu_get_timedate(&tm, 0);
+    s->current_tm = tm;
+
+    DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
+            s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
+            s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
+
+    s->reg_intp = 0;
+    s->reg_rtccon = 0;
+    s->reg_ticcnt = 0;
+    s->reg_rtcalm = 0;
+    s->reg_almsec = 0;
+    s->reg_almmin = 0;
+    s->reg_almhour = 0;
+    s->reg_almday = 0;
+    s->reg_almmon = 0;
+    s->reg_almyear = 0;
+
+    s->reg_curticcnt = 0;
+
+    exynos4210_rtc_update_freq(s, s->reg_rtccon);
+    ptimer_stop(s->ptimer);
+    ptimer_stop(s->ptimer_1Hz);
+}
+
+static const MemoryRegionOps exynos4210_rtc_ops = {
+    .read = exynos4210_rtc_read,
+    .write = exynos4210_rtc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * RTC timer initialization
+ */
+static int exynos4210_rtc_init(SysBusDevice *dev)
+{
+    Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev);
+    QEMUBH *bh;
+
+    bh = qemu_bh_new(exynos4210_rtc_tick, s);
+    s->ptimer = ptimer_init(bh);
+    ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
+    exynos4210_rtc_update_freq(s, 0);
+
+    bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
+    s->ptimer_1Hz = ptimer_init(bh);
+    ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
+
+    sysbus_init_irq(dev, &s->alm_irq);
+    sysbus_init_irq(dev, &s->tick_irq);
+
+    memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc",
+            EXYNOS4210_RTC_REG_MEM_SIZE);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = exynos4210_rtc_init;
+    dc->reset = exynos4210_rtc_reset;
+    dc->vmsd = &vmstate_exynos4210_rtc_state;
+}
+
+static const TypeInfo exynos4210_rtc_info = {
+    .name          = "exynos4210.rtc",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Exynos4210RTCState),
+    .class_init    = exynos4210_rtc_class_init,
+};
+
+static void exynos4210_rtc_register_types(void)
+{
+    type_register_static(&exynos4210_rtc_info);
+}
+
+type_init(exynos4210_rtc_register_types)