wdt_aspeed.c 11 KB

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