Device model for cadence watchdog timer (WDT) Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwa...@petalogix.com> --- Makefile.target | 1 + hw/cadence_wdt.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+), 0 deletions(-) create mode 100644 hw/cadence_wdt.c
diff --git a/Makefile.target b/Makefile.target index 44ba41b..c7abcde 100644 --- a/Makefile.target +++ b/Makefile.target @@ -336,6 +336,7 @@ obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o obj-arm-y += versatile_pci.o obj-arm-y += cadence_uart.o obj-arm-y += cadence_ttc.o +obj-arm-y += cadence_wdt.o obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-arm-y += arm_l2x0.o obj-arm-y += arm_mptimer.o diff --git a/hw/cadence_wdt.c b/hw/cadence_wdt.c new file mode 100644 index 0000000..c153d62 --- /dev/null +++ b/hw/cadence_wdt.c @@ -0,0 +1,260 @@ +/* + * Cadence System Watchdog Timer module. + * + * Copyright (c) 2010 Xilinx Inc. + * Written by M. Habib + * + * 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. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA + * 02139, USA. + */ + +#include <inttypes.h> +#include "qemu-common.h" +#include "qemu-timer.h" +#include "watchdog.h" +#include "hw.h" +#include "sysbus.h" + +#define CADENCE_WDT_ZKEY 0x00000ABC +#define CADENCE_WDT_CKEY 0x00000248 +#define CADENCE_WDT_RKEY 0x00001999 +#define CADENCE_WDT_ZVAL 0x00000FFF +#define CADENCE_WDT_CVAL 0x0000003F + +#define CADENCE_WDT_ZKEY_SHT 12 +#define CADENCE_WDT_CKEY_SHT 6 + +#define CADENCE_WDT_ENABLE_TIMER 0x00000001 +#define CADENCE_WDT_ENABLE_RESET 0x00000002 +#define CADENCE_WDT_ENABLE_INTR 0x00000004 +#define CADENCE_WDT_ENABLE_EXTNL 0x00000008 + +#define CADENCE_WDT_RST_LEN 0x00000070 +#define CADENCE_WDT_INT_LEN 0x00000180 +#define CADENCE_WDT_SIG_LEN 0x00000E00 + +#define CADENCE_WDT_RST_LEN 0x00000070 +#define CADENCE_WDT_INT_LEN 0x00000180 +#define CADENCE_WDT_SIG_LEN 0x00000E00 + +#define CADENCE_WDT_RST_SHT 4 +#define CADENCE_WDT_INT_SHT 7 +#define CADENCE_WDT_SIG_SHT 9 + +#define CADENCE_WDT_CCLOCK_LEN 0x00000003 +#define CADENCE_WDT_RSTART_VAL 0x0000003C +#define CADENCE_WDT_RSTART_CNS 0x00000FFF + +#define CADENCE_WDT_RSTART_SHT 10 + +typedef struct { + uint32_t zero_mode; + uint32_t counter_ctrl; + uint32_t status; + int enabled; + int reset; + int intr; + int extr; + uint32_t timer_preload; + uint32_t clock_prescale; + uint32_t reset_len; + uint32_t intr_len; + uint32_t signal_len; + QEMUTimer *timer; +} cadence_watchdog_timer; + +typedef struct cadence_wdt_state { + SysBusDevice busdev; + MemoryRegion iomem; + cadence_watchdog_timer *timer; +} cadence_wdt_state; + +static void cadence_wdt_restart_timer(cadence_watchdog_timer *s) +{ + int64_t timeout = 0; + int64_t clock_scale = 0x00000008; + + if (!s->enabled) + return; + + timeout = s->timer_preload << (clock_scale << s->clock_prescale); + + timeout = (int64_t) (get_ticks_per_sec() * (timeout / 2500000)); + + qemu_mod_timer(s->timer, qemu_get_clock_ns(vm_clock) + timeout); +} + +static void cadence_wdt_disable_timer(cadence_watchdog_timer *s) +{ + qemu_del_timer(s->timer); +} + +static void cadence_wdt_reset(cadence_watchdog_timer *s) +{ + cadence_wdt_disable_timer(s); +} + +static void cadence_wdt_timer_expired(void *opaque) +{ + cadence_watchdog_timer *s = (cadence_watchdog_timer *) opaque; + + s->status = 0; + + if ((s->enabled) && (s->reset)) { + watchdog_perform_action(); + cadence_wdt_reset(s); + } +} + +static void cadence_wdt_cctrl_update(cadence_watchdog_timer *s , uint32_t value) +{ + /* get counter clock prescalar */ + s->clock_prescale = (value & CADENCE_WDT_CCLOCK_LEN); + + /* get restart value */ + s->timer_preload = (((value & CADENCE_WDT_RSTART_VAL) << + CADENCE_WDT_RSTART_SHT) | CADENCE_WDT_RSTART_CNS); +} + +static void cadence_wdt_zmode_update(cadence_watchdog_timer *s , uint32_t value) +{ + + if (value & CADENCE_WDT_ENABLE_TIMER) { + s->enabled = 1; + } + else { + s->enabled = 0; + } + + if (value & CADENCE_WDT_ENABLE_RESET) { + s->reset = 1; + } + else + s->reset = 0; + + if (value & CADENCE_WDT_ENABLE_INTR) { + s->intr = 1; + } + else + s->intr = 0; + + if (value & CADENCE_WDT_ENABLE_EXTNL) { + s->extr = 1; + } + else + s->extr = 0; + + /* get output time lengths */ + s->reset_len = ((value & CADENCE_WDT_RST_LEN) >> CADENCE_WDT_RST_SHT); + s->intr_len = ((value & CADENCE_WDT_INT_LEN) >> CADENCE_WDT_INT_SHT); + s->signal_len = ((value & CADENCE_WDT_SIG_LEN) >> CADENCE_WDT_SIG_SHT); +} + +static uint64_t cadence_wdt_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + cadence_watchdog_timer *s = (cadence_watchdog_timer *) opaque; + + switch (offset) { + case 0x00: + return s->zero_mode; + case 0x04: + return s->counter_ctrl; + case 0x0c: + return s->status; + default: + return 0; + } +} + +static void cadence_wdt_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + cadence_watchdog_timer *s = (cadence_watchdog_timer *) opaque; + uint32_t tmp = value; + + switch (offset) { + case 0x0: + tmp = value >> CADENCE_WDT_ZKEY_SHT; + if (tmp == CADENCE_WDT_ZKEY) { + value &= CADENCE_WDT_ZVAL; + s->zero_mode = value; + cadence_wdt_zmode_update(s , value); + } + break; + case 0x04: + tmp = value >> CADENCE_WDT_CKEY_SHT; + if (tmp == CADENCE_WDT_CKEY) { + value &= CADENCE_WDT_CVAL; + s->counter_ctrl = value; + cadence_wdt_cctrl_update(s , value); + } + break; + case 0x08: + if (tmp == CADENCE_WDT_RKEY) { + cadence_wdt_restart_timer (s); + } + + break; + default: + break; + } + return; +} + +static const MemoryRegionOps cadence_wdt_ops = { + .read = cadence_wdt_read, + .write = cadence_wdt_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int cadence_wdt_init(SysBusDevice *dev) +{ + cadence_wdt_state *d = FROM_SYSBUS(cadence_wdt_state, dev); + cadence_watchdog_timer *s; + + s = (cadence_watchdog_timer *)g_malloc0(sizeof(cadence_watchdog_timer)); + + s->zero_mode = 0x000001c3; + + s->counter_ctrl = 0x0000003c; + s->status = 0x00000000; + + s->enabled = 1; + s->reset = 1; + s->intr = 0; + s->extr = 0; + s->timer_preload = 0xffff; + s->clock_prescale = 0x0000; + s->reset_len = 0x0004; + s->intr_len = 0x0003; + s->signal_len = 0x0000; + + s->timer = qemu_new_timer_ns(vm_clock, cadence_wdt_timer_expired, s); + d->timer = s; + memory_region_init_io(&d->iomem, &cadence_wdt_ops, s, "wdt", 0x1000); + sysbus_init_mmio(dev, &d->iomem); + + return 0; +} + +static WatchdogTimerModel model = { + .wdt_name = "cadence_wdt", + .wdt_description = "cadence SWDT for Pele", +}; + +static void cadence_wdt_register_devices(void) +{ + watchdog_add_model(&model); + sysbus_register_dev("cadence_wdt", sizeof(struct cadence_wdt_state), + cadence_wdt_init); +} + +device_init(cadence_wdt_register_devices); -- 1.7.3.2