qemu/hw/watchdog/xlnx-swdt.c
<<
>>
Prefs
   1/*
   2 * QEMU model of the SWDT
   3 *
   4 * Copyright (c) 2016 Xilinx Inc.
   5 *
   6 * Written by Konrad Frederic <fred.konrad@greensocs.com>
   7 *
   8 * Permission is hereby granted, free of charge, to any person obtaining a copy
   9 * of this software and associated documentation files (the "Software"), to deal
  10 * in the Software without restriction, including without limitation the rights
  11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12 * copies of the Software, and to permit persons to whom the Software is
  13 * furnished to do so, subject to the following conditions:
  14 *
  15 * The above copyright notice and this permission notice shall be included in
  16 * all copies or substantial portions of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24 * THE SOFTWARE.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "hw/sysbus.h"
  29#include "hw/register.h"
  30#include "qemu/timer.h"
  31#include "qemu/bitops.h"
  32#include "qapi/error.h"
  33#include "qemu/log.h"
  34
  35#include "qapi/qmp/qerror.h"
  36
  37#ifndef XLNX_SWDT_ERR_DEBUG
  38#define XLNX_SWDT_ERR_DEBUG 0
  39#endif
  40
  41#define TYPE_XLNX_SWDT "xlnx.swdt"
  42
  43#define XLNX_SWDT(obj) \
  44     OBJECT_CHECK(SWDTState, (obj), TYPE_XLNX_SWDT)
  45
  46REG32(SWDT_MODE, 0x0)
  47    FIELD(SWDT_MODE, ZKEY, 11, 12)
  48    FIELD(SWDT_MODE, IRQLN, 2, 7)
  49    FIELD(SWDT_MODE, RSTLN, 3, 4)
  50    FIELD(SWDT_MODE, IRQEN, 1, 2)
  51    FIELD(SWDT_MODE, RSTEN, 1, 1)
  52    FIELD(SWDT_MODE, WDEN, 1, 0)
  53REG32(SWDT_CONTROL, 0x4)
  54    FIELD(SWDT_CONTROL, CKEY, 12, 14)
  55    FIELD(SWDT_CONTROL, CRV, 12, 2)
  56    FIELD(SWDT_CONTROL, CLKSEL, 2, 0)
  57REG32(SWDT_RESTART, 0x8)
  58    FIELD(SWDT_RESTART, RSTKEY, 16, 0)
  59REG32(SWDT_STATUS, 0xC)
  60    FIELD(SWDT_STATUS, WDZ, 1, 0)
  61
  62#define R_MAX (R_SWDT_STATUS + 1)
  63
  64typedef struct SWDTState {
  65    SysBusDevice parent_obj;
  66    MemoryRegion iomem;
  67    qemu_irq irq;
  68    qemu_irq rst;
  69    QEMUTimer *timer;
  70    /* model the irq and rst line high time. */
  71    QEMUTimer *irq_done_timer;
  72    QEMUTimer *rst_done_timer;
  73
  74    uint64_t pclk;
  75    uint32_t current_mode;
  76    uint32_t current_control;
  77    uint32_t regs[R_MAX];
  78    RegisterInfo regs_info[R_MAX];
  79} SWDTState;
  80
  81static void swdt_done_irq_update(SWDTState *s)
  82{
  83    qemu_set_irq(s->irq, 1);
  84    timer_mod(s->irq_done_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
  85                                 + muldiv64(1000000000,
  86                                      4 << AF_EX32(s->regs, SWDT_MODE, IRQLN),
  87                                      s->pclk));
  88}
  89
  90static void swdt_reset_irq_update(SWDTState *s)
  91{
  92    qemu_set_irq(s->rst, 1);
  93    timer_mod(s->rst_done_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)
  94                           + muldiv64(1000000000,
  95                                      2 << AF_EX32(s->regs, SWDT_MODE, RSTLN),
  96                                      s->pclk));
  97}
  98
  99static void swdt_irq_done(void *opaque)
 100{
 101    SWDTState *s = XLNX_SWDT(opaque);
 102
 103    qemu_set_irq(s->irq, 0);
 104}
 105
 106static void swdt_reset_done(void *opaque)
 107{
 108    SWDTState *s = XLNX_SWDT(opaque);
 109
 110    qemu_set_irq(s->rst, 0);
 111}
 112
 113static void swdt_time_elapsed(void *opaque)
 114{
 115    SWDTState *s = XLNX_SWDT(opaque);
 116    bool do_a_reset = AF_EX32(s->regs, SWDT_MODE, RSTEN);
 117    bool do_an_irq = AF_EX32(s->regs, SWDT_MODE, IRQEN);
 118
 119    s->regs[R_SWDT_STATUS] = 1;
 120
 121    if (do_a_reset) {
 122        swdt_reset_irq_update(s);
 123    }
 124    if (do_an_irq) {
 125        swdt_done_irq_update(s);
 126    }
 127}
 128
 129static uint32_t swdt_reload_value(SWDTState *s)
 130{
 131    return (AF_EX32(s->regs, SWDT_CONTROL, CRV) << 12) + 0xFFF;
 132}
 133
 134static uint64_t swdt_next_trigger(SWDTState *s)
 135{
 136    return (muldiv64(1000000000,
 137                     8 << (3 * AF_EX32(s->regs, SWDT_CONTROL, CLKSEL)), s->pclk)
 138           * swdt_reload_value(s)) + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 139}
 140
 141/* Reload the counter, mod the timer. */
 142static void swdt_counter_reload(SWDTState *s)
 143{
 144    bool watchdog_enabled = AF_EX32(s->regs, SWDT_MODE, WDEN);
 145
 146    if (watchdog_enabled) {
 147        s->regs[R_SWDT_STATUS] = 0;
 148        timer_mod(s->timer, swdt_next_trigger(s));
 149    } else {
 150        timer_del(s->timer);
 151    }
 152}
 153
 154static void swdt_mode_postw(RegisterInfo *reg, uint64_t val64)
 155{
 156    SWDTState *s = XLNX_SWDT(reg->opaque);
 157    bool valid = (AF_EX32(s->regs, SWDT_MODE, ZKEY) == 0xABC);
 158
 159    if (!valid) {
 160        /* The write is not valid, just restore the old value of the register.
 161         */
 162        s->regs[R_SWDT_MODE] = s->current_mode;
 163        return;
 164    }
 165    /* Backup the mode in case a non valid write happens. */
 166    s->current_mode = s->regs[R_SWDT_MODE];
 167
 168    swdt_counter_reload(s);
 169}
 170
 171static void swdt_control_postw(RegisterInfo *reg, uint64_t val64)
 172{
 173    SWDTState *s = XLNX_SWDT(reg->opaque);
 174    bool valid = (AF_EX32(s->regs, SWDT_CONTROL, CKEY) == 0x248);
 175
 176    if (!valid) {
 177        /* The write is not valid, just restore the old value of the register.
 178         */
 179        s->regs[R_SWDT_CONTROL] = s->current_control;
 180        return;
 181    }
 182    /* Backup the mode in case a non valid write happens. */
 183    s->current_control = s->regs[R_SWDT_CONTROL];
 184}
 185
 186static void swdt_restart_key_postw(RegisterInfo *reg, uint64_t val64)
 187{
 188    SWDTState *s = XLNX_SWDT(reg->opaque);
 189    bool valid = (AF_EX32(s->regs, SWDT_RESTART, RSTKEY) == 0x1999);
 190
 191    if (valid) {
 192        swdt_counter_reload(s);
 193    }
 194
 195    /* Read as 0 (but we probably don't care). */
 196    s->regs[R_SWDT_RESTART] = 0x0000;
 197}
 198
 199static RegisterAccessInfo swdt_regs_info[] = {
 200    {   .name = "SWDT_MODE",  .decode.addr = A_SWDT_MODE,
 201        .reset = 0x000001C2,
 202        .rsvd = 0x00000E08,
 203        .ro = 0x00000E08,
 204        .post_write = swdt_mode_postw,
 205    },{ .name = "SWDT_CONTROL",  .decode.addr = A_SWDT_CONTROL,
 206        .reset = 0x00003FFC,
 207        .post_write = swdt_control_postw,
 208    },{ .name = "SWDT_RESTART",  .decode.addr = A_SWDT_RESTART,
 209        .reset = 0x00000000,
 210        .post_write = swdt_restart_key_postw,
 211    },{ .name = "SWDT_STATUS",  .decode.addr = A_SWDT_STATUS,
 212        .reset = 0x00000000,
 213        .ro = 0x00000001
 214    }
 215};
 216
 217static void swdt_reset(DeviceState *dev)
 218{
 219    SWDTState *s = XLNX_SWDT(dev);
 220    unsigned int i;
 221
 222    /* The reset value in the registers are ok but don't have the key.
 223     * so the write will be invalid and we need to set the backup value
 224     * to the init value.
 225     */
 226    s->current_mode = 0x000001C2;
 227    s->current_control = 0x00003FFC;
 228
 229    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 230        register_reset(&s->regs_info[i]);
 231    }
 232
 233    swdt_counter_reload(s);
 234    swdt_irq_done(s);
 235    swdt_reset_done(s);
 236}
 237
 238static const MemoryRegionOps swdt_ops = {
 239    .read = register_read_memory_le,
 240    .write = register_write_memory_le,
 241    .endianness = DEVICE_LITTLE_ENDIAN,
 242    .valid = {
 243        .min_access_size = 4,
 244        .max_access_size = 4,
 245    },
 246};
 247
 248static void swdt_realize(DeviceState *dev, Error **errp)
 249{
 250    SWDTState *s = XLNX_SWDT(dev);
 251    const char *prefix = object_get_canonical_path(OBJECT(dev));
 252    unsigned int i;
 253
 254    if (!s->pclk) {
 255        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "pclk");
 256        return;
 257    }
 258
 259    for (i = 0; i < ARRAY_SIZE(swdt_regs_info); ++i) {
 260        RegisterInfo *r = &s->regs_info[i];
 261
 262        *r = (RegisterInfo) {
 263            .data = (uint8_t *)&s->regs[swdt_regs_info[i].decode.addr / 4],
 264            .data_size = sizeof(uint32_t),
 265            .access = &swdt_regs_info[i],
 266            .debug = XLNX_SWDT_ERR_DEBUG,
 267            .prefix = prefix,
 268            .opaque = s,
 269        };
 270        memory_region_init_io(&r->mem, OBJECT(dev), &swdt_ops, r,
 271                              r->access->name, 4);
 272        memory_region_add_subregion(&s->iomem, r->access->decode.addr, &r->mem);
 273    }
 274}
 275
 276static void swdt_init(Object *obj)
 277{
 278    SWDTState *s = XLNX_SWDT(obj);
 279    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 280
 281    memory_region_init(&s->iomem, obj, TYPE_XLNX_SWDT, R_MAX * 4);
 282    sysbus_init_mmio(sbd, &s->iomem);
 283    sysbus_init_irq(sbd, &s->irq);
 284
 285    s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, swdt_time_elapsed, s);
 286    s->irq_done_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, swdt_irq_done, s);
 287    s->rst_done_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, swdt_reset_done, s);
 288}
 289
 290static Property swdt_properties[] = {
 291    /* pclk in Hz */
 292    DEFINE_PROP_UINT64("pclk", SWDTState, pclk, 0),
 293    DEFINE_PROP_END_OF_LIST(),
 294};
 295
 296static const VMStateDescription vmstate_swdt = {
 297    .name = TYPE_XLNX_SWDT,
 298    .version_id = 1,
 299    .minimum_version_id = 1,
 300    .fields = (VMStateField[]) {
 301        VMSTATE_UINT32_ARRAY(regs, SWDTState, R_MAX),
 302        VMSTATE_END_OF_LIST(),
 303    }
 304};
 305
 306static void swdt_class_init(ObjectClass *klass, void *data)
 307{
 308    DeviceClass *dc = DEVICE_CLASS(klass);
 309
 310    dc->reset = swdt_reset;
 311    dc->realize = swdt_realize;
 312    dc->vmsd = &vmstate_swdt;
 313    dc->props = swdt_properties;
 314}
 315
 316static const TypeInfo swdt_info = {
 317    .name          = TYPE_XLNX_SWDT,
 318    .parent        = TYPE_SYS_BUS_DEVICE,
 319    .instance_size = sizeof(SWDTState),
 320    .class_init    = swdt_class_init,
 321    .instance_init = swdt_init,
 322};
 323
 324static void swdt_register_types(void)
 325{
 326    type_register_static(&swdt_info);
 327}
 328
 329type_init(swdt_register_types)
 330