From patchwork Wed Feb 6 09:45:10 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 218512 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 9760D2C0296 for ; Wed, 6 Feb 2013 20:47:12 +1100 (EST) Received: from localhost ([::1]:38320 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31aw-00029z-QM for incoming@patchwork.ozlabs.org; Wed, 06 Feb 2013 04:47:10 -0500 Received: from eggs.gnu.org ([208.118.235.92]:35166) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31al-0001ys-Pt for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:47:03 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1U31ai-00072T-JP for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:46:59 -0500 Received: from ie-in-x0233.1e100.net ([2607:f8b0:4001:c03::233]:49208 helo=mail-ie0-x233.google.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U31ai-00072K-Cu for qemu-devel@nongnu.org; Wed, 06 Feb 2013 04:46:56 -0500 Received: by mail-ie0-f179.google.com with SMTP id k11so1621271iea.24 for ; Wed, 06 Feb 2013 01:46:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=wRFH33STyIkNjsE4URoDiPt2Rn1upfVNmyIfGTki4nU=; b=kFhn7mjPXt2dpclGWwfnZTLwQX1t2eABqvIe6IYIZO7gYcb45Y+3LMuPxQ9y9vLasd 0pkcm8t/CAF5Hbq7e3bl2EhLhX3QtgpWbh6q9+L4DY1jVaTUTF5VkuYXq94VH1SlF2Sj bbCwagAB6ftwFRUsI9TAowTDpB/NpyjTrSlXEOipI5jOfWw5SJx6jq5N0RkG2Baw/7B9 SiyKOITh5tqtPPVtAORvLvXxQ5uCwMlayd/vD+D2Ie/2T2XXvtqTpimlyp/WAbHbocFp R6frPvVsuWQ6+h1C3UnmKWAUTjxmbP2nLdy6QqBV1YQOfREsfYMvFArP+P7WmH40fQMB oMCQ== X-Received: by 10.50.149.196 with SMTP id uc4mr4692314igb.74.1360144015455; Wed, 06 Feb 2013 01:46:55 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id hg2sm2671279igc.3.2013.02.06.01.46.52 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Wed, 06 Feb 2013 01:46:55 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Wed, 6 Feb 2013 17:45:10 +0800 Message-Id: <1360143925-10800-7-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1360143925-10800-1-git-send-email-dantesu@gmail.com> References: <1360143925-10800-1-git-send-email-dantesu@gmail.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:4001:c03::233 Cc: Peter Maydell , i.mitsyanko@samsung.com, Blue Swirl , Paul Brook , Kuo-Jung Su , Andreas , fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v3 06/20] arm: add Faraday FTWDT010 watchdog timer support 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 From: Kuo-Jung Su 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 --- hw/arm/Makefile.objs | 1 + hw/arm/faraday_a360.c | 3 + hw/arm/faraday_a369.c | 15 ++++ hw/arm/ftwdt010.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/arm/ftwdt010.h | 23 ++++++ 5 files changed, 247 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 189f777..625a12e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -38,3 +38,4 @@ obj-y += faraday_a369.o faraday_a369_scu.o faraday_a369_keypad.o obj-y += ftahbc020.o obj-y += ftddrii030.o obj-y += ftintc020.o +obj-y += ftwdt010.o diff --git a/hw/arm/faraday_a360.c b/hw/arm/faraday_a360.c index 2467016..8333931 100644 --- a/hw/arm/faraday_a360.c +++ b/hw/arm/faraday_a360.c @@ -55,6 +55,9 @@ a360_device_init(A360State *s) /* pmu */ sysbus_create_simple("a360.pmu", 0x98100000, NULL); + + /* ftwdt010 */ + sysbus_create_simple("ftwdt010", 0x98500000, pic[16]); } static void diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c index 77775b8..1cc7661 100644 --- a/hw/arm/faraday_a369.c +++ b/hw/arm/faraday_a369.c @@ -24,6 +24,17 @@ typedef FaradayMachState A369State; +static void +a369_board_reset(void *opaque) +{ + A369State *s = opaque; + + device_reset(s->scu); + device_reset(s->ddrc); + device_reset(s->ahbc); + cpu_reset(CPU(s->cpu)); +} + /* Board init. */ static void @@ -71,6 +82,10 @@ a369_device_init(A369State *s) qdev_prop_set_ptr(s->ddrc, "mach", s); qdev_init_nofail(s->ddrc); sysbus_mmio_map(SYS_BUS_DEVICE(s->ddrc), 0, 0x93100000); + + /* ftwdt010 */ + sysbus_create_simple("ftwdt010", 0x92200000, pic[46]); + qemu_register_reset(a369_board_reset, s); } static void diff --git a/hw/arm/ftwdt010.c b/hw/arm/ftwdt010.c new file mode 100644 index 0000000..6a61584 --- /dev/null +++ b/hw/arm/ftwdt010.c @@ -0,0 +1,205 @@ +/* + * QEMU model of the FTWDT010 WatchDog Timer + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * This file is licensed under GNU GPL v2+. + */ + +#include +#include +#include + +#include "ftwdt010.h" + +#define TYPE_FTWDT010 "ftwdt010" + +typedef struct Ftwdt010State { + SysBusDevice busdev; + MemoryRegion mmio; + + qemu_irq irq; + + QEMUTimer *qtimer; + + uint64_t timeout; + uint32_t load; + uint32_t cr; + uint32_t sr; + + uint32_t freq; /* desired source clock */ + uint32_t step; /* get_ticks_per_sec() / freq */ + int running; +} 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 rc = 0; + + switch (addr) { + case REG_COUNTER: + if (s->cr & 0x01) { + return (s->timeout - qemu_get_clock_ns(vm_clock)) / s->step; + } else { + return s->load; + } + case REG_LOAD: + return s->load; + case REG_CR: + return s->cr; + case REG_SR: + return s->sr; + case REG_REV: + return 0x00010601; + default: + break; + } + + return rc; +} + +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 & 0x01) && (val == 0x5ab9)) { + s->timeout = (uint64_t)s->step * (uint64_t)s->load + + qemu_get_clock_ns(vm_clock); + qemu_mod_timer(s->qtimer, s->timeout); + } + break; + case REG_CR: + s->cr = (uint32_t)val; + if (s->cr & 0x01) { + if (!s->running) { + s->running = 1; + s->timeout = (uint64_t)s->step * (uint64_t)s->load + + qemu_get_clock_ns(vm_clock); + qemu_mod_timer(s->qtimer, s->timeout); + } + } else { + s->running = 0; + qemu_del_timer(s->qtimer); + } + break; + case REG_SCR: + s->sr &= ~(uint32_t)(val & 0x01); + break; + default: + break; + } +} + +static const MemoryRegionOps ftwdt010_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 = 1; + + /* send interrupt signal */ + qemu_set_irq(s->irq, (s->cr & (1 << 2)) ? 1 : 0); + + /* send system reset */ + if (s->cr & (1 << 1)) { + qemu_system_reset_request(); + } +} + +static void ftwdt010_reset(DeviceState *ds) +{ + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); + Ftwdt010State *s = FTWDT010(FROM_SYSBUS(Ftwdt010State, busdev)); + + s->cr = 0; + s->sr = 0; + s->load = 0x3ef1480; + s->timeout = 0; +} + +static int ftwdt010_init(SysBusDevice *dev) +{ + Ftwdt010State *s = FTWDT010(FROM_SYSBUS(Ftwdt010State, dev)); + + s->step = (uint64_t)get_ticks_per_sec() / (uint64_t)s->freq; + s->qtimer = qemu_new_timer_ns(vm_clock, ftwdt010_timer_tick, s); + + memory_region_init_io(&s->mmio, + &ftwdt010_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_UINT32(freq, Ftwdt010State), + VMSTATE_UINT32(step, Ftwdt010State), + VMSTATE_UINT32(load, Ftwdt010State), + VMSTATE_UINT32(cr, Ftwdt010State), + VMSTATE_UINT32(sr, Ftwdt010State), + VMSTATE_END_OF_LIST() + } +}; + +static Property ftwdt010_properties[] = { + DEFINE_PROP_UINT32("freq", Ftwdt010State, freq, 66000000), + 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..1cc5a8f --- /dev/null +++ b/hw/arm/ftwdt010.h @@ -0,0 +1,23 @@ +/* + * QEMU model of the FTWDT010 WatchDog Timer + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * This file is licensed under GNU GPL v2+. + */ + +#ifndef HW_ARM_FTWDT010_H +#define HW_ARM_FTWDT010_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_REV 0x1C + +#endif