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


Reply via email to