On 18 January 2017 at 16:55, Cédric Le Goater <c...@kaod.org> wrote:
> From: Joel Stanley <j...@jms.id.au>
>
> The Aspeed SoC includes a set of watchdog timers using 32-bit
> decrement counters, which can be based either on the APB clock or
> a 1 MHz clock.
>
> The watchdog timer is designed to prevent system deadlock and, in
> general, it should be restarted before timeout. When a timeout occurs,
> different types of signals can be generated, ARM reset, SOC reset,
> System reset, CPU Interrupt, external signal or boot from alternate
> block. The current model only performs the system reset function as
> this is used by U-Boot and Linux.
>
> Signed-off-by: Joel Stanley <j...@jms.id.au>
> [clg: - fixed compile breakage
>       - fixed io region size
>       - added watchdog_perform_action() on timer expiry
>       - wrote a commit log
>       - merged fixes from Andrew Jeffery to scale the reload value ]
> Signed-off-by: Cédric Le Goater <c...@kaod.org>
> ---
>  hw/watchdog/Makefile.objs        |   1 +
>  hw/watchdog/wdt_aspeed.c         | 214 
> +++++++++++++++++++++++++++++++++++++++
>  include/hw/watchdog/wdt_aspeed.h |  37 +++++++
>  3 files changed, 252 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..96e62c54dc04
> --- /dev/null
> +++ b/hw/watchdog/wdt_aspeed.c
> @@ -0,0 +1,214 @@
> +/*
> + * ASPEED Watchdog Controller
> + *
> + * Copyright (C) 2016-2017 IBM Corp.
> + *
> + * 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      0x20
> +
> +#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
> +
> +#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;
> +    }
> +
> +}
> +
> +#define PCLK_HZ 24000000
> +
> +static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data,
> +                             unsigned size)
> +{
> +    AspeedWDTState *s = ASPEED_WDT(opaque);
> +    bool en = data & BIT(0);
> +    bool pclk = !(data & BIT(4));

These are only valid for WDT_CTRL, right? That being so,
initialising them up here is a bit odd.

> +
> +    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) {
> +            uint32_t reload;
> +
> +            s->reg_status = s->reg_reload_value;
> +
> +            if (pclk) {
> +                reload = muldiv64(s->reg_reload_value, 
> NANOSECONDS_PER_SECOND,
> +                                  PCLK_HZ) ;
> +            } else {
> +                reload = s->reg_reload_value * 1000;
> +            }
> +
> +            if (s->enabled) {
> +                timer_mod(s->timer,
> +                          qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload);
> +            }
> +        }
> +        break;
> +    case WDT_CTRL:
> +        if (en && !s->enabled) {
> +            uint32_t reload;
> +
> +            if (pclk) {
> +                reload = muldiv64(s->reg_reload_value, 
> NANOSECONDS_PER_SECOND,
> +                                  PCLK_HZ);
> +            } else {
> +                reload = s->reg_reload_value * 1000;
> +            }
> +
> +            s->enabled = true;
> +            timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 
> reload);
> +        } else if (!en && s->enabled) {
> +            s->enabled = false;
> +            timer_del(s->timer);
> +        }
> +        break;

Shouldn't this write to s->reg_ctrl ?

s->enabled seems to duplicate state also in s->reg_ctrl.

> +    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),

This doesn't seem to have fields for most of the register state.

> +        VMSTATE_END_OF_LIST()
> +    }
> +};

thanks
-- PMM

Reply via email to