Message ID | 1362554857-3896-9-git-send-email-dantesu@gmail.com |
---|---|
State | New |
Headers | show |
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 <dantesu@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 <dantesu@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. Please add a testcase for the RTC clock, since we have other examples of tested devices like this one. 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 <dantesu@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 >
2013/3/6 Paolo Bonzini <pbonzini@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 <dantesu@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 <dantesu@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 <dantesu@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
Il 07/03/2013 07:39, Kuo-Jung Su ha scritto: > 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 It's much simpler than that. You can look at commit f91837a7f173e0334539024e4d8ba10c19a78bb7 for example. Paolo
> 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 From Makefile.target, qtest is built when you build softmmu target. I guess there is no need to add "--enable-qtest" option. However, I don't know how to run qtest... Stefan (Cc'ed), I think we should cleanup QTest wiki page, right? ;) Regards, chenwj
> From Makefile.target, qtest is built when you build softmmu target. > I guess there is no need to add "--enable-qtest" option. However, I > don't know how to run qtest... make check-qtest-arm Paolo
On Thu, Mar 07, 2013 at 02:41:26AM -0500, Paolo Bonzini wrote: > > > From Makefile.target, qtest is built when you build softmmu target. > > I guess there is no need to add "--enable-qtest" option. However, I > > don't know how to run qtest... > > make check-qtest-arm O.K., let me try to summarize the working flow.. $ ../configure --target-list=i386-softmmu $ make; make install $ make check-qtest-i386 $ cd tests $ export QTEST_QEMU_BINARY=/path/to/qemu-system-i386 $ ./rtc-test /i386/rtc/check-time/bcd: OK /i386/rtc/check-time/dec: OK /i386/rtc/alarm/interrupt: OK ... Developers should add their test into ${QEMU_SRC}/tests . Am I right? Regards, chenwj
2013/3/7 Paolo Bonzini <pbonzini@redhat.com>: > >> From Makefile.target, qtest is built when you build softmmu target. >> I guess there is no need to add "--enable-qtest" option. However, I >> don't know how to run qtest... > > make check-qtest-arm > > Paolo Got it, thanks.
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 <dantesu@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; +} + +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 <dantesu@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
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 <dantesu@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