Message ID | 1340610940-923-2-git-send-email-o.ogurtsov@samsung.com |
---|---|
State | New |
Headers | show |
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>
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
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
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?
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
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?
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
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
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
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
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.
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
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
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
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
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
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
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
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.
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
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 --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)
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