2013/3/6 Paolo Bonzini <pbonz...@redhat.com>: > Il 06/03/2013 08:27, Kuo-Jung Su ha scritto: >> It provides separate second, minute, hour, and day counters. The second >> counter is toggled each second, the minute counter is toggled each minute, >> the hour counter is toggled each hour, and the day counter is toggled each >> day. >> >> The FTRTC011 provides a programmable auto-alarm function. When the second >> auto-alarm function is turned on, the RTC will automatically trigger an >> interrupt each second. The automatic minute and hour alarms can be turned on >> as well. >> >> Signed-off-by: Kuo-Jung Su <dant...@gmail.com> >> --- >> hw/arm/Makefile.objs | 1 + >> hw/arm/faraday_a369_soc.c | 10 ++ >> hw/arm/ftrtc011.c | 346 >> +++++++++++++++++++++++++++++++++++++++++++++ >> hw/arm/ftrtc011.h | 49 +++++++ >> 4 files changed, 406 insertions(+) >> create mode 100644 hw/arm/ftrtc011.c >> create mode 100644 hw/arm/ftrtc011.h >> >> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs >> index bc8e2de..6dbac2f 100644 >> --- a/hw/arm/Makefile.objs >> +++ b/hw/arm/Makefile.objs >> @@ -42,3 +42,4 @@ obj-y += ftahbc020.o >> obj-y += ftddrii030.o >> obj-y += ftpwmtmr010.o >> obj-y += ftwdt010.o >> +obj-y += ftrtc011.o >> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c >> index 1bf64d4..4371a3a 100644 >> --- a/hw/arm/faraday_a369_soc.c >> +++ b/hw/arm/faraday_a369_soc.c >> @@ -189,6 +189,16 @@ a369soc_device_init(FaradaySoCState *s) >> /* ftwdt010 */ >> sysbus_create_simple("ftwdt010", 0x92200000, s->pic[46]); >> qemu_register_reset(a369soc_system_reset, s); >> + >> + /* ftrtc011 */ >> + sysbus_create_varargs("ftrtc011", >> + 0x92100000, >> + s->pic[0], /* Alarm (Level): NC in A369 */ >> + s->pic[42], /* Alarm (Edge) */ >> + s->pic[43], /* Second (Edge) */ >> + s->pic[44], /* Minute (Edge) */ >> + s->pic[45], /* Hour (Edge) */ >> + NULL); >> } >> >> static int a369soc_init(SysBusDevice *dev) >> diff --git a/hw/arm/ftrtc011.c b/hw/arm/ftrtc011.c >> new file mode 100644 >> index 0000000..2e8776e >> --- /dev/null >> +++ b/hw/arm/ftrtc011.c >> @@ -0,0 +1,346 @@ >> +/* >> + * QEMU model of the FTRTC011 RTC Timer >> + * >> + * Copyright (C) 2012 Faraday Technology >> + * Written by Dante Su <dant...@faraday-tech.com> >> + * >> + * This file is licensed under GNU GPL v2+. >> + */ >> + >> +#include "hw/sysbus.h" >> +#include "qemu/timer.h" >> +#include "sysemu/sysemu.h" >> + >> +#include "faraday.h" >> +#include "ftrtc011.h" >> + >> +enum ftrtc011_irqpin { >> + IRQ_ALARM_LEVEL = 0, >> + IRQ_ALARM_EDGE, >> + IRQ_SEC, >> + IRQ_MIN, >> + IRQ_HOUR, >> + IRQ_DAY, >> +}; >> + >> +#define TYPE_FTRTC011 "ftrtc011" >> + >> +#define CFG_REGSIZE (0x3c / 4) >> + >> +typedef struct Ftrtc011State { >> + SysBusDevice busdev; >> + MemoryRegion mmio; >> + >> + qemu_irq irq[6]; >> + >> + QEMUTimer *qtimer; >> + int64_t rtc_start; >> + >> + /* HW register caches */ >> + uint32_t regs[CFG_REGSIZE]; >> +} Ftrtc011State; >> + >> +#define FTRTC011(obj) \ >> + OBJECT_CHECK(Ftrtc011State, obj, TYPE_FTRTC011) >> + >> +#define RTC_REG32(s, off) \ >> + ((s)->regs[(off) / 4]) >> + >> +/* Update interrupts. */ >> +static void ftrtc011_update_irq(Ftrtc011State *s) >> +{ >> + uint32_t mask = extract32(RTC_REG32(s, REG_CR), 1, 5) >> + & RTC_REG32(s, REG_ISR); >> + >> + qemu_set_irq(s->irq[IRQ_ALARM_LEVEL], (mask & ISR_ALARM) ? 1 : 0); >> + >> + if (mask) { >> + if (mask & ISR_SEC) { >> + qemu_irq_pulse(s->irq[IRQ_SEC]); >> + } >> + if (mask & ISR_MIN) { >> + qemu_irq_pulse(s->irq[IRQ_MIN]); >> + } >> + if (mask & ISR_HOUR) { >> + qemu_irq_pulse(s->irq[IRQ_HOUR]); >> + } >> + if (mask & ISR_DAY) { >> + qemu_irq_pulse(s->irq[IRQ_DAY]); >> + } >> + if (mask & ISR_ALARM) { >> + qemu_irq_pulse(s->irq[IRQ_ALARM_EDGE]); >> + } >> + } >> +} >> + >> +static void ftrtc011_timer_resync(Ftrtc011State *s) >> +{ >> + int64_t elapsed = RTC_REG32(s, REG_SEC) >> + + (60 * RTC_REG32(s, REG_MIN)) >> + + (3600 * RTC_REG32(s, REG_HOUR)) >> + + (86400 * RTC_REG32(s, REG_DAY)); >> + s->rtc_start = get_clock_realtime() - elapsed * 1000000000LL; > > Please use instead qemu_get_clock_ns(rtc_clock). > > In general, use rtc_clock instead of rt_clock. This way a) you can > control the clock with -clock; b) you can write testcases. >
Got it, thanks > Please add a testcase for the RTC clock, since we have other examples of > tested devices like this one. > Excuse me, could you please show me the way how to build the test cases and launch the test case. I do have found a web page to qtest at the following link: http://wiki.qemu.org/Features/QTest But it's wrong at both 'build/example usage' and 'Additional Details/Status', please see the logs bellow: dante@vmware:/pub/qemu$ ./configure --target-list=arm-softmmu --audio-drv-list="oss alsa sdl" --enable-qtest ERROR: unknown option --enable-qtest dante@vmware:/pub/qemu$ ./arm-softmmu/qemu-system-arm -test ? qemu-system-arm: -test: invalid option > Paolo > >> +} >> + >> +static void ftrtc011_timer_update(Ftrtc011State *s) >> +{ >> + int64_t elapsed; >> + uint8_t sec, min, hr; >> + uint32_t day; >> + >> + if (!(RTC_REG32(s, REG_CR) & CR_EN)) { >> + return; >> + } >> + >> + /* >> + * Although the timer is supposed to tick per second, >> + * there is no guarantee that the tick interval is >> + * exactly 1 second, and the system might even be >> + * suspend/resume which cause large time drift here. >> + */ >> + elapsed = (get_clock_realtime() - s->rtc_start) / 1000000000LL; >> + sec = (uint8_t)(elapsed % 60LL); >> + min = (uint8_t)((elapsed / 60LL) % 60LL); >> + hr = (uint8_t)((elapsed / 3600LL) % 24LL); >> + day = (uint32_t)(elapsed / 86400LL); >> + >> + /* sec interrupt */ >> + if ((RTC_REG32(s, REG_SEC) != sec) >> + && (RTC_REG32(s, REG_CR) & CR_INTR_SEC)) { >> + RTC_REG32(s, REG_ISR) |= ISR_SEC; >> + } >> + /* min interrupt */ >> + if ((RTC_REG32(s, REG_MIN) != min) >> + && (RTC_REG32(s, REG_CR) & CR_INTR_MIN)) { >> + RTC_REG32(s, REG_ISR) |= ISR_MIN; >> + } >> + /* hr interrupt */ >> + if ((RTC_REG32(s, REG_HOUR) != hr) >> + && (RTC_REG32(s, REG_CR) & CR_INTR_HOUR)) { >> + RTC_REG32(s, REG_ISR) |= ISR_HOUR; >> + } >> + /* day interrupt */ >> + if ((RTC_REG32(s, REG_DAY) != day) >> + && (RTC_REG32(s, REG_CR) & CR_INTR_DAY)) { >> + RTC_REG32(s, REG_ISR) |= ISR_DAY; >> + } >> + /* alarm interrupt */ >> + if (RTC_REG32(s, REG_CR) & CR_INTR_ALARM) { >> + if ((RTC_REG32(s, REG_SEC) >> + | (RTC_REG32(s, REG_MIN) << 8) >> + | (RTC_REG32(s, REG_HOUR) << 16)) == >> + (RTC_REG32(s, REG_ALARM_SEC) >> + | (RTC_REG32(s, REG_ALARM_MIN) << 8) >> + | (RTC_REG32(s, REG_ALARM_HOUR) << 16))) { >> + RTC_REG32(s, REG_ISR) |= ISR_ALARM; >> + } >> + } >> + >> + /* send interrupt signal */ >> + ftrtc011_update_irq(s); >> + >> + /* update RTC registers */ >> + RTC_REG32(s, REG_SEC) = (uint8_t)sec; >> + RTC_REG32(s, REG_MIN) = (uint8_t)min; >> + RTC_REG32(s, REG_HOUR) = (uint8_t)hr; >> + RTC_REG32(s, REG_DAY) = (uint16_t)day; >> +} >> + >> +static uint64_t ftrtc011_mem_read(void *opaque, hwaddr addr, unsigned size) >> +{ >> + Ftrtc011State *s = FTRTC011(opaque); >> + uint32_t ret = 0; >> + >> + switch (addr) { >> + case REG_SEC ... REG_DAY: >> + ftrtc011_timer_update(s); >> + case REG_ALARM_SEC ... REG_ISR: >> + ret = s->regs[addr / 4]; >> + break; >> + case REG_REVR: >> + ret = 0x00010000; /* rev. 1.0.0 */ >> + break; >> + case REG_CURRENT: >> + ftrtc011_timer_update(s); >> + ret |= RTC_REG32(s, REG_DAY) << 17; >> + ret |= RTC_REG32(s, REG_HOUR) << 12; >> + ret |= RTC_REG32(s, REG_MIN) << 6; >> + ret |= RTC_REG32(s, REG_SEC); >> + break; >> + default: >> + qemu_log_mask(LOG_GUEST_ERROR, >> + "ftrtc011: undefined memory access@%#" HWADDR_PRIx "\n", addr); >> + break; >> + } >> + >> + return ret; >> +} >> + >> +static void >> +ftrtc011_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) >> +{ >> + Ftrtc011State *s = FTRTC011(opaque); >> + >> + switch (addr) { >> + case REG_ALARM_SEC ... REG_ALARM_HOUR: >> + case REG_WSEC ... REG_WHOUR: >> + s->regs[addr / 4] = (uint32_t)val & 0x3f; >> + break; >> + case REG_WDAY: >> + s->regs[addr / 4] = (uint32_t)val & 0xffff; >> + break; >> + case REG_CR: >> + /* update the RTC counter with the user supplied values */ >> + if (val & CR_LOAD) { >> + RTC_REG32(s, REG_SEC) = RTC_REG32(s, REG_WSEC); >> + RTC_REG32(s, REG_MIN) = RTC_REG32(s, REG_WMIN); >> + RTC_REG32(s, REG_HOUR) = RTC_REG32(s, REG_WHOUR); >> + RTC_REG32(s, REG_DAY) = RTC_REG32(s, REG_WDAY); >> + val &= ~CR_LOAD; >> + ftrtc011_timer_resync(s); >> + } >> + /* check if RTC enabled */ >> + if (val & CR_EN) { >> + if (!(RTC_REG32(s, REG_CR) & CR_EN)) { >> + ftrtc011_timer_resync(s); >> + } >> + qemu_mod_timer(s->qtimer, qemu_get_clock_ms(rt_clock) + 1000); >> + } else { >> + qemu_del_timer(s->qtimer); >> + } >> + RTC_REG32(s, REG_CR) = (uint32_t)val; >> + break; >> + case REG_ISR: >> + RTC_REG32(s, REG_ISR) &= ~((uint32_t)val); >> + ftrtc011_update_irq(s); >> + break; >> + default: >> + qemu_log_mask(LOG_GUEST_ERROR, >> + "ftrtc011: undefined memory access@%#" HWADDR_PRIx "\n", addr); >> + break; >> + } >> + >> + if (RTC_REG32(s, REG_ALARM_SEC) > 59 >> + || RTC_REG32(s, REG_ALARM_MIN) > 59 >> + || RTC_REG32(s, REG_ALARM_HOUR) > 23 >> + || RTC_REG32(s, REG_WSEC) > 59 >> + || RTC_REG32(s, REG_WMIN) > 59 >> + || RTC_REG32(s, REG_WHOUR) > 23) { >> + goto werr; >> + } >> + >> + return; >> + >> +werr: >> + fprintf(stderr, "ftrtc011: %u is an invalid value.\n", (uint32_t)val); >> + exit(1); >> +} >> + >> +static const MemoryRegionOps mmio_ops = { >> + .read = ftrtc011_mem_read, >> + .write = ftrtc011_mem_write, >> + .endianness = DEVICE_LITTLE_ENDIAN, >> + .valid = { >> + .min_access_size = 4, >> + .max_access_size = 4 >> + } >> +}; >> + >> +static void ftrtc011_timer_tick(void *opaque) >> +{ >> + Ftrtc011State *s = FTRTC011(opaque); >> + ftrtc011_timer_update(s); >> + qemu_mod_timer(s->qtimer, qemu_get_clock_ms(rt_clock) + 1000); >> +} >> + >> +static void ftrtc011_reset(DeviceState *ds) >> +{ >> + Ftrtc011State *s = FTRTC011(SYS_BUS_DEVICE(ds)); >> + >> + qemu_del_timer(s->qtimer); >> + memset(s->regs, 0, sizeof(s->regs)); >> + ftrtc011_update_irq(s); >> +} >> + >> +static void ftrtc011_save(QEMUFile *f, void *opaque) >> +{ >> + int i; >> + Ftrtc011State *s = FTRTC011(opaque); >> + >> + for (i = 0; i < sizeof(s->regs) / 4; ++i) { >> + qemu_put_be32(f, s->regs[i]); >> + } >> +} >> + >> +static int ftrtc011_load(QEMUFile *f, void *opaque, int version_id) >> +{ >> + int i; >> + Ftrtc011State *s = FTRTC011(opaque); >> + >> + for (i = 0; i < sizeof(s->regs) / 4; ++i) { >> + s->regs[i] = qemu_get_be32(f); >> + } >> + >> + return 0; >> +} >> + >> +static int ftrtc011_init(SysBusDevice *dev) >> +{ >> + Ftrtc011State *s = FTRTC011(dev); >> + >> + s->qtimer = qemu_new_timer_ms(rt_clock, ftrtc011_timer_tick, s); >> + >> + memory_region_init_io(&s->mmio, >> + &mmio_ops, >> + s, >> + TYPE_FTRTC011, >> + 0x1000); >> + sysbus_init_mmio(dev, &s->mmio); >> + sysbus_init_irq(dev, &s->irq[IRQ_ALARM_LEVEL]); >> + sysbus_init_irq(dev, &s->irq[IRQ_ALARM_EDGE]); >> + sysbus_init_irq(dev, &s->irq[IRQ_SEC]); >> + sysbus_init_irq(dev, &s->irq[IRQ_MIN]); >> + sysbus_init_irq(dev, &s->irq[IRQ_HOUR]); >> + sysbus_init_irq(dev, &s->irq[IRQ_DAY]); >> + >> + register_savevm(&dev->qdev, TYPE_FTRTC011, -1, 0, >> + ftrtc011_save, ftrtc011_load, s); >> + >> + return 0; >> +} >> + >> +static const VMStateDescription vmstate_ftrtc011 = { >> + .name = TYPE_FTRTC011, >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .minimum_version_id_old = 1, >> + .fields = (VMStateField[]) { >> + VMSTATE_UINT32_ARRAY(regs, Ftrtc011State, CFG_REGSIZE), >> + VMSTATE_END_OF_LIST() >> + } >> +}; >> + >> +static void ftrtc011_class_init(ObjectClass *klass, void *data) >> +{ >> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + k->init = ftrtc011_init; >> + dc->vmsd = &vmstate_ftrtc011; >> + dc->reset = ftrtc011_reset; >> + dc->no_user = 1; >> +} >> + >> +static const TypeInfo ftrtc011_info = { >> + .name = TYPE_FTRTC011, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(Ftrtc011State), >> + .class_init = ftrtc011_class_init, >> +}; >> + >> +static void ftrtc011_register_types(void) >> +{ >> + type_register_static(&ftrtc011_info); >> +} >> + >> +type_init(ftrtc011_register_types) >> diff --git a/hw/arm/ftrtc011.h b/hw/arm/ftrtc011.h >> new file mode 100644 >> index 0000000..feb8aab >> --- /dev/null >> +++ b/hw/arm/ftrtc011.h >> @@ -0,0 +1,49 @@ >> +/* >> + * QEMU model of the FTRTC011 RTC Timer >> + * >> + * Copyright (C) 2012 Faraday Technology >> + * Written by Dante Su <dant...@faraday-tech.com> >> + * >> + * This file is licensed under GNU GPL v2+. >> + */ >> +#ifndef HW_ARM_FTRTC011_H >> +#define HW_ARM_FTRTC011_H >> + >> +#include "qemu/bitops.h" >> + >> +/* Hardware registers */ >> +#define REG_SEC 0x00 >> +#define REG_MIN 0x04 >> +#define REG_HOUR 0x08 >> +#define REG_DAY 0x0C >> + >> +#define REG_ALARM_SEC 0x10 /* Alarm: sec */ >> +#define REG_ALARM_MIN 0x14 /* Alarm: min */ >> +#define REG_ALARM_HOUR 0x18 /* Alarm: hour */ >> + >> +#define REG_CR 0x20 /* Control Register */ >> +#define REG_WSEC 0x24 /* Write SEC Register */ >> +#define REG_WMIN 0x28 /* Write MIN Register */ >> +#define REG_WHOUR 0x2C /* Write HOUR Register */ >> +#define REG_WDAY 0x30 /* Write DAY Register */ >> +#define REG_ISR 0x34 /* Interrupt Status Register */ >> + >> +#define REG_REVR 0x3C /* Revision Register */ >> +#define REG_CURRENT 0x44 /* Group-up day/hour/min/sec as a register >> */ >> + >> +#define CR_LOAD BIT(6) /* Update counters by Wxxx registers */ >> +#define CR_INTR_ALARM BIT(5) /* Alarm interrupt enabled */ >> +#define CR_INTR_DAY BIT(4) /* DDAY interrupt enabled */ >> +#define CR_INTR_HOUR BIT(3) /* HOUR interrupt enabled */ >> +#define CR_INTR_MIN BIT(2) /* MIN interrupt enabled */ >> +#define CR_INTR_SEC BIT(1) /* SEC interrupt enabled */ >> +#define CR_EN BIT(0) /* RTC enabled */ >> + >> +#define ISR_LOAD BIT(5) /* CR_LOAD finished (no interrupt occurs) */ >> +#define ISR_ALARM BIT(4) >> +#define ISR_DAY BIT(3) >> +#define ISR_HOUR BIT(2) >> +#define ISR_MIN BIT(1) >> +#define ISR_SEC BIT(0) >> + >> +#endif >> > -- Best wishes, Kuo-Jung Su