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 } 229 230 static const VMStateDescription vmstate_aspeed_wdt = { 231 .name = "vmstate_aspeed_wdt", 232 .version_id = 0, 233 .minimum_version_id = 0, 234 .fields = (const VMStateField[]) { 235 VMSTATE_TIMER_PTR(timer, AspeedWDTState), 236 VMSTATE_UINT32_ARRAY(regs, AspeedWDTState, ASPEED_WDT_REGS_MAX), 237 VMSTATE_END_OF_LIST() 238 } 239 }; 240 241 static const MemoryRegionOps aspeed_wdt_ops = { 242 .read = aspeed_wdt_read, 243 .write = aspeed_wdt_write, 244 .endianness = DEVICE_LITTLE_ENDIAN, 245 .valid.min_access_size = 4, 246 .valid.max_access_size = 4, 247 .valid.unaligned = false, 248 }; 249 250 static void aspeed_wdt_reset(DeviceState *dev) 251 { 252 AspeedWDTState *s = ASPEED_WDT(dev); 253 AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(s); 254 255 s->regs[WDT_STATUS] = awc->default_status; 256 s->regs[WDT_RELOAD_VALUE] = awc->default_reload_value; 257 s->regs[WDT_RESTART] = 0; 258 s->regs[WDT_CTRL] = awc->sanitize_ctrl(0); 259 s->regs[WDT_RESET_WIDTH] = 0xFF; 260 261 timer_del(s->timer); 262 } 263 264 static void aspeed_wdt_timer_expired(void *dev) 265 { 266 AspeedWDTState *s = ASPEED_WDT(dev); 267 uint32_t reset_ctrl_reg = ASPEED_WDT_GET_CLASS(s)->reset_ctrl_reg; 268 269 /* Do not reset on SDRAM controller reset */ 270 if (s->scu->regs[reset_ctrl_reg] & SCU_RESET_SDRAM) { 271 timer_del(s->timer); 272 s->regs[WDT_CTRL] = 0; 273 return; 274 } 275 276 qemu_log_mask(CPU_LOG_RESET, "Watchdog timer %" HWADDR_PRIx " expired.\n", 277 s->iomem.addr); 278 watchdog_perform_action(); 279 timer_del(s->timer); 280 } 281 282 #define PCLK_HZ 24000000 283 284 static void aspeed_wdt_realize(DeviceState *dev, Error **errp) 285 { 286 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 287 AspeedWDTState *s = ASPEED_WDT(dev); 288 AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(dev); 289 290 assert(s->scu); 291 292 s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); 293 294 /* 295 * FIXME: This setting should be derived from the SCU hw strapping 296 * register SCU70 297 */ 298 s->pclk_freq = PCLK_HZ; 299 300 memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_wdt_ops, s, 301 TYPE_ASPEED_WDT, awc->iosize); 302 sysbus_init_mmio(sbd, &s->iomem); 303 } 304 305 static const Property aspeed_wdt_properties[] = { 306 DEFINE_PROP_LINK("scu", AspeedWDTState, scu, TYPE_ASPEED_SCU, 307 AspeedSCUState *), 308 }; 309 310 static void aspeed_wdt_class_init(ObjectClass *klass, const void *data) 311 { 312 DeviceClass *dc = DEVICE_CLASS(klass); 313 314 dc->desc = "ASPEED Watchdog Controller"; 315 dc->realize = aspeed_wdt_realize; 316 device_class_set_legacy_reset(dc, aspeed_wdt_reset); 317 set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); 318 dc->vmsd = &vmstate_aspeed_wdt; 319 device_class_set_props(dc, aspeed_wdt_properties); 320 dc->desc = "Aspeed watchdog device"; 321 } 322 323 static const TypeInfo aspeed_wdt_info = { 324 .parent = TYPE_SYS_BUS_DEVICE, 325 .name = TYPE_ASPEED_WDT, 326 .instance_size = sizeof(AspeedWDTState), 327 .class_init = aspeed_wdt_class_init, 328 .class_size = sizeof(AspeedWDTClass), 329 .abstract = true, 330 }; 331 332 static void aspeed_2400_wdt_class_init(ObjectClass *klass, const void *data) 333 { 334 DeviceClass *dc = DEVICE_CLASS(klass); 335 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 336 337 dc->desc = "ASPEED 2400 Watchdog Controller"; 338 awc->iosize = 0x20; 339 awc->ext_pulse_width_mask = 0xff; 340 awc->reset_ctrl_reg = SCU_RESET_CONTROL1; 341 awc->wdt_reload = aspeed_wdt_reload; 342 awc->sanitize_ctrl = aspeed_2400_sanitize_ctrl; 343 awc->default_status = 0x03EF1480; 344 awc->default_reload_value = 0x03EF1480; 345 } 346 347 static const TypeInfo aspeed_2400_wdt_info = { 348 .name = TYPE_ASPEED_2400_WDT, 349 .parent = TYPE_ASPEED_WDT, 350 .instance_size = sizeof(AspeedWDTState), 351 .class_init = aspeed_2400_wdt_class_init, 352 }; 353 354 static void aspeed_2500_wdt_reset_pulse(AspeedWDTState *s, uint32_t property) 355 { 356 if (property) { 357 if (property == WDT_ACTIVE_HIGH_MAGIC) { 358 s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_ACTIVE_HIGH; 359 } else if (property == WDT_ACTIVE_LOW_MAGIC) { 360 s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_ACTIVE_HIGH; 361 } else if (property == WDT_PUSH_PULL_MAGIC) { 362 s->regs[WDT_RESET_WIDTH] |= WDT_RESET_WIDTH_PUSH_PULL; 363 } else if (property == WDT_OPEN_DRAIN_MAGIC) { 364 s->regs[WDT_RESET_WIDTH] &= ~WDT_RESET_WIDTH_PUSH_PULL; 365 } 366 } 367 } 368 369 static void aspeed_2500_wdt_class_init(ObjectClass *klass, const void *data) 370 { 371 DeviceClass *dc = DEVICE_CLASS(klass); 372 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 373 374 dc->desc = "ASPEED 2500 Watchdog Controller"; 375 awc->iosize = 0x20; 376 awc->ext_pulse_width_mask = 0xfffff; 377 awc->reset_ctrl_reg = SCU_RESET_CONTROL1; 378 awc->reset_pulse = aspeed_2500_wdt_reset_pulse; 379 awc->wdt_reload = aspeed_wdt_reload_1mhz; 380 awc->sanitize_ctrl = aspeed_2500_sanitize_ctrl; 381 awc->default_status = 0x014FB180; 382 awc->default_reload_value = 0x014FB180; 383 } 384 385 static const TypeInfo aspeed_2500_wdt_info = { 386 .name = TYPE_ASPEED_2500_WDT, 387 .parent = TYPE_ASPEED_WDT, 388 .instance_size = sizeof(AspeedWDTState), 389 .class_init = aspeed_2500_wdt_class_init, 390 }; 391 392 static void aspeed_2600_wdt_class_init(ObjectClass *klass, const void *data) 393 { 394 DeviceClass *dc = DEVICE_CLASS(klass); 395 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 396 397 dc->desc = "ASPEED 2600 Watchdog Controller"; 398 awc->iosize = 0x40; 399 awc->ext_pulse_width_mask = 0xfffff; /* TODO */ 400 awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; 401 awc->reset_pulse = aspeed_2500_wdt_reset_pulse; 402 awc->wdt_reload = aspeed_wdt_reload_1mhz; 403 awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; 404 awc->default_status = 0x014FB180; 405 awc->default_reload_value = 0x014FB180; 406 } 407 408 static const TypeInfo aspeed_2600_wdt_info = { 409 .name = TYPE_ASPEED_2600_WDT, 410 .parent = TYPE_ASPEED_WDT, 411 .instance_size = sizeof(AspeedWDTState), 412 .class_init = aspeed_2600_wdt_class_init, 413 }; 414 415 static void aspeed_1030_wdt_class_init(ObjectClass *klass, const void *data) 416 { 417 DeviceClass *dc = DEVICE_CLASS(klass); 418 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 419 420 dc->desc = "ASPEED 1030 Watchdog Controller"; 421 awc->iosize = 0x80; 422 awc->ext_pulse_width_mask = 0xfffff; /* TODO */ 423 awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; 424 awc->reset_pulse = aspeed_2500_wdt_reset_pulse; 425 awc->wdt_reload = aspeed_wdt_reload_1mhz; 426 awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; 427 awc->default_status = 0x014FB180; 428 awc->default_reload_value = 0x014FB180; 429 } 430 431 static const TypeInfo aspeed_1030_wdt_info = { 432 .name = TYPE_ASPEED_1030_WDT, 433 .parent = TYPE_ASPEED_WDT, 434 .instance_size = sizeof(AspeedWDTState), 435 .class_init = aspeed_1030_wdt_class_init, 436 }; 437 438 static void aspeed_2700_wdt_class_init(ObjectClass *klass, const void *data) 439 { 440 DeviceClass *dc = DEVICE_CLASS(klass); 441 AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); 442 443 dc->desc = "ASPEED 2700 Watchdog Controller"; 444 awc->iosize = 0x80; 445 awc->ext_pulse_width_mask = 0xfffff; /* TODO */ 446 awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; 447 awc->reset_pulse = aspeed_2500_wdt_reset_pulse; 448 awc->wdt_reload = aspeed_wdt_reload_1mhz; 449 awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; 450 awc->default_status = 0x014FB180; 451 awc->default_reload_value = 0x014FB180; 452 } 453 454 static const TypeInfo aspeed_2700_wdt_info = { 455 .name = TYPE_ASPEED_2700_WDT, 456 .parent = TYPE_ASPEED_WDT, 457 .instance_size = sizeof(AspeedWDTState), 458 .class_init = aspeed_2700_wdt_class_init, 459 }; 460 461 static void wdt_aspeed_register_types(void) 462 { 463 type_register_static(&aspeed_wdt_info); 464 type_register_static(&aspeed_2400_wdt_info); 465 type_register_static(&aspeed_2500_wdt_info); 466 type_register_static(&aspeed_2600_wdt_info); 467 type_register_static(&aspeed_2700_wdt_info); 468 type_register_static(&aspeed_1030_wdt_info); 469 } 470 471 type_init(wdt_aspeed_register_types) 472