1*bf01a04fSTommy Wu /* 2*bf01a04fSTommy Wu * SiFive HiFive1 AON (Always On Domain) for QEMU. 3*bf01a04fSTommy Wu * 4*bf01a04fSTommy Wu * Copyright (c) 2022 SiFive, Inc. All rights reserved. 5*bf01a04fSTommy Wu * 6*bf01a04fSTommy Wu * This program is free software; you can redistribute it and/or modify it 7*bf01a04fSTommy Wu * under the terms and conditions of the GNU General Public License, 8*bf01a04fSTommy Wu * version 2 or later, as published by the Free Software Foundation. 9*bf01a04fSTommy Wu * 10*bf01a04fSTommy Wu * This program is distributed in the hope it will be useful, but WITHOUT 11*bf01a04fSTommy Wu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12*bf01a04fSTommy Wu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13*bf01a04fSTommy Wu * more details. 14*bf01a04fSTommy Wu * 15*bf01a04fSTommy Wu * You should have received a copy of the GNU General Public License along with 16*bf01a04fSTommy Wu * this program. If not, see <http://www.gnu.org/licenses/>. 17*bf01a04fSTommy Wu */ 18*bf01a04fSTommy Wu 19*bf01a04fSTommy Wu #include "qemu/osdep.h" 20*bf01a04fSTommy Wu #include "qemu/timer.h" 21*bf01a04fSTommy Wu #include "qemu/log.h" 22*bf01a04fSTommy Wu #include "hw/irq.h" 23*bf01a04fSTommy Wu #include "hw/registerfields.h" 24*bf01a04fSTommy Wu #include "hw/misc/sifive_e_aon.h" 25*bf01a04fSTommy Wu #include "qapi/visitor.h" 26*bf01a04fSTommy Wu #include "qapi/error.h" 27*bf01a04fSTommy Wu #include "sysemu/watchdog.h" 28*bf01a04fSTommy Wu #include "hw/qdev-properties.h" 29*bf01a04fSTommy Wu 30*bf01a04fSTommy Wu REG32(AON_WDT_WDOGCFG, 0x0) 31*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4) 32*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4) 33*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1) 34*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1) 35*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2) 36*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1) 37*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1) 38*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14) 39*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, IP0, 28, 1) 40*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3) 41*bf01a04fSTommy Wu REG32(AON_WDT_WDOGCOUNT, 0x8) 42*bf01a04fSTommy Wu FIELD(AON_WDT_WDOGCOUNT, VALUE, 0, 31) 43*bf01a04fSTommy Wu REG32(AON_WDT_WDOGS, 0x10) 44*bf01a04fSTommy Wu REG32(AON_WDT_WDOGFEED, 0x18) 45*bf01a04fSTommy Wu REG32(AON_WDT_WDOGKEY, 0x1c) 46*bf01a04fSTommy Wu REG32(AON_WDT_WDOGCMP0, 0x20) 47*bf01a04fSTommy Wu 48*bf01a04fSTommy Wu static void sifive_e_aon_wdt_update_wdogcount(SiFiveEAONState *r) 49*bf01a04fSTommy Wu { 50*bf01a04fSTommy Wu int64_t now; 51*bf01a04fSTommy Wu if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 0 && 52*bf01a04fSTommy Wu FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 0) { 53*bf01a04fSTommy Wu return; 54*bf01a04fSTommy Wu } 55*bf01a04fSTommy Wu 56*bf01a04fSTommy Wu now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 57*bf01a04fSTommy Wu r->wdogcount += muldiv64(now - r->wdog_restart_time, 58*bf01a04fSTommy Wu r->wdogclk_freq, NANOSECONDS_PER_SECOND); 59*bf01a04fSTommy Wu 60*bf01a04fSTommy Wu /* Clean the most significant bit. */ 61*bf01a04fSTommy Wu r->wdogcount &= R_AON_WDT_WDOGCOUNT_VALUE_MASK; 62*bf01a04fSTommy Wu r->wdog_restart_time = now; 63*bf01a04fSTommy Wu } 64*bf01a04fSTommy Wu 65*bf01a04fSTommy Wu static void sifive_e_aon_wdt_update_state(SiFiveEAONState *r) 66*bf01a04fSTommy Wu { 67*bf01a04fSTommy Wu uint16_t wdogs; 68*bf01a04fSTommy Wu bool cmp_signal = false; 69*bf01a04fSTommy Wu sifive_e_aon_wdt_update_wdogcount(r); 70*bf01a04fSTommy Wu wdogs = (uint16_t)(r->wdogcount >> 71*bf01a04fSTommy Wu FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE)); 72*bf01a04fSTommy Wu 73*bf01a04fSTommy Wu if (wdogs >= r->wdogcmp0) { 74*bf01a04fSTommy Wu cmp_signal = true; 75*bf01a04fSTommy Wu if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, ZEROCMP) == 1) { 76*bf01a04fSTommy Wu r->wdogcount = 0; 77*bf01a04fSTommy Wu wdogs = 0; 78*bf01a04fSTommy Wu } 79*bf01a04fSTommy Wu } 80*bf01a04fSTommy Wu 81*bf01a04fSTommy Wu if (cmp_signal) { 82*bf01a04fSTommy Wu if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN) == 1) { 83*bf01a04fSTommy Wu watchdog_perform_action(); 84*bf01a04fSTommy Wu } 85*bf01a04fSTommy Wu r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, IP0, 1); 86*bf01a04fSTommy Wu } 87*bf01a04fSTommy Wu 88*bf01a04fSTommy Wu qemu_set_irq(r->wdog_irq, FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, IP0)); 89*bf01a04fSTommy Wu 90*bf01a04fSTommy Wu if (wdogs < r->wdogcmp0 && 91*bf01a04fSTommy Wu (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 1 || 92*bf01a04fSTommy Wu FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 1)) { 93*bf01a04fSTommy Wu int64_t next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 94*bf01a04fSTommy Wu next += muldiv64((r->wdogcmp0 - wdogs) << 95*bf01a04fSTommy Wu FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE), 96*bf01a04fSTommy Wu NANOSECONDS_PER_SECOND, r->wdogclk_freq); 97*bf01a04fSTommy Wu timer_mod(r->wdog_timer, next); 98*bf01a04fSTommy Wu } else { 99*bf01a04fSTommy Wu timer_mod(r->wdog_timer, INT64_MAX); 100*bf01a04fSTommy Wu } 101*bf01a04fSTommy Wu } 102*bf01a04fSTommy Wu 103*bf01a04fSTommy Wu /* 104*bf01a04fSTommy Wu * Callback used when the timer set using timer_mod expires. 105*bf01a04fSTommy Wu */ 106*bf01a04fSTommy Wu static void sifive_e_aon_wdt_expired_cb(void *opaque) 107*bf01a04fSTommy Wu { 108*bf01a04fSTommy Wu SiFiveEAONState *r = SIFIVE_E_AON(opaque); 109*bf01a04fSTommy Wu sifive_e_aon_wdt_update_state(r); 110*bf01a04fSTommy Wu } 111*bf01a04fSTommy Wu 112*bf01a04fSTommy Wu static uint64_t 113*bf01a04fSTommy Wu sifive_e_aon_wdt_read(void *opaque, hwaddr addr, unsigned int size) 114*bf01a04fSTommy Wu { 115*bf01a04fSTommy Wu SiFiveEAONState *r = SIFIVE_E_AON(opaque); 116*bf01a04fSTommy Wu 117*bf01a04fSTommy Wu switch (addr) { 118*bf01a04fSTommy Wu case A_AON_WDT_WDOGCFG: 119*bf01a04fSTommy Wu return r->wdogcfg; 120*bf01a04fSTommy Wu case A_AON_WDT_WDOGCOUNT: 121*bf01a04fSTommy Wu sifive_e_aon_wdt_update_wdogcount(r); 122*bf01a04fSTommy Wu return r->wdogcount; 123*bf01a04fSTommy Wu case A_AON_WDT_WDOGS: 124*bf01a04fSTommy Wu sifive_e_aon_wdt_update_wdogcount(r); 125*bf01a04fSTommy Wu return r->wdogcount >> 126*bf01a04fSTommy Wu FIELD_EX32(r->wdogcfg, 127*bf01a04fSTommy Wu AON_WDT_WDOGCFG, 128*bf01a04fSTommy Wu SCALE); 129*bf01a04fSTommy Wu case A_AON_WDT_WDOGFEED: 130*bf01a04fSTommy Wu return 0; 131*bf01a04fSTommy Wu case A_AON_WDT_WDOGKEY: 132*bf01a04fSTommy Wu return r->wdogunlock; 133*bf01a04fSTommy Wu case A_AON_WDT_WDOGCMP0: 134*bf01a04fSTommy Wu return r->wdogcmp0; 135*bf01a04fSTommy Wu default: 136*bf01a04fSTommy Wu qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", 137*bf01a04fSTommy Wu __func__, (int)addr); 138*bf01a04fSTommy Wu } 139*bf01a04fSTommy Wu 140*bf01a04fSTommy Wu return 0; 141*bf01a04fSTommy Wu } 142*bf01a04fSTommy Wu 143*bf01a04fSTommy Wu static void 144*bf01a04fSTommy Wu sifive_e_aon_wdt_write(void *opaque, hwaddr addr, 145*bf01a04fSTommy Wu uint64_t val64, unsigned int size) 146*bf01a04fSTommy Wu { 147*bf01a04fSTommy Wu SiFiveEAONState *r = SIFIVE_E_AON(opaque); 148*bf01a04fSTommy Wu uint32_t value = val64; 149*bf01a04fSTommy Wu 150*bf01a04fSTommy Wu switch (addr) { 151*bf01a04fSTommy Wu case A_AON_WDT_WDOGCFG: { 152*bf01a04fSTommy Wu uint8_t new_en_always; 153*bf01a04fSTommy Wu uint8_t new_en_core_awake; 154*bf01a04fSTommy Wu uint8_t old_en_always; 155*bf01a04fSTommy Wu uint8_t old_en_core_awake; 156*bf01a04fSTommy Wu if (r->wdogunlock == 0) { 157*bf01a04fSTommy Wu return; 158*bf01a04fSTommy Wu } 159*bf01a04fSTommy Wu 160*bf01a04fSTommy Wu new_en_always = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_ALWAYS); 161*bf01a04fSTommy Wu new_en_core_awake = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_CORE_AWAKE); 162*bf01a04fSTommy Wu old_en_always = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS); 163*bf01a04fSTommy Wu old_en_core_awake = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, 164*bf01a04fSTommy Wu EN_CORE_AWAKE); 165*bf01a04fSTommy Wu 166*bf01a04fSTommy Wu if ((old_en_always || 167*bf01a04fSTommy Wu old_en_core_awake) == 1 && 168*bf01a04fSTommy Wu (new_en_always || 169*bf01a04fSTommy Wu new_en_core_awake) == 0) { 170*bf01a04fSTommy Wu sifive_e_aon_wdt_update_wdogcount(r); 171*bf01a04fSTommy Wu } else if ((old_en_always || 172*bf01a04fSTommy Wu old_en_core_awake) == 0 && 173*bf01a04fSTommy Wu (new_en_always || 174*bf01a04fSTommy Wu new_en_core_awake) == 1) { 175*bf01a04fSTommy Wu r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 176*bf01a04fSTommy Wu } 177*bf01a04fSTommy Wu r->wdogcfg = value; 178*bf01a04fSTommy Wu r->wdogunlock = 0; 179*bf01a04fSTommy Wu break; 180*bf01a04fSTommy Wu } 181*bf01a04fSTommy Wu case A_AON_WDT_WDOGCOUNT: 182*bf01a04fSTommy Wu if (r->wdogunlock == 0) { 183*bf01a04fSTommy Wu return; 184*bf01a04fSTommy Wu } 185*bf01a04fSTommy Wu r->wdogcount = value & R_AON_WDT_WDOGCOUNT_VALUE_MASK; 186*bf01a04fSTommy Wu r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 187*bf01a04fSTommy Wu r->wdogunlock = 0; 188*bf01a04fSTommy Wu break; 189*bf01a04fSTommy Wu case A_AON_WDT_WDOGS: 190*bf01a04fSTommy Wu return; 191*bf01a04fSTommy Wu case A_AON_WDT_WDOGFEED: 192*bf01a04fSTommy Wu if (r->wdogunlock == 0) { 193*bf01a04fSTommy Wu return; 194*bf01a04fSTommy Wu } 195*bf01a04fSTommy Wu if (value == SIFIVE_E_AON_WDOGFEED) { 196*bf01a04fSTommy Wu r->wdogcount = 0; 197*bf01a04fSTommy Wu r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 198*bf01a04fSTommy Wu } 199*bf01a04fSTommy Wu r->wdogunlock = 0; 200*bf01a04fSTommy Wu break; 201*bf01a04fSTommy Wu case A_AON_WDT_WDOGKEY: 202*bf01a04fSTommy Wu if (value == SIFIVE_E_AON_WDOGKEY) { 203*bf01a04fSTommy Wu r->wdogunlock = 1; 204*bf01a04fSTommy Wu } 205*bf01a04fSTommy Wu break; 206*bf01a04fSTommy Wu case A_AON_WDT_WDOGCMP0: 207*bf01a04fSTommy Wu if (r->wdogunlock == 0) { 208*bf01a04fSTommy Wu return; 209*bf01a04fSTommy Wu } 210*bf01a04fSTommy Wu r->wdogcmp0 = (uint16_t) value; 211*bf01a04fSTommy Wu r->wdogunlock = 0; 212*bf01a04fSTommy Wu break; 213*bf01a04fSTommy Wu default: 214*bf01a04fSTommy Wu qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n", 215*bf01a04fSTommy Wu __func__, (int)addr, (int)value); 216*bf01a04fSTommy Wu } 217*bf01a04fSTommy Wu sifive_e_aon_wdt_update_state(r); 218*bf01a04fSTommy Wu } 219*bf01a04fSTommy Wu 220*bf01a04fSTommy Wu static uint64_t 221*bf01a04fSTommy Wu sifive_e_aon_read(void *opaque, hwaddr addr, unsigned int size) 222*bf01a04fSTommy Wu { 223*bf01a04fSTommy Wu if (addr < SIFIVE_E_AON_RTC) { 224*bf01a04fSTommy Wu return sifive_e_aon_wdt_read(opaque, addr, size); 225*bf01a04fSTommy Wu } else if (addr < SIFIVE_E_AON_MAX) { 226*bf01a04fSTommy Wu qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read: addr=0x%x\n", 227*bf01a04fSTommy Wu __func__, (int)addr); 228*bf01a04fSTommy Wu } else { 229*bf01a04fSTommy Wu qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", 230*bf01a04fSTommy Wu __func__, (int)addr); 231*bf01a04fSTommy Wu } 232*bf01a04fSTommy Wu return 0; 233*bf01a04fSTommy Wu } 234*bf01a04fSTommy Wu 235*bf01a04fSTommy Wu static void 236*bf01a04fSTommy Wu sifive_e_aon_write(void *opaque, hwaddr addr, 237*bf01a04fSTommy Wu uint64_t val64, unsigned int size) 238*bf01a04fSTommy Wu { 239*bf01a04fSTommy Wu if (addr < SIFIVE_E_AON_RTC) { 240*bf01a04fSTommy Wu sifive_e_aon_wdt_write(opaque, addr, val64, size); 241*bf01a04fSTommy Wu } else if (addr < SIFIVE_E_AON_MAX) { 242*bf01a04fSTommy Wu qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write: addr=0x%x\n", 243*bf01a04fSTommy Wu __func__, (int)addr); 244*bf01a04fSTommy Wu } else { 245*bf01a04fSTommy Wu qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x\n", 246*bf01a04fSTommy Wu __func__, (int)addr); 247*bf01a04fSTommy Wu } 248*bf01a04fSTommy Wu } 249*bf01a04fSTommy Wu 250*bf01a04fSTommy Wu static const MemoryRegionOps sifive_e_aon_ops = { 251*bf01a04fSTommy Wu .read = sifive_e_aon_read, 252*bf01a04fSTommy Wu .write = sifive_e_aon_write, 253*bf01a04fSTommy Wu .endianness = DEVICE_NATIVE_ENDIAN, 254*bf01a04fSTommy Wu .impl = { 255*bf01a04fSTommy Wu .min_access_size = 4, 256*bf01a04fSTommy Wu .max_access_size = 4 257*bf01a04fSTommy Wu }, 258*bf01a04fSTommy Wu .valid = { 259*bf01a04fSTommy Wu .min_access_size = 4, 260*bf01a04fSTommy Wu .max_access_size = 4 261*bf01a04fSTommy Wu } 262*bf01a04fSTommy Wu }; 263*bf01a04fSTommy Wu 264*bf01a04fSTommy Wu static void sifive_e_aon_reset(DeviceState *dev) 265*bf01a04fSTommy Wu { 266*bf01a04fSTommy Wu SiFiveEAONState *r = SIFIVE_E_AON(dev); 267*bf01a04fSTommy Wu 268*bf01a04fSTommy Wu r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN, 0); 269*bf01a04fSTommy Wu r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0); 270*bf01a04fSTommy Wu r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE, 0); 271*bf01a04fSTommy Wu r->wdogcmp0 = 0xbeef; 272*bf01a04fSTommy Wu 273*bf01a04fSTommy Wu sifive_e_aon_wdt_update_state(r); 274*bf01a04fSTommy Wu } 275*bf01a04fSTommy Wu 276*bf01a04fSTommy Wu static void sifive_e_aon_init(Object *obj) 277*bf01a04fSTommy Wu { 278*bf01a04fSTommy Wu SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 279*bf01a04fSTommy Wu SiFiveEAONState *r = SIFIVE_E_AON(obj); 280*bf01a04fSTommy Wu 281*bf01a04fSTommy Wu memory_region_init_io(&r->mmio, OBJECT(r), &sifive_e_aon_ops, r, 282*bf01a04fSTommy Wu TYPE_SIFIVE_E_AON, SIFIVE_E_AON_MAX); 283*bf01a04fSTommy Wu sysbus_init_mmio(sbd, &r->mmio); 284*bf01a04fSTommy Wu 285*bf01a04fSTommy Wu /* watchdog timer */ 286*bf01a04fSTommy Wu r->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 287*bf01a04fSTommy Wu sifive_e_aon_wdt_expired_cb, r); 288*bf01a04fSTommy Wu r->wdogclk_freq = SIFIVE_E_LFCLK_DEFAULT_FREQ; 289*bf01a04fSTommy Wu sysbus_init_irq(sbd, &r->wdog_irq); 290*bf01a04fSTommy Wu } 291*bf01a04fSTommy Wu 292*bf01a04fSTommy Wu static Property sifive_e_aon_properties[] = { 293*bf01a04fSTommy Wu DEFINE_PROP_UINT64("wdogclk-frequency", SiFiveEAONState, wdogclk_freq, 294*bf01a04fSTommy Wu SIFIVE_E_LFCLK_DEFAULT_FREQ), 295*bf01a04fSTommy Wu DEFINE_PROP_END_OF_LIST(), 296*bf01a04fSTommy Wu }; 297*bf01a04fSTommy Wu 298*bf01a04fSTommy Wu static void sifive_e_aon_class_init(ObjectClass *oc, void *data) 299*bf01a04fSTommy Wu { 300*bf01a04fSTommy Wu DeviceClass *dc = DEVICE_CLASS(oc); 301*bf01a04fSTommy Wu 302*bf01a04fSTommy Wu dc->reset = sifive_e_aon_reset; 303*bf01a04fSTommy Wu device_class_set_props(dc, sifive_e_aon_properties); 304*bf01a04fSTommy Wu } 305*bf01a04fSTommy Wu 306*bf01a04fSTommy Wu static const TypeInfo sifive_e_aon_info = { 307*bf01a04fSTommy Wu .name = TYPE_SIFIVE_E_AON, 308*bf01a04fSTommy Wu .parent = TYPE_SYS_BUS_DEVICE, 309*bf01a04fSTommy Wu .instance_size = sizeof(SiFiveEAONState), 310*bf01a04fSTommy Wu .instance_init = sifive_e_aon_init, 311*bf01a04fSTommy Wu .class_init = sifive_e_aon_class_init, 312*bf01a04fSTommy Wu }; 313*bf01a04fSTommy Wu 314*bf01a04fSTommy Wu static void sifive_e_aon_register_types(void) 315*bf01a04fSTommy Wu { 316*bf01a04fSTommy Wu type_register_static(&sifive_e_aon_info); 317*bf01a04fSTommy Wu } 318*bf01a04fSTommy Wu 319*bf01a04fSTommy Wu type_init(sifive_e_aon_register_types) 320