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 "system/watchdog.h" 17 #include "hw/qdev-properties.h" 18 #include "hw/sysbus.h" 19 #include "hw/watchdog/wdt_aspeed.h" 20 #include "migration/vmstate.h" 21 #include "trace.h" 22 23 #define WDT_STATUS (0x00 / 4) 24 #define WDT_RELOAD_VALUE (0x04 / 4) 25 #define WDT_RESTART (0x08 / 4) 26 #define WDT_CTRL (0x0C / 4) 27 #define WDT_CTRL_RESET_MODE_SOC (0x00 << 5) 28 #define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5) 29 #define WDT_CTRL_1MHZ_CLK BIT(4) 30 #define WDT_CTRL_WDT_EXT BIT(3) 31 #define WDT_CTRL_WDT_INTR BIT(2) 32 #define WDT_CTRL_RESET_SYSTEM BIT(1) 33 #define WDT_CTRL_ENABLE BIT(0) 34 #define WDT_RESET_WIDTH (0x18 / 4) 35 #define WDT_RESET_WIDTH_ACTIVE_HIGH BIT(31) 36 #define WDT_POLARITY_MASK (0xFF << 24) 37 #define WDT_ACTIVE_HIGH_MAGIC (0xA5 << 24) 38 #define WDT_ACTIVE_LOW_MAGIC (0x5A << 24) 39 #define WDT_RESET_WIDTH_PUSH_PULL BIT(30) 40 #define WDT_DRIVE_TYPE_MASK (0xFF << 24) 41 #define WDT_PUSH_PULL_MAGIC (0xA8 << 24) 42 #define WDT_OPEN_DRAIN_MAGIC (0x8A << 24) 43 #define WDT_RESET_MASK1 (0x1c / 4) 44 #define WDT_RESET_MASK2 (0x20 / 4) 45 46 #define WDT_SW_RESET_CTRL (0x24 / 4) 47 #define WDT_SW_RESET_MASK1 (0x28 / 4) 48 #define WDT_SW_RESET_MASK2 (0x2c / 4) 49 50 #define WDT_TIMEOUT_STATUS (0x10 / 4) 51 #define WDT_TIMEOUT_CLEAR (0x14 / 4) 52 53 #define WDT_RESTART_MAGIC 0x4755 54 #define WDT_SW_RESET_ENABLE 0xAEEDF123 55 56 #define AST2600_SCU_RESET_CONTROL1 (0x40 / 4) 57 #define SCU_RESET_CONTROL1 (0x04 / 4) 58 #define SCU_RESET_SDRAM BIT(0) 59 60 static bool aspeed_wdt_is_soc_reset_mode(const AspeedWDTState *s) 61 { 62 uint32_t mode; 63 64 mode = extract32(s->regs[WDT_CTRL], 5, 2); 65 return (mode == WDT_CTRL_RESET_MODE_SOC); 66 } 67 68 static bool aspeed_wdt_is_enabled(const AspeedWDTState *s) 69 { 70 return s->regs[WDT_CTRL] & WDT_CTRL_ENABLE; 71 } 72 73 static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) 74 { 75 AspeedWDTState *s = ASPEED_WDT(opaque); 76 77 trace_aspeed_wdt_read(offset, size); 78 79 offset >>= 2; 80 81 switch (offset) { 82 case WDT_STATUS: 83 return s->regs[WDT_STATUS]; 84 case WDT_RELOAD_VALUE: 85 return s->regs[WDT_RELOAD_VALUE]; 86 case WDT_RESTART: 87 qemu_log_mask(LOG_GUEST_ERROR, 88 "%s: read from write-only reg at offset 0x%" 89 HWADDR_PRIx "\n", __func__, offset); 90 return 0; 91 case WDT_CTRL: 92 return s->regs[WDT_CTRL]; 93 case WDT_RESET_WIDTH: 94 return s->regs[WDT_RESET_WIDTH]; 95 case WDT_RESET_MASK1: 96 return s->regs[WDT_RESET_MASK1]; 97 case WDT_TIMEOUT_STATUS: 98 case WDT_TIMEOUT_CLEAR: 99 case WDT_RESET_MASK2: 100 case WDT_SW_RESET_CTRL: 101 case WDT_SW_RESET_MASK1: 102 case WDT_SW_RESET_MASK2: 103 qemu_log_mask(LOG_UNIMP, 104 "%s: uninmplemented read at offset 0x%" HWADDR_PRIx "\n", 105 __func__, offset); 106 return 0; 107 default: 108 qemu_log_mask(LOG_GUEST_ERROR, 109 "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", 110 __func__, offset); 111 return 0; 112 } 113 114 } 115 116 static void aspeed_wdt_reload(AspeedWDTState *s) 117 { 118 uint64_t reload; 119 120 if (!(s->regs[WDT_CTRL] & WDT_CTRL_1MHZ_CLK)) { 121 reload = muldiv64(s->regs[WDT_RELOAD_VALUE], NANOSECONDS_PER_SECOND, 122 s->pclk_freq); 123 } else { 124 reload = s->regs[WDT_RELOAD_VALUE] * 1000ULL; 125 } 126 127 if (aspeed_wdt_is_enabled(s)) { 128 timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload); 129 } 130 } 131 132 static void aspeed_wdt_reload_1mhz(AspeedWDTState *s) 133 { 134 uint64_t reload = s->regs[WDT_RELOAD_VALUE] * 1000ULL; 135 136 if (aspeed_wdt_is_enabled(s)) { 137 timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload); 138 } 139 } 140 141 static uint64_t aspeed_2400_sanitize_ctrl(uint64_t data) 142 { 143 return data & 0xffff; 144 } 145 146 static uint64_t aspeed_2500_sanitize_ctrl(uint64_t data) 147 { 148 return (data & ~(0xfUL << 8)) | WDT_CTRL_1MHZ_CLK; 149 } 150 151 static uint64_t aspeed_2600_sanitize_ctrl(uint64_t data) 152 { 153 return data & ~(0x7UL << 7); 154 } 155 156 static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, 157 unsigned size) 158 { 159 AspeedWDTState *s = ASPEED_WDT(opaque); 160 AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(s); 161 bool enable; 162 163 trace_aspeed_wdt_write(offset, size, data); 164 165 offset >>= 2; 166 167 switch (offset) { 168 case WDT_STATUS: 169 qemu_log_mask(LOG_GUEST_ERROR, 170 "%s: write to read-only reg at offset 0x%" 171 HWADDR_PRIx "\n", __func__, offset); 172 break; 173 case WDT_RELOAD_VALUE: 174 s->regs[WDT_RELOAD_VALUE] = data; 175 break; 176 case WDT_RESTART: 177 if ((data & 0xFFFF) == WDT_RESTART_MAGIC) { 178 s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE]; 179 awc->wdt_reload(s); 180 } 181 break; 182 case WDT_CTRL: 183 data = awc->sanitize_ctrl(data); 184 enable = data & WDT_CTRL_ENABLE; 185 if (enable && !aspeed_wdt_is_enabled(s)) { 186 s->regs[WDT_CTRL] = data; 187 awc->wdt_reload(s); 188 } else if (!enable && aspeed_wdt_is_enabled(s)) { 189 s->regs[WDT_CTRL] = data; 190 timer_del(s->timer); 191 } else { 192 s->regs[WDT_CTRL] = data; 193 } 194 break; 195 case WDT_RESET_WIDTH: 196 if (awc->reset_pulse) { 197 awc->reset_pulse(s, data & WDT_POLARITY_MASK); 198 } 199 s->regs[WDT_RESET_WIDTH] &= ~awc->ext_pulse_width_mask; 200 s->regs[WDT_RESET_WIDTH] |= data & awc->ext_pulse_width_mask; 201 break; 202 203 case WDT_RESET_MASK1: 204 /* TODO: implement */ 205 s->regs[WDT_RESET_MASK1] = data; 206 break; 207 208 case WDT_TIMEOUT_STATUS: 209 case WDT_TIMEOUT_CLEAR: 210 case WDT_RESET_MASK2: 211 case WDT_SW_RESET_MASK1: 212 case WDT_SW_RESET_MASK2: 213 qemu_log_mask(LOG_UNIMP, 214 "%s: uninmplemented write at offset 0x%" HWADDR_PRIx "\n", 215 __func__, offset); 216 break; 217 case WDT_SW_RESET_CTRL: 218 if (aspeed_wdt_is_soc_reset_mode(s) && 219 (data == WDT_SW_RESET_ENABLE)) { 220 watchdog_perform_action(); 221 } 222 break; 223 default: 224 qemu_log_mask(LOG_GUEST_ERROR, 225 "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", 226 __func__, offset); 227 } 228 return; 229 } 230 231 static const VMStateDescription vmstate_aspeed_wdt = { 232 .name = "vmstate_aspeed_wdt", 233 .version_id = 0, 234 .minimum_version_id = 0, 235 .fields = (const VMStateField[]) { 236 VMSTATE_TIMER_PTR(timer, AspeedWDTState), 237 VMSTATE_UINT32_ARRAY(regs, AspeedWDTState, ASPEED_WDT_REGS_MAX), 238 VMSTATE_END_OF_LIST() 239 } 240 }; 241 242 static const MemoryRegionOps aspeed_wdt_ops = { 243 .read = aspeed_wdt_read, 244 .write = aspeed_wdt_write, 245 .endianness = DEVICE_LITTLE_ENDIAN, 246 .valid.min_access_size = 4, 247 .valid.max_access_size = 4, 248 .valid.unaligned = false, 249 }; 250 251 static void aspeed_wdt_reset(DeviceState *dev) 252 { 253 AspeedWDTState *s = ASPEED_WDT(dev); 254 AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(s); 255 256 s->regs[WDT_STATUS] = awc->default_status; 257 s->regs[WDT_RELOAD_VALUE] = awc->default_reload_value; 258 s->regs[WDT_RESTART] = 0; 259 s->regs[WDT_CTRL] = awc->sanitize_ctrl(0); 260 s->regs[WDT_RESET_WIDTH] = 0xFF; 261 262 timer_del(s->timer); 263 } 264 265 static void aspeed_wdt_timer_expired(void *dev) 266 { 267 AspeedWDTState *s = ASPEED_WDT(dev); 268 uint32_t reset_ctrl_reg = ASPEED_WDT_GET_CLASS(s)->reset_ctrl_reg; 269 270 /* Do not reset on SDRAM controller reset */ 271 if (s->scu->regs[reset_ctrl_reg] & SCU_RESET_SDRAM) { 272 timer_del(s->timer); 273 s->regs[WDT_CTRL] = 0; 274 return; 275 } 276 277 qemu_log_mask(CPU_LOG_RESET, "Watchdog timer %" HWADDR_PRIx " expired.\n", 278 s->iomem.addr); 279 watchdog_perform_action(); 280 timer_del(s->timer); 281 } 282 283 #define PCLK_HZ 24000000 284 285 static void aspeed_wdt_realize(DeviceState *dev, Error **errp) 286 { 287 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 288 AspeedWDTState *s = ASPEED_WDT(dev); 289 AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(dev); 290 291 assert(s->scu); 292 293 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); 294 295 /* 296 * FIXME: This setting should be derived from the SCU hw strapping 297 * register SCU70 298 */ 299 s->pclk_freq = PCLK_HZ; 300 301 memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s, 302 TYPE_ASPEED_WDT, awc->iosize); 303 sysbus_init_mmio(sbd, &s->iomem); 304 } 305 306 static const Property aspeed_wdt_properties[] = { 307 DEFINE_PROP_LINK("scu", AspeedWDTState, scu, TYPE_ASPEED_SCU, 308 AspeedSCUState *), 309 }; 310 311 static void aspeed_wdt_class_init(ObjectClass *klass, void *data) 312 { 313 DeviceClass *dc = DEVICE_CLASS(klass); 314 315 dc->desc = "ASPEED Watchdog Controller"; 316 dc->realize = aspeed_wdt_realize; 317 device_class_set_legacy_reset(dc, aspeed_wdt_reset); 318 set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); 319 dc->vmsd = &vmstate_aspeed_wdt; 320 device_class_set_props(dc, aspeed_wdt_properties); 321 dc->desc = "Aspeed watchdog device"; 322 } 323 324 static const TypeInfo aspeed_wdt_info = { 325 .parent = TYPE_SYS_BUS_DEVICE, 326 .name = TYPE_ASPEED_WDT, 327 .instance_size = sizeof(AspeedWDTState), 328 .class_init = aspeed_wdt_class_init, 329 .class_size = sizeof(AspeedWDTClass), 330 .abstract = true, 331 }; 332 333 static void aspeed_2400_wdt_class_init(ObjectClass *klass, void *data) 334 { 335 DeviceClass *dc = DEVICE_CLASS(klass); 336 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 337 338 dc->desc = "ASPEED 2400 Watchdog Controller"; 339 awc->iosize = 0x20; 340 awc->ext_pulse_width_mask = 0xff; 341 awc->reset_ctrl_reg = SCU_RESET_CONTROL1; 342 awc->wdt_reload = aspeed_wdt_reload; 343 awc->sanitize_ctrl = aspeed_2400_sanitize_ctrl; 344 awc->default_status = 0x03EF1480; 345 awc->default_reload_value = 0x03EF1480; 346 } 347 348 static const TypeInfo aspeed_2400_wdt_info = { 349 .name = TYPE_ASPEED_2400_WDT, 350 .parent = TYPE_ASPEED_WDT, 351 .instance_size = sizeof(AspeedWDTState), 352 .class_init = aspeed_2400_wdt_class_init, 353 }; 354 355 static void aspeed_2500_wdt_reset_pulse(AspeedWDTState *s, uint32_t property) 356 { 357 if (property) { 358 if (property == WDT_ACTIVE_HIGH_MAGIC) { 359 s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_ACTIVE_HIGH; 360 } else if (property == WDT_ACTIVE_LOW_MAGIC) { 361 s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_ACTIVE_HIGH; 362 } else if (property == WDT_PUSH_PULL_MAGIC) { 363 s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_PUSH_PULL; 364 } else if (property == WDT_OPEN_DRAIN_MAGIC) { 365 s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_PUSH_PULL; 366 } 367 } 368 } 369 370 static void aspeed_2500_wdt_class_init(ObjectClass *klass, void *data) 371 { 372 DeviceClass *dc = DEVICE_CLASS(klass); 373 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 374 375 dc->desc = "ASPEED 2500 Watchdog Controller"; 376 awc->iosize = 0x20; 377 awc->ext_pulse_width_mask = 0xfffff; 378 awc->reset_ctrl_reg = SCU_RESET_CONTROL1; 379 awc->reset_pulse = aspeed_2500_wdt_reset_pulse; 380 awc->wdt_reload = aspeed_wdt_reload_1mhz; 381 awc->sanitize_ctrl = aspeed_2500_sanitize_ctrl; 382 awc->default_status = 0x014FB180; 383 awc->default_reload_value = 0x014FB180; 384 } 385 386 static const TypeInfo aspeed_2500_wdt_info = { 387 .name = TYPE_ASPEED_2500_WDT, 388 .parent = TYPE_ASPEED_WDT, 389 .instance_size = sizeof(AspeedWDTState), 390 .class_init = aspeed_2500_wdt_class_init, 391 }; 392 393 static void aspeed_2600_wdt_class_init(ObjectClass *klass, void *data) 394 { 395 DeviceClass *dc = DEVICE_CLASS(klass); 396 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 397 398 dc->desc = "ASPEED 2600 Watchdog Controller"; 399 awc->iosize = 0x40; 400 awc->ext_pulse_width_mask = 0xfffff; /* TODO */ 401 awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; 402 awc->reset_pulse = aspeed_2500_wdt_reset_pulse; 403 awc->wdt_reload = aspeed_wdt_reload_1mhz; 404 awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; 405 awc->default_status = 0x014FB180; 406 awc->default_reload_value = 0x014FB180; 407 } 408 409 static const TypeInfo aspeed_2600_wdt_info = { 410 .name = TYPE_ASPEED_2600_WDT, 411 .parent = TYPE_ASPEED_WDT, 412 .instance_size = sizeof(AspeedWDTState), 413 .class_init = aspeed_2600_wdt_class_init, 414 }; 415 416 static void aspeed_1030_wdt_class_init(ObjectClass *klass, void *data) 417 { 418 DeviceClass *dc = DEVICE_CLASS(klass); 419 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 420 421 dc->desc = "ASPEED 1030 Watchdog Controller"; 422 awc->iosize = 0x80; 423 awc->ext_pulse_width_mask = 0xfffff; /* TODO */ 424 awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; 425 awc->reset_pulse = aspeed_2500_wdt_reset_pulse; 426 awc->wdt_reload = aspeed_wdt_reload_1mhz; 427 awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; 428 awc->default_status = 0x014FB180; 429 awc->default_reload_value = 0x014FB180; 430 } 431 432 static const TypeInfo aspeed_1030_wdt_info = { 433 .name = TYPE_ASPEED_1030_WDT, 434 .parent = TYPE_ASPEED_WDT, 435 .instance_size = sizeof(AspeedWDTState), 436 .class_init = aspeed_1030_wdt_class_init, 437 }; 438 439 static void aspeed_2700_wdt_class_init(ObjectClass *klass, void *data) 440 { 441 DeviceClass *dc = DEVICE_CLASS(klass); 442 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 443 444 dc->desc = "ASPEED 2700 Watchdog Controller"; 445 awc->iosize = 0x80; 446 awc->ext_pulse_width_mask = 0xfffff; /* TODO */ 447 awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; 448 awc->reset_pulse = aspeed_2500_wdt_reset_pulse; 449 awc->wdt_reload = aspeed_wdt_reload_1mhz; 450 awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; 451 awc->default_status = 0x014FB180; 452 awc->default_reload_value = 0x014FB180; 453 } 454 455 static const TypeInfo aspeed_2700_wdt_info = { 456 .name = TYPE_ASPEED_2700_WDT, 457 .parent = TYPE_ASPEED_WDT, 458 .instance_size = sizeof(AspeedWDTState), 459 .class_init = aspeed_2700_wdt_class_init, 460 }; 461 462 static void wdt_aspeed_register_types(void) 463 { 464 type_register_static(&aspeed_wdt_info); 465 type_register_static(&aspeed_2400_wdt_info); 466 type_register_static(&aspeed_2500_wdt_info); 467 type_register_static(&aspeed_2600_wdt_info); 468 type_register_static(&aspeed_2700_wdt_info); 469 type_register_static(&aspeed_1030_wdt_info); 470 } 471 472 type_init(wdt_aspeed_register_types) 473