1*854123bfSCédric Le Goater /* 2*854123bfSCédric Le Goater * ASPEED Watchdog Controller 3*854123bfSCédric Le Goater * 4*854123bfSCédric Le Goater * Copyright (C) 2016-2017 IBM Corp. 5*854123bfSCédric Le Goater * 6*854123bfSCédric Le Goater * This code is licensed under the GPL version 2 or later. See the 7*854123bfSCédric Le Goater * COPYING file in the top-level directory. 8*854123bfSCédric Le Goater */ 9*854123bfSCédric Le Goater 10*854123bfSCédric Le Goater #include "qemu/osdep.h" 11*854123bfSCédric Le Goater #include "qemu/log.h" 12*854123bfSCédric Le Goater #include "sysemu/watchdog.h" 13*854123bfSCédric Le Goater #include "hw/sysbus.h" 14*854123bfSCédric Le Goater #include "qemu/timer.h" 15*854123bfSCédric Le Goater #include "hw/watchdog/wdt_aspeed.h" 16*854123bfSCédric Le Goater 17*854123bfSCédric Le Goater #define WDT_STATUS (0x00 / 4) 18*854123bfSCédric Le Goater #define WDT_RELOAD_VALUE (0x04 / 4) 19*854123bfSCédric Le Goater #define WDT_RESTART (0x08 / 4) 20*854123bfSCédric Le Goater #define WDT_CTRL (0x0C / 4) 21*854123bfSCédric Le Goater #define WDT_CTRL_RESET_MODE_SOC (0x00 << 5) 22*854123bfSCédric Le Goater #define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5) 23*854123bfSCédric Le Goater #define WDT_CTRL_1MHZ_CLK BIT(4) 24*854123bfSCédric Le Goater #define WDT_CTRL_WDT_EXT BIT(3) 25*854123bfSCédric Le Goater #define WDT_CTRL_WDT_INTR BIT(2) 26*854123bfSCédric Le Goater #define WDT_CTRL_RESET_SYSTEM BIT(1) 27*854123bfSCédric Le Goater #define WDT_CTRL_ENABLE BIT(0) 28*854123bfSCédric Le Goater 29*854123bfSCédric Le Goater #define WDT_TIMEOUT_STATUS (0x10 / 4) 30*854123bfSCédric Le Goater #define WDT_TIMEOUT_CLEAR (0x14 / 4) 31*854123bfSCédric Le Goater #define WDT_RESET_WDITH (0x18 / 4) 32*854123bfSCédric Le Goater 33*854123bfSCédric Le Goater #define WDT_RESTART_MAGIC 0x4755 34*854123bfSCédric Le Goater 35*854123bfSCédric Le Goater static bool aspeed_wdt_is_enabled(const AspeedWDTState *s) 36*854123bfSCédric Le Goater { 37*854123bfSCédric Le Goater return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE; 38*854123bfSCédric Le Goater } 39*854123bfSCédric Le Goater 40*854123bfSCédric Le Goater static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) 41*854123bfSCédric Le Goater { 42*854123bfSCédric Le Goater AspeedWDTState *s = ASPEED_WDT(opaque); 43*854123bfSCédric Le Goater 44*854123bfSCédric Le Goater offset >>= 2; 45*854123bfSCédric Le Goater 46*854123bfSCédric Le Goater switch (offset) { 47*854123bfSCédric Le Goater case WDT_STATUS: 48*854123bfSCédric Le Goater return s->regs[WDT_STATUS]; 49*854123bfSCédric Le Goater case WDT_RELOAD_VALUE: 50*854123bfSCédric Le Goater return s->regs[WDT_RELOAD_VALUE]; 51*854123bfSCédric Le Goater case WDT_RESTART: 52*854123bfSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, 53*854123bfSCédric Le Goater "%s: read from write-only reg at offset 0x%" 54*854123bfSCédric Le Goater HWADDR_PRIx "\n", __func__, offset); 55*854123bfSCédric Le Goater return 0; 56*854123bfSCédric Le Goater case WDT_CTRL: 57*854123bfSCédric Le Goater return s->regs[WDT_CTRL]; 58*854123bfSCédric Le Goater case WDT_TIMEOUT_STATUS: 59*854123bfSCédric Le Goater case WDT_TIMEOUT_CLEAR: 60*854123bfSCédric Le Goater case WDT_RESET_WDITH: 61*854123bfSCédric Le Goater qemu_log_mask(LOG_UNIMP, 62*854123bfSCédric Le Goater "%s: uninmplemented read at offset 0x%" HWADDR_PRIx "\n", 63*854123bfSCédric Le Goater __func__, offset); 64*854123bfSCédric Le Goater return 0; 65*854123bfSCédric Le Goater default: 66*854123bfSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, 67*854123bfSCédric Le Goater "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", 68*854123bfSCédric Le Goater __func__, offset); 69*854123bfSCédric Le Goater return 0; 70*854123bfSCédric Le Goater } 71*854123bfSCédric Le Goater 72*854123bfSCédric Le Goater } 73*854123bfSCédric Le Goater 74*854123bfSCédric Le Goater static void aspeed_wdt_reload(AspeedWDTState *s, bool pclk) 75*854123bfSCédric Le Goater { 76*854123bfSCédric Le Goater uint32_t reload; 77*854123bfSCédric Le Goater 78*854123bfSCédric Le Goater if (pclk) { 79*854123bfSCédric Le Goater reload = muldiv64(s->regs[WDT_RELOAD_VALUE], NANOSECONDS_PER_SECOND, 80*854123bfSCédric Le Goater s->pclk_freq); 81*854123bfSCédric Le Goater } else { 82*854123bfSCédric Le Goater reload = s->regs[WDT_RELOAD_VALUE] * 1000; 83*854123bfSCédric Le Goater } 84*854123bfSCédric Le Goater 85*854123bfSCédric Le Goater if (aspeed_wdt_is_enabled(s)) { 86*854123bfSCédric Le Goater timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload); 87*854123bfSCédric Le Goater } 88*854123bfSCédric Le Goater } 89*854123bfSCédric Le Goater 90*854123bfSCédric Le Goater static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, 91*854123bfSCédric Le Goater unsigned size) 92*854123bfSCédric Le Goater { 93*854123bfSCédric Le Goater AspeedWDTState *s = ASPEED_WDT(opaque); 94*854123bfSCédric Le Goater bool enable = data & WDT_CTRL_ENABLE; 95*854123bfSCédric Le Goater 96*854123bfSCédric Le Goater offset >>= 2; 97*854123bfSCédric Le Goater 98*854123bfSCédric Le Goater switch (offset) { 99*854123bfSCédric Le Goater case WDT_STATUS: 100*854123bfSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, 101*854123bfSCédric Le Goater "%s: write to read-only reg at offset 0x%" 102*854123bfSCédric Le Goater HWADDR_PRIx "\n", __func__, offset); 103*854123bfSCédric Le Goater break; 104*854123bfSCédric Le Goater case WDT_RELOAD_VALUE: 105*854123bfSCédric Le Goater s->regs[WDT_RELOAD_VALUE] = data; 106*854123bfSCédric Le Goater break; 107*854123bfSCédric Le Goater case WDT_RESTART: 108*854123bfSCédric Le Goater if ((data & 0xFFFF) == WDT_RESTART_MAGIC) { 109*854123bfSCédric Le Goater s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE]; 110*854123bfSCédric Le Goater aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); 111*854123bfSCédric Le Goater } 112*854123bfSCédric Le Goater break; 113*854123bfSCédric Le Goater case WDT_CTRL: 114*854123bfSCédric Le Goater if (enable && !aspeed_wdt_is_enabled(s)) { 115*854123bfSCédric Le Goater s->regs[WDT_CTRL] = data; 116*854123bfSCédric Le Goater aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); 117*854123bfSCédric Le Goater } else if (!enable && aspeed_wdt_is_enabled(s)) { 118*854123bfSCédric Le Goater s->regs[WDT_CTRL] = data; 119*854123bfSCédric Le Goater timer_del(s->timer); 120*854123bfSCédric Le Goater } 121*854123bfSCédric Le Goater break; 122*854123bfSCédric Le Goater case WDT_TIMEOUT_STATUS: 123*854123bfSCédric Le Goater case WDT_TIMEOUT_CLEAR: 124*854123bfSCédric Le Goater case WDT_RESET_WDITH: 125*854123bfSCédric Le Goater qemu_log_mask(LOG_UNIMP, 126*854123bfSCédric Le Goater "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n", 127*854123bfSCédric Le Goater __func__, offset); 128*854123bfSCédric Le Goater break; 129*854123bfSCédric Le Goater default: 130*854123bfSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, 131*854123bfSCédric Le Goater "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", 132*854123bfSCédric Le Goater __func__, offset); 133*854123bfSCédric Le Goater } 134*854123bfSCédric Le Goater return; 135*854123bfSCédric Le Goater } 136*854123bfSCédric Le Goater 137*854123bfSCédric Le Goater static WatchdogTimerModel model = { 138*854123bfSCédric Le Goater .wdt_name = TYPE_ASPEED_WDT, 139*854123bfSCédric Le Goater .wdt_description = "Aspeed watchdog device", 140*854123bfSCédric Le Goater }; 141*854123bfSCédric Le Goater 142*854123bfSCédric Le Goater static const VMStateDescription vmstate_aspeed_wdt = { 143*854123bfSCédric Le Goater .name = "vmstate_aspeed_wdt", 144*854123bfSCédric Le Goater .version_id = 0, 145*854123bfSCédric Le Goater .minimum_version_id = 0, 146*854123bfSCédric Le Goater .fields = (VMStateField[]) { 147*854123bfSCédric Le Goater VMSTATE_TIMER_PTR(timer, AspeedWDTState), 148*854123bfSCédric Le Goater VMSTATE_UINT32_ARRAY(regs, AspeedWDTState, ASPEED_WDT_REGS_MAX), 149*854123bfSCédric Le Goater VMSTATE_END_OF_LIST() 150*854123bfSCédric Le Goater } 151*854123bfSCédric Le Goater }; 152*854123bfSCédric Le Goater 153*854123bfSCédric Le Goater static const MemoryRegionOps aspeed_wdt_ops = { 154*854123bfSCédric Le Goater .read = aspeed_wdt_read, 155*854123bfSCédric Le Goater .write = aspeed_wdt_write, 156*854123bfSCédric Le Goater .endianness = DEVICE_LITTLE_ENDIAN, 157*854123bfSCédric Le Goater .valid.min_access_size = 4, 158*854123bfSCédric Le Goater .valid.max_access_size = 4, 159*854123bfSCédric Le Goater .valid.unaligned = false, 160*854123bfSCédric Le Goater }; 161*854123bfSCédric Le Goater 162*854123bfSCédric Le Goater static void aspeed_wdt_reset(DeviceState *dev) 163*854123bfSCédric Le Goater { 164*854123bfSCédric Le Goater AspeedWDTState *s = ASPEED_WDT(dev); 165*854123bfSCédric Le Goater 166*854123bfSCédric Le Goater s->regs[WDT_STATUS] = 0x3EF1480; 167*854123bfSCédric Le Goater s->regs[WDT_RELOAD_VALUE] = 0x03EF1480; 168*854123bfSCédric Le Goater s->regs[WDT_RESTART] = 0; 169*854123bfSCédric Le Goater s->regs[WDT_CTRL] = 0; 170*854123bfSCédric Le Goater 171*854123bfSCédric Le Goater timer_del(s->timer); 172*854123bfSCédric Le Goater } 173*854123bfSCédric Le Goater 174*854123bfSCédric Le Goater static void aspeed_wdt_timer_expired(void *dev) 175*854123bfSCédric Le Goater { 176*854123bfSCédric Le Goater AspeedWDTState *s = ASPEED_WDT(dev); 177*854123bfSCédric Le Goater 178*854123bfSCédric Le Goater qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n"); 179*854123bfSCédric Le Goater watchdog_perform_action(); 180*854123bfSCédric Le Goater timer_del(s->timer); 181*854123bfSCédric Le Goater } 182*854123bfSCédric Le Goater 183*854123bfSCédric Le Goater #define PCLK_HZ 24000000 184*854123bfSCédric Le Goater 185*854123bfSCédric Le Goater static void aspeed_wdt_realize(DeviceState *dev, Error **errp) 186*854123bfSCédric Le Goater { 187*854123bfSCédric Le Goater SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 188*854123bfSCédric Le Goater AspeedWDTState *s = ASPEED_WDT(dev); 189*854123bfSCédric Le Goater 190*854123bfSCédric Le Goater s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); 191*854123bfSCédric Le Goater 192*854123bfSCédric Le Goater /* FIXME: This setting should be derived from the SCU hw strapping 193*854123bfSCédric Le Goater * register SCU70 194*854123bfSCédric Le Goater */ 195*854123bfSCédric Le Goater s->pclk_freq = PCLK_HZ; 196*854123bfSCédric Le Goater 197*854123bfSCédric Le Goater memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s, 198*854123bfSCédric Le Goater TYPE_ASPEED_WDT, ASPEED_WDT_REGS_MAX * 4); 199*854123bfSCédric Le Goater sysbus_init_mmio(sbd, &s->iomem); 200*854123bfSCédric Le Goater } 201*854123bfSCédric Le Goater 202*854123bfSCédric Le Goater static void aspeed_wdt_class_init(ObjectClass *klass, void *data) 203*854123bfSCédric Le Goater { 204*854123bfSCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 205*854123bfSCédric Le Goater 206*854123bfSCédric Le Goater dc->realize = aspeed_wdt_realize; 207*854123bfSCédric Le Goater dc->reset = aspeed_wdt_reset; 208*854123bfSCédric Le Goater set_bit(DEVICE_CATEGORY_MISC, dc->categories); 209*854123bfSCédric Le Goater dc->vmsd = &vmstate_aspeed_wdt; 210*854123bfSCédric Le Goater } 211*854123bfSCédric Le Goater 212*854123bfSCédric Le Goater static const TypeInfo aspeed_wdt_info = { 213*854123bfSCédric Le Goater .parent = TYPE_SYS_BUS_DEVICE, 214*854123bfSCédric Le Goater .name = TYPE_ASPEED_WDT, 215*854123bfSCédric Le Goater .instance_size = sizeof(AspeedWDTState), 216*854123bfSCédric Le Goater .class_init = aspeed_wdt_class_init, 217*854123bfSCédric Le Goater }; 218*854123bfSCédric Le Goater 219*854123bfSCédric Le Goater static void wdt_aspeed_register_types(void) 220*854123bfSCédric Le Goater { 221*854123bfSCédric Le Goater watchdog_add_model(&model); 222*854123bfSCédric Le Goater type_register_static(&aspeed_wdt_info); 223*854123bfSCédric Le Goater } 224*854123bfSCédric Le Goater 225*854123bfSCédric Le Goater type_init(wdt_aspeed_register_types) 226