wdt_aspeed.c 14 KB

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