1 /* 2 * ASPEED Watchdog Controller 3 * 4 * Copyright (C) 2016-2017 IBM Corp. 5 * 6 * This code is licensed under the GPL version 2 or later. See the 7 * COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 12 #include "qapi/error.h" 13 #include "qemu/log.h" 14 #include "qemu/module.h" 15 #include "qemu/timer.h" 16 #include "sysemu/watchdog.h" 17 #include "hw/misc/aspeed_scu.h" 18 #include "hw/sysbus.h" 19 #include "hw/watchdog/wdt_aspeed.h" 20 #include "migration/vmstate.h" 21 22 #define WDT_STATUS (0x00 / 4) 23 #define WDT_RELOAD_VALUE (0x04 / 4) 24 #define WDT_RESTART (0x08 / 4) 25 #define WDT_CTRL (0x0C / 4) 26 #define WDT_CTRL_RESET_MODE_SOC (0x00 << 5) 27 #define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5) 28 #define WDT_CTRL_1MHZ_CLK BIT(4) 29 #define WDT_CTRL_WDT_EXT BIT(3) 30 #define WDT_CTRL_WDT_INTR BIT(2) 31 #define WDT_CTRL_RESET_SYSTEM BIT(1) 32 #define WDT_CTRL_ENABLE BIT(0) 33 #define WDT_RESET_WIDTH (0x18 / 4) 34 #define WDT_RESET_WIDTH_ACTIVE_HIGH BIT(31) 35 #define WDT_POLARITY_MASK (0xFF << 24) 36 #define WDT_ACTIVE_HIGH_MAGIC (0xA5 << 24) 37 #define WDT_ACTIVE_LOW_MAGIC (0x5A << 24) 38 #define WDT_RESET_WIDTH_PUSH_PULL BIT(30) 39 #define WDT_DRIVE_TYPE_MASK (0xFF << 24) 40 #define WDT_PUSH_PULL_MAGIC (0xA8 << 24) 41 #define WDT_OPEN_DRAIN_MAGIC (0x8A << 24) 42 43 #define WDT_TIMEOUT_STATUS (0x10 / 4) 44 #define WDT_TIMEOUT_CLEAR (0x14 / 4) 45 46 #define WDT_RESTART_MAGIC 0x4755 47 48 #define SCU_RESET_CONTROL1 (0x04 / 4) 49 #define SCU_RESET_SDRAM BIT(0) 50 51 static bool aspeed_wdt_is_enabled(const AspeedWDTState *s) 52 { 53 return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE; 54 } 55 56 static bool is_ast2500(const AspeedWDTState *s) 57 { 58 switch (s->silicon_rev) { 59 case AST2500_A0_SILICON_REV: 60 case AST2500_A1_SILICON_REV: 61 return true; 62 case AST2400_A0_SILICON_REV: 63 case AST2400_A1_SILICON_REV: 64 default: 65 break; 66 } 67 68 return false; 69 } 70 71 static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) 72 { 73 AspeedWDTState *s = ASPEED_WDT(opaque); 74 75 offset >>= 2; 76 77 switch (offset) { 78 case WDT_STATUS: 79 return s->regs[WDT_STATUS]; 80 case WDT_RELOAD_VALUE: 81 return s->regs[WDT_RELOAD_VALUE]; 82 case WDT_RESTART: 83 qemu_log_mask(LOG_GUEST_ERROR, 84 "%s: read from write-only reg at offset 0x%" 85 HWADDR_PRIx "\n", __func__, offset); 86 return 0; 87 case WDT_CTRL: 88 return s->regs[WDT_CTRL]; 89 case WDT_RESET_WIDTH: 90 return s->regs[WDT_RESET_WIDTH]; 91 case WDT_TIMEOUT_STATUS: 92 case WDT_TIMEOUT_CLEAR: 93 qemu_log_mask(LOG_UNIMP, 94 "%s: uninmplemented read at offset 0x%" HWADDR_PRIx "\n", 95 __func__, offset); 96 return 0; 97 default: 98 qemu_log_mask(LOG_GUEST_ERROR, 99 "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", 100 __func__, offset); 101 return 0; 102 } 103 104 } 105 106 static void aspeed_wdt_reload(AspeedWDTState *s, bool pclk) 107 { 108 uint64_t reload; 109 110 if (pclk) { 111 reload = muldiv64(s->regs[WDT_RELOAD_VALUE], NANOSECONDS_PER_SECOND, 112 s->pclk_freq); 113 } else { 114 reload = s->regs[WDT_RELOAD_VALUE] * 1000ULL; 115 } 116 117 if (aspeed_wdt_is_enabled(s)) { 118 timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload); 119 } 120 } 121 122 static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, 123 unsigned size) 124 { 125 AspeedWDTState *s = ASPEED_WDT(opaque); 126 bool enable = data & WDT_CTRL_ENABLE; 127 128 offset >>= 2; 129 130 switch (offset) { 131 case WDT_STATUS: 132 qemu_log_mask(LOG_GUEST_ERROR, 133 "%s: write to read-only reg at offset 0x%" 134 HWADDR_PRIx "\n", __func__, offset); 135 break; 136 case WDT_RELOAD_VALUE: 137 s->regs[WDT_RELOAD_VALUE] = data; 138 break; 139 case WDT_RESTART: 140 if ((data & 0xFFFF) == WDT_RESTART_MAGIC) { 141 s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE]; 142 aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); 143 } 144 break; 145 case WDT_CTRL: 146 if (enable && !aspeed_wdt_is_enabled(s)) { 147 s->regs[WDT_CTRL] = data; 148 aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); 149 } else if (!enable && aspeed_wdt_is_enabled(s)) { 150 s->regs[WDT_CTRL] = data; 151 timer_del(s->timer); 152 } 153 break; 154 case WDT_RESET_WIDTH: 155 { 156 uint32_t property = data & WDT_POLARITY_MASK; 157 158 if (property && is_ast2500(s)) { 159 if (property == WDT_ACTIVE_HIGH_MAGIC) { 160 s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_ACTIVE_HIGH; 161 } else if (property == WDT_ACTIVE_LOW_MAGIC) { 162 s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_ACTIVE_HIGH; 163 } else if (property == WDT_PUSH_PULL_MAGIC) { 164 s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_PUSH_PULL; 165 } else if (property == WDT_OPEN_DRAIN_MAGIC) { 166 s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_PUSH_PULL; 167 } 168 } 169 s->regs[WDT_RESET_WIDTH] &= ~s->ext_pulse_width_mask; 170 s->regs[WDT_RESET_WIDTH] |= data & s->ext_pulse_width_mask; 171 break; 172 } 173 case WDT_TIMEOUT_STATUS: 174 case WDT_TIMEOUT_CLEAR: 175 qemu_log_mask(LOG_UNIMP, 176 "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n", 177 __func__, offset); 178 break; 179 default: 180 qemu_log_mask(LOG_GUEST_ERROR, 181 "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", 182 __func__, offset); 183 } 184 return; 185 } 186 187 static WatchdogTimerModel model = { 188 .wdt_name = TYPE_ASPEED_WDT, 189 .wdt_description = "Aspeed watchdog device", 190 }; 191 192 static const VMStateDescription vmstate_aspeed_wdt = { 193 .name = "vmstate_aspeed_wdt", 194 .version_id = 0, 195 .minimum_version_id = 0, 196 .fields = (VMStateField[]) { 197 VMSTATE_TIMER_PTR(timer, AspeedWDTState), 198 VMSTATE_UINT32_ARRAY(regs, AspeedWDTState, ASPEED_WDT_REGS_MAX), 199 VMSTATE_END_OF_LIST() 200 } 201 }; 202 203 static const MemoryRegionOps aspeed_wdt_ops = { 204 .read = aspeed_wdt_read, 205 .write = aspeed_wdt_write, 206 .endianness = DEVICE_LITTLE_ENDIAN, 207 .valid.min_access_size = 4, 208 .valid.max_access_size = 4, 209 .valid.unaligned = false, 210 }; 211 212 static void aspeed_wdt_reset(DeviceState *dev) 213 { 214 AspeedWDTState *s = ASPEED_WDT(dev); 215 216 s->regs[WDT_STATUS] = 0x3EF1480; 217 s->regs[WDT_RELOAD_VALUE] = 0x03EF1480; 218 s->regs[WDT_RESTART] = 0; 219 s->regs[WDT_CTRL] = 0; 220 s->regs[WDT_RESET_WIDTH] = 0xFF; 221 222 timer_del(s->timer); 223 } 224 225 static void aspeed_wdt_timer_expired(void *dev) 226 { 227 AspeedWDTState *s = ASPEED_WDT(dev); 228 229 /* Do not reset on SDRAM controller reset */ 230 if (s->scu->regs[SCU_RESET_CONTROL1] & SCU_RESET_SDRAM) { 231 timer_del(s->timer); 232 s->regs[WDT_CTRL] = 0; 233 return; 234 } 235 236 qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n"); 237 watchdog_perform_action(); 238 timer_del(s->timer); 239 } 240 241 #define PCLK_HZ 24000000 242 243 static void aspeed_wdt_realize(DeviceState *dev, Error **errp) 244 { 245 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 246 AspeedWDTState *s = ASPEED_WDT(dev); 247 Error *err = NULL; 248 Object *obj; 249 250 obj = object_property_get_link(OBJECT(dev), "scu", &err); 251 if (!obj) { 252 error_propagate(errp, err); 253 error_prepend(errp, "required link 'scu' not found: "); 254 return; 255 } 256 s->scu = ASPEED_SCU(obj); 257 258 if (!is_supported_silicon_rev(s->silicon_rev)) { 259 error_setg(errp, "Unknown silicon revision: 0x%" PRIx32, 260 s->silicon_rev); 261 return; 262 } 263 264 switch (s->silicon_rev) { 265 case AST2400_A0_SILICON_REV: 266 case AST2400_A1_SILICON_REV: 267 s->ext_pulse_width_mask = 0xff; 268 break; 269 case AST2500_A0_SILICON_REV: 270 case AST2500_A1_SILICON_REV: 271 s->ext_pulse_width_mask = 0xfffff; 272 break; 273 default: 274 g_assert_not_reached(); 275 } 276 277 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); 278 279 /* FIXME: This setting should be derived from the SCU hw strapping 280 * register SCU70 281 */ 282 s->pclk_freq = PCLK_HZ; 283 284 memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s, 285 TYPE_ASPEED_WDT, ASPEED_WDT_REGS_MAX * 4); 286 sysbus_init_mmio(sbd, &s->iomem); 287 } 288 289 static Property aspeed_wdt_properties[] = { 290 DEFINE_PROP_UINT32("silicon-rev", AspeedWDTState, silicon_rev, 0), 291 DEFINE_PROP_END_OF_LIST(), 292 }; 293 294 static void aspeed_wdt_class_init(ObjectClass *klass, void *data) 295 { 296 DeviceClass *dc = DEVICE_CLASS(klass); 297 298 dc->realize = aspeed_wdt_realize; 299 dc->reset = aspeed_wdt_reset; 300 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 301 dc->vmsd = &vmstate_aspeed_wdt; 302 dc->props = aspeed_wdt_properties; 303 } 304 305 static const TypeInfo aspeed_wdt_info = { 306 .parent = TYPE_SYS_BUS_DEVICE, 307 .name = TYPE_ASPEED_WDT, 308 .instance_size = sizeof(AspeedWDTState), 309 .class_init = aspeed_wdt_class_init, 310 }; 311 312 static void wdt_aspeed_register_types(void) 313 { 314 watchdog_add_model(&model); 315 type_register_static(&aspeed_wdt_info); 316 } 317 318 type_init(wdt_aspeed_register_types) 319