sbsa_gwdt.c (8115B)
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_MISC, dc->categories); 277 dc->vmsd = &vmstate_sbsa_gwdt; 278} 279 280static const TypeInfo wdt_sbsa_gwdt_info = { 281 .class_init = wdt_sbsa_gwdt_class_init, 282 .parent = TYPE_SYS_BUS_DEVICE, 283 .name = TYPE_WDT_SBSA, 284 .instance_size = sizeof(SBSA_GWDTState), 285}; 286 287static void wdt_sbsa_gwdt_register_types(void) 288{ 289 watchdog_add_model(&model); 290 type_register_static(&wdt_sbsa_gwdt_info); 291} 292 293type_init(wdt_sbsa_gwdt_register_types)