[PATCH qemu 30/38] wdt: Add Aspeed watchdog device model
Cédric Le Goater
clg at kaod.org
Tue Nov 22 00:03:31 AEDT 2016
On 11/18/2016 03:22 PM, Cédric Le Goater wrote:
> From: Joel Stanley <joel at jms.id.au>
>
> Signed-off-by: Joel Stanley <joel at jms.id.au>
> [clg: fixed compile breakage
> fixed io region size
> added watchdog_perform_action() on timer expiry ]
> Signed-off-by: Cédric Le Goater <clg at kaod.org>
> ---
> hw/watchdog/Makefile.objs | 1 +
> hw/watchdog/wdt_aspeed.c | 194 +++++++++++++++++++++++++++++++++++++++
> include/hw/watchdog/wdt_aspeed.h | 41 +++++++++
> 3 files changed, 236 insertions(+)
> create mode 100644 hw/watchdog/wdt_aspeed.c
> create mode 100644 include/hw/watchdog/wdt_aspeed.h
>
> diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs
> index 72e3ffd93c59..9589bed63a3d 100644
> --- a/hw/watchdog/Makefile.objs
> +++ b/hw/watchdog/Makefile.objs
> @@ -2,3 +2,4 @@ common-obj-y += watchdog.o
> common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
> common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
> common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o
> +common-obj-$(CONFIG_ASPEED_SOC) += wdt_aspeed.o
> diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c
> new file mode 100644
> index 000000000000..29d9d7f91463
> --- /dev/null
> +++ b/hw/watchdog/wdt_aspeed.c
> @@ -0,0 +1,194 @@
> +/*
> + * Copyright 2016 IBM Corporation
> + *
> + * This code is licensed under the GPL version 2 or later. See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "sysemu/watchdog.h"
> +#include "hw/sysbus.h"
> +#include "qemu/timer.h"
> +#include "hw/watchdog/wdt_aspeed.h"
> +
> +#define WDT_IO_REGION_SIZE 0x1000
> +
> +#define WDT_STATUS 0x00
> +#define WDT_RELOAD_VALUE 0x04
> +#define WDT_RESTART 0x08
> +#define WDT_CTRL 0x0C
> +#define WDT_TIMEOUT_STATUS 0x10
> +#define WDT_TIMEOUT_CLEAR 0x14
> +#define WDT_RESET_WDITH 0x18
May be we should reduce the size of WDT_IO_REGION_SIZE to 0x20
to instantiate the three watchdog of the Aspeed AST2500 with
one class ?
Here is the layout :
WDT00: WDT1 Counter Status Register
WDT04: WDT1 Counter Reload Value Register
WDT08: WDT1 Counter Restart Register
WDT0C: WDT1 Control Register
WDT10: WDT1 Timeout Status Register
WDT14: WDT1 Clear Timeout Status Register
WDT18: WDT1 Reset Width Register
WDT1C: WDT1 Reset Mask Register (AST2500)
WDT20: WDT2 Counter Status Register
WDT24: WDT2 Counter Reload Value Register
WDT28: WDT2 Counter Restart Register
WDT2C: WDT2 Control Register
WDT30: WDT2 Timeout Status Register
WDT34: WDT2 Clear Timeout Status Register
WDT38: WDT2 Reset Width Register
WDT3C: WDT2 Reset Mask Register (AST2500)
WDT40: WDT3 Counter Status Register (AST2500)
WDT44: WDT3 Counter Reload Value Register (AST2500)
WDT48: WDT3 Counter Restart Register (AST2500)
WDT4C: WDT3 Control Register (AST2500)
WDT50: WDT3 Timeout Status Register (AST2500)
WDT54: WDT3 Clear Timeout Status Register (AST2500)
WDT5C: WDT3 Reset Mask Register (AST2500)
C.
> +#define WDT_RESTART_MAGIC 0x4755
> +
> +static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size)
> +{
> + AspeedWDTState *s = ASPEED_WDT(opaque);
> +
> + switch (offset) {
> + case WDT_STATUS:
> + return s->reg_status;
> + case WDT_RELOAD_VALUE:
> + return s->reg_reload_value;
> + case WDT_RESTART:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: read from write-only reg at offset 0x%"
> + HWADDR_PRIx "\n", __func__, offset);
> + return 0;
> + case WDT_CTRL:
> + return s->reg_ctrl;
> + case WDT_TIMEOUT_STATUS:
> + case WDT_TIMEOUT_CLEAR:
> + case WDT_RESET_WDITH:
> + qemu_log_mask(LOG_UNIMP,
> + "%s: uninmplemented read at offset 0x%" HWADDR_PRIx "\n",
> + __func__, offset);
> + return 0;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
> + __func__, offset);
> + return 0;
> + }
> +
> +}
> +
> +static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data,
> + unsigned size)
> +{
> + AspeedWDTState *s = ASPEED_WDT(opaque);
> + bool en = data & BIT(0);
> +
> + switch (offset) {
> + case WDT_STATUS:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: write to read-only reg at offset 0x%"
> + HWADDR_PRIx "\n", __func__, offset);
> + break;
> + case WDT_RELOAD_VALUE:
> + s->reg_reload_value = data;
> + break;
> + case WDT_RESTART:
> + if ((data & 0xFFFF) == 0x4755) {
> + s->reg_status = s->reg_reload_value;
> +
> + if (s->enabled) {
> + timer_mod(s->timer,
> + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> + s->reg_reload_value * NANOSECONDS_PER_SECOND);
> + }
> + }
> + break;
> + case WDT_CTRL:
> + if (en && !s->enabled) {
> + s->enabled = true;
> + timer_mod(s->timer,
> + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> + s->reg_reload_value * NANOSECONDS_PER_SECOND);
> + } else if (!en && s->enabled) {
> + s->enabled = false;
> + timer_del(s->timer);
> + }
> + break;
> + case WDT_TIMEOUT_STATUS:
> + case WDT_TIMEOUT_CLEAR:
> + case WDT_RESET_WDITH:
> + qemu_log_mask(LOG_UNIMP,
> + "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n",
> + __func__, offset);
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
> + __func__, offset);
> + }
> + return;
> +}
> +
> +static WatchdogTimerModel model = {
> + .wdt_name = TYPE_ASPEED_WDT,
> + .wdt_description = "aspeed watchdog device",
> +};
> +
> +static const VMStateDescription vmstate_aspeed_wdt = {
> + .name = "vmstate_aspeed_wdt",
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .fields = (VMStateField[]) {
> + VMSTATE_TIMER_PTR(timer, AspeedWDTState),
> + VMSTATE_BOOL(enabled, AspeedWDTState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static const MemoryRegionOps aspeed_wdt_ops = {
> + .read = aspeed_wdt_read,
> + .write = aspeed_wdt_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid.min_access_size = 4,
> + .valid.max_access_size = 4,
> + .valid.unaligned = false,
> +};
> +
> +static void aspeed_wdt_reset(DeviceState *dev)
> +{
> + AspeedWDTState *s = ASPEED_WDT(dev);
> +
> + s->reg_status = 0x3EF1480;
> + s->reg_reload_value = 0x03EF1480;
> + s->reg_restart = 0;
> + s->reg_ctrl = 0;
> +
> + s->enabled = false;
> + timer_del(s->timer);
> +}
> +
> +static void aspeed_wdt_timer_expired(void *dev)
> +{
> + AspeedWDTState *s = ASPEED_WDT(dev);
> +
> + qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
> + watchdog_perform_action();
> + timer_del(s->timer);
> +}
> +
> +static void aspeed_wdt_realize(DeviceState *dev, Error **errp)
> +{
> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> + AspeedWDTState *s = ASPEED_WDT(dev);
> +
> + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired,
> + dev);
> +
> + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s,
> + TYPE_ASPEED_WDT, WDT_IO_REGION_SIZE);
> + sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static void aspeed_wdt_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->realize = aspeed_wdt_realize;
> + dc->reset = aspeed_wdt_reset;
> + set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> + dc->vmsd = &vmstate_aspeed_wdt;
> +}
> +
> +static const TypeInfo aspeed_wdt_info = {
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .name = TYPE_ASPEED_WDT,
> + .instance_size = sizeof(AspeedWDTState),
> + .class_init = aspeed_wdt_class_init,
> +};
> +
> +static void wdt_aspeed_register_types(void)
> +{
> + watchdog_add_model(&model);
> + type_register_static(&aspeed_wdt_info);
> +}
> +
> +type_init(wdt_aspeed_register_types)
> diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h
> new file mode 100644
> index 000000000000..dbf45ae968db
> --- /dev/null
> +++ b/include/hw/watchdog/wdt_aspeed.h
> @@ -0,0 +1,41 @@
> +/*
> + * ASPEED Watchdog Controller
> + *
> + * Copyright (C) 2016 IBM Corp.
> + *
> + * This code is licensed under the GPL version 2 or later. See the
> + * COPYING file in the top-level directory.
> + */
> +#ifndef ASPEED_WDT_H
> +#define ASPEED_WDT_H
> +
> +#include "hw/sysbus.h"
> +
> +#define TYPE_ASPEED_WDT "aspeed.wdt"
> +#define ASPEED_WDT(obj) \
> + OBJECT_CHECK(AspeedWDTState, (obj), TYPE_ASPEED_WDT)
> +#define ASPEED_WDT_CLASS(klass) \
> + OBJECT_CLASS_CHECK(AspeedWDTClass, (klass), TYPE_ASPEED_WDT)
> +#define ASPEED_WDT_GET_CLASS(obj) \
> + OBJECT_GET_CLASS(AspeedWDTClass, (obj), TYPE_ASPEED_WDT)
> +
> +#define WDT_ASPEED_INIT 0
> +#define WDT_ASPEED_CHANGE 1
> +#define WDT_ASPEED_CANCEL 2
> +
> +typedef struct AspeedWDTState {
> + /*< private >*/
> + SysBusDevice parent_obj;
> + QEMUTimer *timer;
> + bool enabled;
> +
> + /*< public >*/
> + MemoryRegion iomem;
> +
> + uint32_t reg_status;
> + uint32_t reg_reload_value;
> + uint32_t reg_restart;
> + uint32_t reg_ctrl;
> +} AspeedWDTState;
> +
> +#endif /* ASPEED_WDT_H */
>
More information about the openbmc
mailing list