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 "hw/irq.h"
  31#include "qemu/timer.h"
  32#include "qemu/bitops.h"
  33#include "qapi/error.h"
  34#include "qemu/log.h"
  35#include "migration/vmstate.h"
  36#include "hw/qdev-properties.h"
  37#include "hw/fdt_generic_util.h"
  38
  39#include "qapi/qmp/qerror.h"
  40
  41#ifndef XLNX_SWDT_ERR_DEBUG
  42#define XLNX_SWDT_ERR_DEBUG 0
  43#endif
  44
  45#define TYPE_XLNX_SWDT "xlnx.swdt"
  46
  47#define XLNX_SWDT(obj) \
  48     OBJECT_CHECK(SWDTState, (obj), TYPE_XLNX_SWDT)
  49
  50REG32(MODE, 0x0)
  51    FIELD(MODE, ZKEY, 12, 12)
  52    FIELD(MODE, IRQLN, 7, 2)
  53    FIELD(MODE, RSTLN, 4, 3)
  54    FIELD(MODE, IRQEN, 2, 1)
  55    FIELD(MODE, RSTEN, 1, 1)
  56    FIELD(MODE, WDEN, 0, 1)
  57REG32(CONTROL, 0x4)
  58    FIELD(CONTROL, CKEY, 14, 12)
  59    FIELD(CONTROL, CRV, 2, 12)
  60    FIELD(CONTROL, CLKSEL, 0, 2)
  61REG32(RESTART, 0x8)
  62    FIELD(RESTART, RSTKEY, 0, 16)
  63REG32(STATUS, 0xc)
  64    FIELD(STATUS, WDZ, 0, 1)
  65
  66#define SWDT_R_MAX (R_STATUS + 1)
  67
  68typedef struct SWDTState {
  69    SysBusDevice parent_obj;
  70    MemoryRegion iomem;
  71    qemu_irq irq;
  72    qemu_irq rst;
  73    qemu_irq wdt_timeout_irq;
  74    QEMUTimer *timer;
  75    /* model the irq and rst line high time. */
  76    QEMUTimer *irq_done_timer;
  77    QEMUTimer *rst_done_timer;
  78
  79    uint64_t pclk;
  80    uint32_t current_mode;
  81    uint32_t current_control;
  82    uint32_t regs[SWDT_R_MAX];
  83    RegisterInfo regs_info[SWDT_R_MAX];
  84} SWDTState;
  85
  86static void swdt_done_irq_update(SWDTState *s)
  87{
  88    uint64_t irqln = muldiv64(1000000000,
  89                              4 << ARRAY_FIELD_EX32(s->regs, MODE, IRQLN),
  90                              s->pclk);
  91
  92    qemu_set_irq(s->irq, 1);
  93    timer_mod(s->irq_done_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + irqln);
  94}
  95
  96static void swdt_reset_irq_update(SWDTState *s)
  97{
  98    uint64_t rstln = muldiv64(1000000000,
  99                              2 << ARRAY_FIELD_EX32(s->regs, MODE, RSTLN),
 100                              s->pclk);
 101
 102    qemu_set_irq(s->rst, 1);
 103    timer_mod(s->rst_done_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + rstln);
 104}
 105
 106static void swdt_irq_done(void *opaque)
 107{
 108    SWDTState *s = XLNX_SWDT(opaque);
 109
 110    qemu_set_irq(s->irq, 0);
 111}
 112
 113static void swdt_reset_done(void *opaque)
 114{
 115    SWDTState *s = XLNX_SWDT(opaque);
 116
 117    qemu_set_irq(s->rst, 0);
 118}
 119
 120static void swdt_time_elapsed(void *opaque)
 121{
 122    SWDTState *s = XLNX_SWDT(opaque);
 123    bool do_a_reset = ARRAY_FIELD_EX32(s->regs, MODE, RSTEN);
 124    bool do_an_irq = ARRAY_FIELD_EX32(s->regs, MODE, IRQEN);
 125
 126    s->regs[R_STATUS] = 1;
 127    qemu_set_irq(s->wdt_timeout_irq, 1);
 128
 129    if (do_a_reset) {
 130        swdt_reset_irq_update(s);
 131    }
 132    if (do_an_irq) {
 133        swdt_done_irq_update(s);
 134    }
 135}
 136
 137static uint32_t swdt_reload_value(SWDTState *s)
 138{
 139    return (ARRAY_FIELD_EX32(s->regs, CONTROL, CRV) << 12) + 0xFFF;
 140}
 141
 142static uint64_t swdt_next_trigger(SWDTState *s)
 143{
 144    uint64_t clksel = muldiv64(1000000000,
 145                               8 << (3 * ARRAY_FIELD_EX32(s->regs,
 146                                                          CONTROL, CLKSEL)),
 147                               s->pclk);
 148
 149    return (clksel * swdt_reload_value(s)) +
 150               qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 151}
 152
 153/* Reload the counter, mod the timer. */
 154static void swdt_counter_reload(SWDTState *s)
 155{
 156    bool watchdog_enabled = ARRAY_FIELD_EX32(s->regs, MODE, WDEN);
 157
 158    if (watchdog_enabled) {
 159        s->regs[R_STATUS] = 0;
 160        timer_mod(s->timer, swdt_next_trigger(s));
 161    } else {
 162        timer_del(s->timer);
 163    }
 164}
 165
 166static void swdt_mode_postw(RegisterInfo *reg, uint64_t val64)
 167{
 168    SWDTState *s = XLNX_SWDT(reg->opaque);
 169    bool valid = (ARRAY_FIELD_EX32(s->regs, MODE, ZKEY) == 0xABC);
 170
 171    if (!valid) {
 172        /* The write is not valid, just restore the old value of the register.
 173         */
 174        s->regs[R_MODE] = s->current_mode;
 175        return;
 176    }
 177    /* Backup the mode in case a non valid write happens. */
 178    s->current_mode = s->regs[R_MODE];
 179
 180    swdt_counter_reload(s);
 181}
 182
 183static uint64_t swdt_mode_postr(RegisterInfo *reg, uint64_t val)
 184{
 185    SWDTState *s = XLNX_SWDT(reg->opaque);
 186
 187    /* The ZKEY is write only */
 188    return s->regs[R_MODE] & ~R_MODE_ZKEY_MASK;
 189}
 190
 191static void swdt_control_postw(RegisterInfo *reg, uint64_t val64)
 192{
 193    SWDTState *s = XLNX_SWDT(reg->opaque);
 194    bool valid = (ARRAY_FIELD_EX32(s->regs, CONTROL, CKEY) == 0x248);
 195
 196    if (!valid) {
 197        /* The write is not valid, just restore the old value of the register.
 198         */
 199        s->regs[R_CONTROL] = s->current_control;
 200        return;
 201    }
 202
 203    /* Backup the mode in case a non valid write happens. */
 204    s->current_control = s->regs[R_CONTROL];
 205}
 206
 207static void swdt_restart_key_postw(RegisterInfo *reg, uint64_t val64)
 208{
 209    SWDTState *s = XLNX_SWDT(reg->opaque);
 210    bool valid = (ARRAY_FIELD_EX32(s->regs, RESTART, RSTKEY) == 0x1999);
 211
 212    if (valid) {
 213        swdt_counter_reload(s);
 214    }
 215
 216    /* Read as 0 (but we probably don't care). */
 217    s->regs[R_RESTART] = 0x0000;
 218}
 219
 220static const RegisterAccessInfo swdt_regs_info[] = {
 221    {   .name = "MODE",  .addr = A_MODE,
 222        .reset = 0x1c2,
 223        .rsvd = 0xe08,
 224        .post_write = swdt_mode_postw,
 225        .post_read = swdt_mode_postr,
 226    },{ .name = "CONTROL",  .addr = A_CONTROL,
 227        .reset = 0x3ffc,
 228        .post_write = swdt_control_postw,
 229    },{ .name = "RESTART",  .addr = A_RESTART,
 230        .post_write = swdt_restart_key_postw,
 231    },{ .name = "STATUS",  .addr = A_STATUS,
 232        .ro = 0x1,
 233    }
 234};
 235
 236static void swdt_reset(DeviceState *dev)
 237{
 238    SWDTState *s = XLNX_SWDT(dev);
 239    unsigned int i;
 240
 241    /* The reset value in the registers are ok but don't have the key.
 242     * so the write will be invalid and we need to set the backup value
 243     * to the init value.
 244     */
 245    s->current_mode = 0x000001C2;
 246    s->current_control = 0x00003FFC;
 247
 248    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
 249        register_reset(&s->regs_info[i]);
 250    }
 251
 252    swdt_counter_reload(s);
 253    swdt_irq_done(s);
 254    swdt_reset_done(s);
 255}
 256
 257static const MemoryRegionOps swdt_ops = {
 258    .read = register_read_memory,
 259    .write = register_write_memory,
 260    .endianness = DEVICE_LITTLE_ENDIAN,
 261    .valid = {
 262        .min_access_size = 4,
 263        .max_access_size = 4,
 264    },
 265};
 266
 267static void swdt_realize(DeviceState *dev, Error **errp)
 268{
 269    SWDTState *s = XLNX_SWDT(dev);
 270
 271    if (!s->pclk) {
 272        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "pclk");
 273        return;
 274    }
 275}
 276
 277static void swdt_init(Object *obj)
 278{
 279    SWDTState *s = XLNX_SWDT(obj);
 280    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 281    RegisterInfoArray *reg_array;
 282
 283    memory_region_init(&s->iomem, obj, TYPE_XLNX_SWDT, SWDT_R_MAX * 4);
 284    reg_array =
 285        register_init_block32(DEVICE(obj), swdt_regs_info,
 286                              ARRAY_SIZE(swdt_regs_info),
 287                              s->regs_info, s->regs,
 288                              &swdt_ops,
 289                              XLNX_SWDT_ERR_DEBUG,
 290                              SWDT_R_MAX * 4);
 291    memory_region_add_subregion(&s->iomem,
 292                                0x0,
 293                                &reg_array->mem);
 294    sysbus_init_mmio(sbd, &s->iomem);
 295    sysbus_init_irq(sbd, &s->irq);
 296
 297    qdev_init_gpio_out_named(DEVICE(obj), &s->wdt_timeout_irq,
 298                             "wdt_timeout_error_out", 1);
 299
 300    s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, swdt_time_elapsed, s);
 301    s->irq_done_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, swdt_irq_done, s);
 302    s->rst_done_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, swdt_reset_done, s);
 303}
 304
 305static Property swdt_properties[] = {
 306    /* pclk in Hz */
 307    DEFINE_PROP_UINT64("pclk", SWDTState, pclk, 0),
 308    DEFINE_PROP_END_OF_LIST(),
 309};
 310
 311static const FDTGenericGPIOSet wdt_client_gpios[] = {
 312    {
 313        .names = &fdt_generic_gpio_name_set_gpio,
 314        .gpios = (FDTGenericGPIOConnection[]) {
 315            { .name = "wdt_timeout_error_out", .fdt_index = 0, .range = 1 },
 316            { },
 317        }
 318    },
 319    { },
 320};
 321
 322static const VMStateDescription vmstate_swdt = {
 323    .name = TYPE_XLNX_SWDT,
 324    .version_id = 1,
 325    .minimum_version_id = 1,
 326    .fields = (VMStateField[]) {
 327        VMSTATE_UINT32_ARRAY(regs, SWDTState, SWDT_R_MAX),
 328        VMSTATE_END_OF_LIST(),
 329    }
 330};
 331
 332static void swdt_class_init(ObjectClass *klass, void *data)
 333{
 334    DeviceClass *dc = DEVICE_CLASS(klass);
 335    FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(klass);
 336
 337    dc->reset = swdt_reset;
 338    dc->realize = swdt_realize;
 339    dc->vmsd = &vmstate_swdt;
 340    device_class_set_props(dc, swdt_properties);
 341    fggc->client_gpios = wdt_client_gpios;
 342}
 343
 344static const TypeInfo swdt_info = {
 345    .name          = TYPE_XLNX_SWDT,
 346    .parent        = TYPE_SYS_BUS_DEVICE,
 347    .instance_size = sizeof(SWDTState),
 348    .class_init    = swdt_class_init,
 349    .instance_init = swdt_init,
 350    .interfaces    = (InterfaceInfo[]) {
 351        { TYPE_FDT_GENERIC_GPIO },
 352        { }
 353    },
 354};
 355
 356static void swdt_register_types(void)
 357{
 358    type_register_static(&swdt_info);
 359}
 360
 361type_init(swdt_register_types)
 362