qemu/hw/watchdog/sbsa_gwdt.c
<<
>>
Prefs
   1/*
   2 * Generic watchdog device model for SBSA
   3 *
   4 * The watchdog device has been implemented as revision 1 variant of
   5 * the ARM SBSA specification v6.0
   6 * (https://developer.arm.com/documentation/den0029/d?lang=en)
   7 *
   8 * Copyright Linaro.org 2020
   9 *
  10 * Authors:
  11 *  Shashi Mallela <shashi.mallela@linaro.org>
  12 *
  13 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
  14 * option) any later version.  See the COPYING file in the top-level directory.
  15 *
  16 */
  17
  18#include "qemu/osdep.h"
  19#include "sysemu/reset.h"
  20#include "sysemu/watchdog.h"
  21#include "hw/watchdog/sbsa_gwdt.h"
  22#include "qemu/timer.h"
  23#include "migration/vmstate.h"
  24#include "qemu/log.h"
  25#include "qemu/module.h"
  26
  27static WatchdogTimerModel model = {
  28    .wdt_name = TYPE_WDT_SBSA,
  29    .wdt_description = "SBSA-compliant generic watchdog device",
  30};
  31
  32static const VMStateDescription vmstate_sbsa_gwdt = {
  33    .name = "sbsa-gwdt",
  34    .version_id = 1,
  35    .minimum_version_id = 1,
  36    .fields = (VMStateField[]) {
  37        VMSTATE_TIMER_PTR(timer, SBSA_GWDTState),
  38        VMSTATE_UINT32(wcs, SBSA_GWDTState),
  39        VMSTATE_UINT32(worl, SBSA_GWDTState),
  40        VMSTATE_UINT32(woru, SBSA_GWDTState),
  41        VMSTATE_UINT32(wcvl, SBSA_GWDTState),
  42        VMSTATE_UINT32(wcvu, SBSA_GWDTState),
  43        VMSTATE_END_OF_LIST()
  44    }
  45};
  46
  47typedef enum WdtRefreshType {
  48    EXPLICIT_REFRESH = 0,
  49    TIMEOUT_REFRESH = 1,
  50} WdtRefreshType;
  51
  52static uint64_t sbsa_gwdt_rread(void *opaque, hwaddr addr, unsigned int size)
  53{
  54    SBSA_GWDTState *s = SBSA_GWDT(opaque);
  55    uint32_t ret = 0;
  56
  57    switch (addr) {
  58    case SBSA_GWDT_WRR:
  59        /* watch refresh read has no effect and returns 0 */
  60        ret = 0;
  61        break;
  62    case SBSA_GWDT_W_IIDR:
  63        ret = s->id;
  64        break;
  65    default:
  66        qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame read :"
  67                        " 0x%x\n", (int)addr);
  68    }
  69    return ret;
  70}
  71
  72static uint64_t sbsa_gwdt_read(void *opaque, hwaddr addr, unsigned int size)
  73{
  74    SBSA_GWDTState *s = SBSA_GWDT(opaque);
  75    uint32_t ret = 0;
  76
  77    switch (addr) {
  78    case SBSA_GWDT_WCS:
  79        ret = s->wcs;
  80        break;
  81    case SBSA_GWDT_WOR:
  82        ret = s->worl;
  83        break;
  84    case SBSA_GWDT_WORU:
  85         ret = s->woru;
  86         break;
  87    case SBSA_GWDT_WCV:
  88        ret = s->wcvl;
  89        break;
  90    case SBSA_GWDT_WCVU:
  91        ret = s->wcvu;
  92        break;
  93    case SBSA_GWDT_W_IIDR:
  94        ret = s->id;
  95        break;
  96    default:
  97        qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame read :"
  98                        " 0x%x\n", (int)addr);
  99    }
 100    return ret;
 101}
 102
 103static void sbsa_gwdt_update_timer(SBSA_GWDTState *s, WdtRefreshType rtype)
 104{
 105    uint64_t timeout = 0;
 106
 107    timer_del(s->timer);
 108
 109    if (s->wcs & SBSA_GWDT_WCS_EN) {
 110        /*
 111         * Extract the upper 16 bits from woru & 32 bits from worl
 112         * registers to construct the 48 bit offset value
 113         */
 114        timeout = s->woru;
 115        timeout <<= 32;
 116        timeout |= s->worl;
 117        timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, SBSA_TIMER_FREQ);
 118        timeout += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 119
 120        if ((rtype == EXPLICIT_REFRESH) || ((rtype == TIMEOUT_REFRESH) &&
 121                (!(s->wcs & SBSA_GWDT_WCS_WS0)))) {
 122            /* store the current timeout value into compare registers */
 123            s->wcvu = timeout >> 32;
 124            s->wcvl = timeout;
 125        }
 126        timer_mod(s->timer, timeout);
 127    }
 128}
 129
 130static void sbsa_gwdt_rwrite(void *opaque, hwaddr offset, uint64_t data,
 131                             unsigned size) {
 132    SBSA_GWDTState *s = SBSA_GWDT(opaque);
 133
 134    if (offset == SBSA_GWDT_WRR) {
 135        s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
 136
 137        sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
 138    } else {
 139        qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame write :"
 140                        " 0x%x\n", (int)offset);
 141    }
 142}
 143
 144static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data,
 145                             unsigned size) {
 146    SBSA_GWDTState *s = SBSA_GWDT(opaque);
 147
 148    switch (offset) {
 149    case SBSA_GWDT_WCS:
 150        s->wcs = data & SBSA_GWDT_WCS_EN;
 151        qemu_set_irq(s->irq, 0);
 152        sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
 153        break;
 154
 155    case SBSA_GWDT_WOR:
 156        s->worl = data;
 157        s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
 158        qemu_set_irq(s->irq, 0);
 159        sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
 160        break;
 161
 162    case SBSA_GWDT_WORU:
 163        s->woru = data & SBSA_GWDT_WOR_MASK;
 164        s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1);
 165        qemu_set_irq(s->irq, 0);
 166        sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH);
 167        break;
 168
 169    case SBSA_GWDT_WCV:
 170        s->wcvl = data;
 171        break;
 172
 173    case SBSA_GWDT_WCVU:
 174        s->wcvu = data;
 175        break;
 176
 177    default:
 178        qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame write :"
 179                " 0x%x\n", (int)offset);
 180    }
 181    return;
 182}
 183
 184static void wdt_sbsa_gwdt_reset(DeviceState *dev)
 185{
 186    SBSA_GWDTState *s = SBSA_GWDT(dev);
 187
 188    timer_del(s->timer);
 189
 190    s->wcs  = 0;
 191    s->wcvl = 0;
 192    s->wcvu = 0;
 193    s->worl = 0;
 194    s->woru = 0;
 195    s->id = SBSA_GWDT_ID;
 196}
 197
 198static void sbsa_gwdt_timer_sysinterrupt(void *opaque)
 199{
 200    SBSA_GWDTState *s = SBSA_GWDT(opaque);
 201
 202    if (!(s->wcs & SBSA_GWDT_WCS_WS0)) {
 203        s->wcs |= SBSA_GWDT_WCS_WS0;
 204        sbsa_gwdt_update_timer(s, TIMEOUT_REFRESH);
 205        qemu_set_irq(s->irq, 1);
 206    } else {
 207        s->wcs |= SBSA_GWDT_WCS_WS1;
 208        qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
 209        /*
 210         * Reset the watchdog only if the guest gets notified about
 211         * expiry. watchdog_perform_action() may temporarily relinquish
 212         * the BQL; reset before triggering the action to avoid races with
 213         * sbsa_gwdt instructions.
 214         */
 215        switch (get_watchdog_action()) {
 216        case WATCHDOG_ACTION_DEBUG:
 217        case WATCHDOG_ACTION_NONE:
 218        case WATCHDOG_ACTION_PAUSE:
 219            break;
 220        default:
 221            wdt_sbsa_gwdt_reset(DEVICE(s));
 222        }
 223        watchdog_perform_action();
 224    }
 225}
 226
 227static const MemoryRegionOps sbsa_gwdt_rops = {
 228    .read = sbsa_gwdt_rread,
 229    .write = sbsa_gwdt_rwrite,
 230    .endianness = DEVICE_LITTLE_ENDIAN,
 231    .valid.min_access_size = 4,
 232    .valid.max_access_size = 4,
 233    .valid.unaligned = false,
 234};
 235
 236static const MemoryRegionOps sbsa_gwdt_ops = {
 237    .read = sbsa_gwdt_read,
 238    .write = sbsa_gwdt_write,
 239    .endianness = DEVICE_LITTLE_ENDIAN,
 240    .valid.min_access_size = 4,
 241    .valid.max_access_size = 4,
 242    .valid.unaligned = false,
 243};
 244
 245static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp)
 246{
 247    SBSA_GWDTState *s = SBSA_GWDT(dev);
 248    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 249
 250    memory_region_init_io(&s->rmmio, OBJECT(dev),
 251                          &sbsa_gwdt_rops, s,
 252                          "sbsa_gwdt.refresh",
 253                          SBSA_GWDT_RMMIO_SIZE);
 254
 255    memory_region_init_io(&s->cmmio, OBJECT(dev),
 256                          &sbsa_gwdt_ops, s,
 257                          "sbsa_gwdt.control",
 258                          SBSA_GWDT_CMMIO_SIZE);
 259
 260    sysbus_init_mmio(sbd, &s->rmmio);
 261    sysbus_init_mmio(sbd, &s->cmmio);
 262
 263    sysbus_init_irq(sbd, &s->irq);
 264
 265    s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sbsa_gwdt_timer_sysinterrupt,
 266                dev);
 267}
 268
 269static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data)
 270{
 271    DeviceClass *dc = DEVICE_CLASS(klass);
 272
 273    dc->realize = wdt_sbsa_gwdt_realize;
 274    dc->reset = wdt_sbsa_gwdt_reset;
 275    dc->hotpluggable = false;
 276    set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
 277    dc->vmsd = &vmstate_sbsa_gwdt;
 278    dc->desc = "SBSA-compliant generic watchdog device";
 279}
 280
 281static const TypeInfo wdt_sbsa_gwdt_info = {
 282    .class_init = wdt_sbsa_gwdt_class_init,
 283    .parent = TYPE_SYS_BUS_DEVICE,
 284    .name  = TYPE_WDT_SBSA,
 285    .instance_size  = sizeof(SBSA_GWDTState),
 286};
 287
 288static void wdt_sbsa_gwdt_register_types(void)
 289{
 290    watchdog_add_model(&model);
 291    type_register_static(&wdt_sbsa_gwdt_info);
 292}
 293
 294type_init(wdt_sbsa_gwdt_register_types)
 295