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