Il 06/03/2013 08:27, Kuo-Jung Su ha scritto: > The FTWDT010 is used to prevent system from infinite loop > while software gets trapped in the deadlock. > > Under the normal operation, users should restart FTWDT010 > at the regular intervals before counter counts down to 0. > > If the counter does reach 0, FTWDT010 will try to reset > the system by generating one or a combination of signals, > system reset, system interrupt, and external interrupt. > > Signed-off-by: Kuo-Jung Su <dant...@gmail.com> > --- > hw/arm/Makefile.objs | 1 + > hw/arm/faraday_a369_soc.c | 23 +++++ > hw/arm/ftwdt010.c | 212 > +++++++++++++++++++++++++++++++++++++++++++++ > hw/arm/ftwdt010.h | 35 ++++++++ > 4 files changed, 271 insertions(+) > create mode 100644 hw/arm/ftwdt010.c > create mode 100644 hw/arm/ftwdt010.h > > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs > index 2190edd..bc8e2de 100644 > --- a/hw/arm/Makefile.objs > +++ b/hw/arm/Makefile.objs > @@ -41,3 +41,4 @@ obj-y += ftintc020.o > obj-y += ftahbc020.o > obj-y += ftddrii030.o > obj-y += ftpwmtmr010.o > +obj-y += ftwdt010.o > diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c > index 66d9891..1bf64d4 100644 > --- a/hw/arm/faraday_a369_soc.c > +++ b/hw/arm/faraday_a369_soc.c > @@ -68,6 +68,25 @@ static void a369soc_reset(DeviceState *ds) > } > > static void > +a369soc_system_reset(void *opaque) > +{ > + FaradaySoCState *s = FARADAY_SOC(opaque); > + > + if (s->scu) { > + device_reset(s->scu); > + } > + if (s->ddrc) { > + device_reset(s->ddrc); > + } > + if (s->ahbc) { > + device_reset(s->ahbc); > + } > + if (s->cpu) { > + cpu_reset(CPU(s->cpu)); > + } > +}
Why is this needed? Aren't they called already by qemu_register_reset(qbus_reset_all_fn, sysbus_get_default()); ? > a369soc_device_init(FaradaySoCState *s) > { > DriveInfo *dinfo; > @@ -166,6 +185,10 @@ a369soc_device_init(FaradaySoCState *s) > sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[9]); > sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[10]); > sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[11]); > + > + /* ftwdt010 */ > + sysbus_create_simple("ftwdt010", 0x92200000, s->pic[46]); > + qemu_register_reset(a369soc_system_reset, s); > } > > static int a369soc_init(SysBusDevice *dev) > diff --git a/hw/arm/ftwdt010.c b/hw/arm/ftwdt010.c > new file mode 100644 > index 0000000..361f16e > --- /dev/null > +++ b/hw/arm/ftwdt010.c > @@ -0,0 +1,212 @@ > +/* > + * QEMU model of the FTWDT010 WatchDog 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 "sysemu/sysemu.h" > +#include "qemu/timer.h" > + > +#include "faraday.h" > +#include "ftwdt010.h" > + > +#define TYPE_FTWDT010 "ftwdt010" > + > +typedef struct Ftwdt010State { > + SysBusDevice busdev; > + MemoryRegion mmio; > + > + qemu_irq irq; > + > + QEMUTimer *qtimer; > + > + uint64_t timeout; > + uint64_t freq; /* desired source clock */ > + uint64_t step; /* get_ticks_per_sec() / freq */ > + bool running; > + > + /* HW register cache */ > + uint32_t load; > + uint32_t cr; > + uint32_t sr; > +} Ftwdt010State; > + > +#define FTWDT010(obj) \ > + OBJECT_CHECK(Ftwdt010State, obj, TYPE_FTWDT010) > + > +static uint64_t > +ftwdt010_mem_read(void *opaque, hwaddr addr, unsigned size) > +{ > + Ftwdt010State *s = FTWDT010(opaque); > + uint32_t ret = 0; > + > + switch (addr) { > + case REG_COUNTER: > + if (s->cr & CR_EN) { > + ret = s->timeout - qemu_get_clock_ms(rt_clock); > + ret = MIN(s->load, ret * 1000000ULL / s->step); > + } else { > + ret = s->load; > + } > + break; > + case REG_LOAD: > + return s->load; > + case REG_CR: > + return s->cr; > + case REG_SR: > + return s->sr; > + case REG_REVR: > + return 0x00010601; /* rev. 1.6.1 */ > + default: > + qemu_log_mask(LOG_GUEST_ERROR, > + "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr); > + break; > + } > + > + return ret; > +} > + > +static void > +ftwdt010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) > +{ > + Ftwdt010State *s = FTWDT010(opaque); > + > + switch (addr) { > + case REG_LOAD: > + s->load = (uint32_t)val; > + break; > + case REG_RESTART: > + if ((s->cr & CR_EN) && (val == WDT_MAGIC)) { > + s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL; > + s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1); > + qemu_mod_timer(s->qtimer, s->timeout); > + } > + break; > + case REG_CR: > + s->cr = (uint32_t)val; > + if (s->cr & CR_EN) { > + if (s->running) { > + break; > + } > + s->running = true; > + s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL; > + s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1); > + qemu_mod_timer(s->qtimer, s->timeout); > + } else { > + s->running = false; > + qemu_del_timer(s->qtimer); > + } > + break; > + case REG_SCR: > + s->sr &= ~(uint32_t)val; > + break; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, > + "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr); > + break; > + } > +} > + > +static const MemoryRegionOps mmio_ops = { > + .read = ftwdt010_mem_read, > + .write = ftwdt010_mem_write, > + .endianness = DEVICE_LITTLE_ENDIAN, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 4 > + } > +}; > + > +static void ftwdt010_timer_tick(void *opaque) > +{ > + Ftwdt010State *s = FTWDT010(opaque); > + > + s->sr = SR_SRST; > + > + /* send interrupt signal */ > + qemu_set_irq(s->irq, (s->cr & CR_INTR) ? 1 : 0); > + > + /* send system reset */ > + if (s->cr & CR_SRST) { > + qemu_system_reset_request(); Please use watchdog_perform_action() instead. Paolo > + } > +} > + > +static void ftwdt010_reset(DeviceState *ds) > +{ > + Ftwdt010State *s = FTWDT010(SYS_BUS_DEVICE(ds)); > + > + s->cr = 0; > + s->sr = 0; > + s->load = 0x3ef1480; > + s->timeout = 0; > +} > + > +static int ftwdt010_init(SysBusDevice *dev) > +{ > + Ftwdt010State *s = FTWDT010(dev); > + > + s->step = (uint64_t)get_ticks_per_sec() / s->freq; > + s->qtimer = qemu_new_timer_ms(rt_clock, ftwdt010_timer_tick, s); > + > + memory_region_init_io(&s->mmio, > + &mmio_ops, > + s, > + TYPE_FTWDT010, > + 0x1000); > + sysbus_init_mmio(dev, &s->mmio); > + sysbus_init_irq(dev, &s->irq); > + > + return 0; > +} > + > +static const VMStateDescription vmstate_ftwdt010 = { > + .name = TYPE_FTWDT010, > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT64(timeout, Ftwdt010State), > + VMSTATE_UINT64(freq, Ftwdt010State), > + VMSTATE_UINT64(step, Ftwdt010State), > + VMSTATE_UINT32(load, Ftwdt010State), > + VMSTATE_UINT32(cr, Ftwdt010State), > + VMSTATE_UINT32(sr, Ftwdt010State), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static Property ftwdt010_properties[] = { > + DEFINE_PROP_UINT64("freq", Ftwdt010State, freq, 66000000ULL), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void ftwdt010_class_init(ObjectClass *klass, void *data) > +{ > + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + k->init = ftwdt010_init; > + dc->vmsd = &vmstate_ftwdt010; > + dc->props = ftwdt010_properties; > + dc->reset = ftwdt010_reset; > + dc->no_user = 1; > +} > + > +static const TypeInfo ftwdt010_info = { > + .name = TYPE_FTWDT010, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(Ftwdt010State), > + .class_init = ftwdt010_class_init, > +}; > + > +static void ftwdt010_register_types(void) > +{ > + type_register_static(&ftwdt010_info); > +} > + > +type_init(ftwdt010_register_types) > diff --git a/hw/arm/ftwdt010.h b/hw/arm/ftwdt010.h > new file mode 100644 > index 0000000..46c0871 > --- /dev/null > +++ b/hw/arm/ftwdt010.h > @@ -0,0 +1,35 @@ > +/* > + * QEMU model of the FTWDT010 WatchDog 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_FTWDT010_H > +#define HW_ARM_FTWDT010_H > + > +#include "qemu/bitops.h" > + > +/* Hardware registers */ > +#define REG_COUNTER 0x00 /* counter register */ > +#define REG_LOAD 0x04 /* (re)load register */ > +#define REG_RESTART 0x08 /* restart register */ > +#define REG_CR 0x0C /* control register */ > +#define REG_SR 0x10 /* status register */ > +#define REG_SCR 0x14 /* status clear register */ > +#define REG_INTR_LEN 0x18 /* interrupt length register */ > +#define REG_REVR 0x1C /* revision register */ > + > +#define CR_CLKS BIT(4) /* clock source */ > +#define CR_ESIG BIT(3) /* external signal enabled */ > +#define CR_INTR BIT(2) /* system reset interrupt enabled */ > +#define CR_SRST BIT(1) /* system reset enabled */ > +#define CR_EN BIT(0) /* chip enabled */ > + > +#define SR_SRST BIT(1) /* system reset */ > + > +#define WDT_MAGIC 0x5ab9 /* magic for watchdog restart */ > + > +#endif >