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