2
0

wdt_imx2.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * Copyright (c) 2018, Impinj, Inc.
  3. *
  4. * i.MX2 Watchdog IP block
  5. *
  6. * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
  7. *
  8. * This work is licensed under the terms of the GNU GPL, version 2 or later.
  9. * See the COPYING file in the top-level directory.
  10. */
  11. #include "qemu/osdep.h"
  12. #include "qemu/bitops.h"
  13. #include "qemu/module.h"
  14. #include "system/watchdog.h"
  15. #include "migration/vmstate.h"
  16. #include "hw/qdev-properties.h"
  17. #include "hw/watchdog/wdt_imx2.h"
  18. #include "trace.h"
  19. static void imx2_wdt_interrupt(void *opaque)
  20. {
  21. IMX2WdtState *s = IMX2_WDT(opaque);
  22. trace_imx2_wdt_interrupt();
  23. s->wicr |= IMX2_WDT_WICR_WTIS;
  24. qemu_set_irq(s->irq, 1);
  25. }
  26. static void imx2_wdt_expired(void *opaque)
  27. {
  28. IMX2WdtState *s = IMX2_WDT(opaque);
  29. trace_imx2_wdt_expired();
  30. s->wrsr = IMX2_WDT_WRSR_TOUT;
  31. /* Perform watchdog action if watchdog is enabled */
  32. if (s->wcr & IMX2_WDT_WCR_WDE) {
  33. watchdog_perform_action();
  34. }
  35. }
  36. static void imx2_wdt_reset(DeviceState *dev)
  37. {
  38. IMX2WdtState *s = IMX2_WDT(dev);
  39. ptimer_transaction_begin(s->timer);
  40. ptimer_stop(s->timer);
  41. ptimer_transaction_commit(s->timer);
  42. if (s->pretimeout_support) {
  43. ptimer_transaction_begin(s->itimer);
  44. ptimer_stop(s->itimer);
  45. ptimer_transaction_commit(s->itimer);
  46. }
  47. s->wicr_locked = false;
  48. s->wcr_locked = false;
  49. s->wcr_wde_locked = false;
  50. s->wcr = IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS;
  51. s->wsr = 0;
  52. s->wrsr &= ~(IMX2_WDT_WRSR_TOUT | IMX2_WDT_WRSR_SFTW);
  53. s->wicr = IMX2_WDT_WICR_WICT_DEF;
  54. s->wmcr = IMX2_WDT_WMCR_PDE;
  55. }
  56. static uint64_t imx2_wdt_read(void *opaque, hwaddr addr, unsigned int size)
  57. {
  58. IMX2WdtState *s = IMX2_WDT(opaque);
  59. uint16_t value = 0;
  60. switch (addr) {
  61. case IMX2_WDT_WCR:
  62. value = s->wcr;
  63. break;
  64. case IMX2_WDT_WSR:
  65. value = s->wsr;
  66. break;
  67. case IMX2_WDT_WRSR:
  68. value = s->wrsr;
  69. break;
  70. case IMX2_WDT_WICR:
  71. value = s->wicr;
  72. break;
  73. case IMX2_WDT_WMCR:
  74. value = s->wmcr;
  75. break;
  76. }
  77. trace_imx2_wdt_read(addr, value);
  78. return value;
  79. }
  80. static void imx_wdt2_update_itimer(IMX2WdtState *s, bool start)
  81. {
  82. bool running = (s->wcr & IMX2_WDT_WCR_WDE) && (s->wcr & IMX2_WDT_WCR_WT);
  83. bool enabled = s->wicr & IMX2_WDT_WICR_WIE;
  84. ptimer_transaction_begin(s->itimer);
  85. if (start || !enabled) {
  86. ptimer_stop(s->itimer);
  87. }
  88. if (running && enabled) {
  89. int count = ptimer_get_count(s->timer);
  90. int pretimeout = s->wicr & IMX2_WDT_WICR_WICT;
  91. /*
  92. * Only (re-)start pretimeout timer if its counter value is larger
  93. * than 0. Otherwise it will fire right away and we'll get an
  94. * interrupt loop.
  95. */
  96. if (count > pretimeout) {
  97. ptimer_set_count(s->itimer, count - pretimeout);
  98. if (start) {
  99. ptimer_run(s->itimer, 1);
  100. }
  101. }
  102. }
  103. ptimer_transaction_commit(s->itimer);
  104. }
  105. static void imx_wdt2_update_timer(IMX2WdtState *s, bool start)
  106. {
  107. ptimer_transaction_begin(s->timer);
  108. if (start) {
  109. ptimer_stop(s->timer);
  110. }
  111. if ((s->wcr & IMX2_WDT_WCR_WDE) && (s->wcr & IMX2_WDT_WCR_WT)) {
  112. int count = (s->wcr & IMX2_WDT_WCR_WT) >> 8;
  113. /* A value of 0 reflects one period (0.5s). */
  114. ptimer_set_count(s->timer, count + 1);
  115. if (start) {
  116. ptimer_run(s->timer, 1);
  117. }
  118. }
  119. ptimer_transaction_commit(s->timer);
  120. if (s->pretimeout_support) {
  121. imx_wdt2_update_itimer(s, start);
  122. }
  123. }
  124. static void imx2_wdt_write(void *opaque, hwaddr addr,
  125. uint64_t value, unsigned int size)
  126. {
  127. IMX2WdtState *s = IMX2_WDT(opaque);
  128. trace_imx2_wdt_write(addr, value);
  129. switch (addr) {
  130. case IMX2_WDT_WCR:
  131. if (s->wcr_locked) {
  132. value &= ~IMX2_WDT_WCR_LOCK_MASK;
  133. value |= (s->wicr & IMX2_WDT_WCR_LOCK_MASK);
  134. }
  135. s->wcr_locked = true;
  136. if (s->wcr_wde_locked) {
  137. value &= ~IMX2_WDT_WCR_WDE;
  138. value |= (s->wicr & ~IMX2_WDT_WCR_WDE);
  139. } else if (value & IMX2_WDT_WCR_WDE) {
  140. s->wcr_wde_locked = true;
  141. }
  142. if (s->wcr_wdt_locked) {
  143. value &= ~IMX2_WDT_WCR_WDT;
  144. value |= (s->wicr & ~IMX2_WDT_WCR_WDT);
  145. } else if (value & IMX2_WDT_WCR_WDT) {
  146. s->wcr_wdt_locked = true;
  147. }
  148. s->wcr = value;
  149. if (!(value & IMX2_WDT_WCR_SRS)) {
  150. s->wrsr = IMX2_WDT_WRSR_SFTW;
  151. }
  152. if (!(value & (IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS)) ||
  153. (!(value & IMX2_WDT_WCR_WT) && (value & IMX2_WDT_WCR_WDE))) {
  154. watchdog_perform_action();
  155. }
  156. s->wcr |= IMX2_WDT_WCR_SRS;
  157. imx_wdt2_update_timer(s, true);
  158. break;
  159. case IMX2_WDT_WSR:
  160. if (s->wsr == IMX2_WDT_SEQ1 && value == IMX2_WDT_SEQ2) {
  161. imx_wdt2_update_timer(s, false);
  162. }
  163. s->wsr = value;
  164. break;
  165. case IMX2_WDT_WRSR:
  166. break;
  167. case IMX2_WDT_WICR:
  168. if (!s->pretimeout_support) {
  169. return;
  170. }
  171. value &= IMX2_WDT_WICR_LOCK_MASK | IMX2_WDT_WICR_WTIS;
  172. if (s->wicr_locked) {
  173. value &= IMX2_WDT_WICR_WTIS;
  174. value |= (s->wicr & IMX2_WDT_WICR_LOCK_MASK);
  175. }
  176. s->wicr = value | (s->wicr & IMX2_WDT_WICR_WTIS);
  177. if (value & IMX2_WDT_WICR_WTIS) {
  178. s->wicr &= ~IMX2_WDT_WICR_WTIS;
  179. qemu_set_irq(s->irq, 0);
  180. }
  181. imx_wdt2_update_itimer(s, true);
  182. s->wicr_locked = true;
  183. break;
  184. case IMX2_WDT_WMCR:
  185. s->wmcr = value & IMX2_WDT_WMCR_PDE;
  186. break;
  187. }
  188. }
  189. static const MemoryRegionOps imx2_wdt_ops = {
  190. .read = imx2_wdt_read,
  191. .write = imx2_wdt_write,
  192. .endianness = DEVICE_NATIVE_ENDIAN,
  193. .impl = {
  194. /*
  195. * Our device would not work correctly if the guest was doing
  196. * unaligned access. This might not be a limitation on the
  197. * real device but in practice there is no reason for a guest
  198. * to access this device unaligned.
  199. */
  200. .min_access_size = 2,
  201. .max_access_size = 2,
  202. .unaligned = false,
  203. },
  204. };
  205. static const VMStateDescription vmstate_imx2_wdt = {
  206. .name = "imx2.wdt",
  207. .fields = (const VMStateField[]) {
  208. VMSTATE_PTIMER(timer, IMX2WdtState),
  209. VMSTATE_PTIMER(itimer, IMX2WdtState),
  210. VMSTATE_BOOL(wicr_locked, IMX2WdtState),
  211. VMSTATE_BOOL(wcr_locked, IMX2WdtState),
  212. VMSTATE_BOOL(wcr_wde_locked, IMX2WdtState),
  213. VMSTATE_BOOL(wcr_wdt_locked, IMX2WdtState),
  214. VMSTATE_UINT16(wcr, IMX2WdtState),
  215. VMSTATE_UINT16(wsr, IMX2WdtState),
  216. VMSTATE_UINT16(wrsr, IMX2WdtState),
  217. VMSTATE_UINT16(wmcr, IMX2WdtState),
  218. VMSTATE_UINT16(wicr, IMX2WdtState),
  219. VMSTATE_END_OF_LIST()
  220. }
  221. };
  222. static void imx2_wdt_realize(DeviceState *dev, Error **errp)
  223. {
  224. IMX2WdtState *s = IMX2_WDT(dev);
  225. SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
  226. memory_region_init_io(&s->mmio, OBJECT(dev),
  227. &imx2_wdt_ops, s,
  228. TYPE_IMX2_WDT,
  229. IMX2_WDT_MMIO_SIZE);
  230. sysbus_init_mmio(sbd, &s->mmio);
  231. sysbus_init_irq(sbd, &s->irq);
  232. s->timer = ptimer_init(imx2_wdt_expired, s,
  233. PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
  234. PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
  235. PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
  236. ptimer_transaction_begin(s->timer);
  237. ptimer_set_freq(s->timer, 2);
  238. ptimer_set_limit(s->timer, 0xff, 1);
  239. ptimer_transaction_commit(s->timer);
  240. if (s->pretimeout_support) {
  241. s->itimer = ptimer_init(imx2_wdt_interrupt, s,
  242. PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
  243. PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
  244. PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
  245. ptimer_transaction_begin(s->itimer);
  246. ptimer_set_freq(s->itimer, 2);
  247. ptimer_set_limit(s->itimer, 0xff, 1);
  248. ptimer_transaction_commit(s->itimer);
  249. }
  250. }
  251. static const Property imx2_wdt_properties[] = {
  252. DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState, pretimeout_support,
  253. false),
  254. };
  255. static void imx2_wdt_class_init(ObjectClass *klass, void *data)
  256. {
  257. DeviceClass *dc = DEVICE_CLASS(klass);
  258. device_class_set_props(dc, imx2_wdt_properties);
  259. dc->realize = imx2_wdt_realize;
  260. device_class_set_legacy_reset(dc, imx2_wdt_reset);
  261. dc->vmsd = &vmstate_imx2_wdt;
  262. dc->desc = "i.MX2 watchdog timer";
  263. set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
  264. }
  265. static const TypeInfo imx2_wdt_info = {
  266. .name = TYPE_IMX2_WDT,
  267. .parent = TYPE_SYS_BUS_DEVICE,
  268. .instance_size = sizeof(IMX2WdtState),
  269. .class_init = imx2_wdt_class_init,
  270. };
  271. static void imx2_wdt_register_type(void)
  272. {
  273. type_register_static(&imx2_wdt_info);
  274. }
  275. type_init(imx2_wdt_register_type)