From patchwork Mon Jan 23 07:20:30 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Peter A. G. Crosthwaite" X-Patchwork-Id: 137288 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 33A7AB6F71 for ; Mon, 23 Jan 2012 19:33:26 +1100 (EST) Received: from localhost ([::1]:34168 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RpFL8-0004AI-9X for incoming@patchwork.ozlabs.org; Mon, 23 Jan 2012 03:33:22 -0500 Received: from eggs.gnu.org ([140.186.70.92]:45023) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RpEBv-0000xm-Gn for qemu-devel@nongnu.org; Mon, 23 Jan 2012 02:19:48 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RpEBu-0004VD-39 for qemu-devel@nongnu.org; Mon, 23 Jan 2012 02:19:47 -0500 Received: from mail-gx0-f173.google.com ([209.85.161.173]:47015) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RpEBt-0004U2-TG for qemu-devel@nongnu.org; Mon, 23 Jan 2012 02:19:45 -0500 Received: by mail-gx0-f173.google.com with SMTP id r1so585260ggn.4 for ; Sun, 22 Jan 2012 23:19:45 -0800 (PST) MIME-Version: 1.0 Received: by 10.101.210.30 with SMTP id m30mr2665797anq.64.1327303185661; Sun, 22 Jan 2012 23:19:45 -0800 (PST) Received: from localhost ([124.148.20.9]) by mx.google.com with ESMTPS id n72sm16504909yhh.21.2012.01.22.23.19.40 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 22 Jan 2012 23:19:44 -0800 (PST) From: "Peter A. G. Crosthwaite" To: qemu-devel@nongnu.org, monstr@monstr.eu, john.williams@petalogix.com, peter.crosthwaite@petalogix.com, edgar.iglesias@petalogix.com, duyl@xilinx.com, linnj@xilinx.com Date: Mon, 23 Jan 2012 17:20:30 +1000 Message-Id: X-Mailer: git-send-email 1.7.3.2 In-Reply-To: References: In-Reply-To: References: X-Gm-Message-State: ALoCoQn0gnQhaDbJJOJwOL4yvOpfvkaUwikN6ORrAUkzXEzOED+/zaVEVrdjjogGTJ6VfatayLpE X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.161.173 X-Mailman-Approved-At: Mon, 23 Jan 2012 03:32:08 -0500 Subject: [Qemu-devel] [RFC PATCH 3/7] cadence_wdt: first revision X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Device model for cadence watchdog timer (WDT) Signed-off-by: Peter A. G. Crosthwaite Signed-off-by: John Linn --- 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 +#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);