Patchwork [RFC,3/7] cadence_wdt: first revision

login
register
mail settings
Submitter Peter A. G. Crosthwaite
Date Jan. 23, 2012, 7:20 a.m.
Message ID <fdc2a47ef085966994a3c83227b09d1963a2b89f.1327302677.git.peter.crosthwaite@petalogix.com>
Download mbox | patch
Permalink /patch/137288/
State New
Headers show

Comments

Peter A. G. Crosthwaite - Jan. 23, 2012, 7:20 a.m.
Device model for cadence watchdog timer (WDT)

Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
---
 Makefile.target  |    1 +
 hw/cadence_wdt.c |  260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 261 insertions(+), 0 deletions(-)
 create mode 100644 hw/cadence_wdt.c
John Linn - Jan. 24, 2012, 2:03 a.m.
> -----Original Message-----
> From: Peter A. G. Crosthwaite [mailto:peter.crosthwaite@petalogix.com]
> Sent: Sunday, January 22, 2012 11:21 PM
> To: qemu-devel@nongnu.org; monstr@monstr.eu;
> john.williams@petalogix.com; peter.crosthwaite@petalogix.com;
> edgar.iglesias@petalogix.com; Duy Le; John Linn
> Subject: [RFC PATCH 3/7] cadence_wdt: first revision
> 
> Device model for cadence watchdog timer (WDT)
> 
> Signed-off-by: Peter A. G. Crosthwaite
> <peter.crosthwaite@petalogix.com>

Signed-off-by: John Linn <john.linn@xilinx.com>

*** My apologies, please ignore the legal footer below which I'm working
on getting removed ***



This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

Patch

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);