cmsdk-apb-timer.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * ARM CMSDK APB timer emulation
  3. *
  4. * Copyright (c) 2017 Linaro Limited
  5. * Written by Peter Maydell
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 or
  9. * (at your option) any later version.
  10. */
  11. /* This is a model of the "APB timer" which is part of the Cortex-M
  12. * System Design Kit (CMSDK) and documented in the Cortex-M System
  13. * Design Kit Technical Reference Manual (ARM DDI0479C):
  14. * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
  15. *
  16. * The hardware has an EXTIN input wire, which can be configured
  17. * by the guest to act either as a 'timer enable' (timer does not run
  18. * when EXTIN is low), or as a 'timer clock' (timer runs at frequency
  19. * of EXTIN clock, not PCLK frequency). We don't model this.
  20. *
  21. * The documentation is not very clear about the exact behaviour;
  22. * we choose to implement that the interrupt is triggered when
  23. * the counter goes from 1 to 0, that the counter then holds at 0
  24. * for one clock cycle before reloading from the RELOAD register,
  25. * and that if the RELOAD register is 0 this does not cause an
  26. * interrupt (as there is no further 1->0 transition).
  27. */
  28. #include "qemu/osdep.h"
  29. #include "qemu/log.h"
  30. #include "qemu/module.h"
  31. #include "qapi/error.h"
  32. #include "trace.h"
  33. #include "hw/sysbus.h"
  34. #include "hw/irq.h"
  35. #include "hw/registerfields.h"
  36. #include "hw/qdev-clock.h"
  37. #include "hw/timer/cmsdk-apb-timer.h"
  38. #include "migration/vmstate.h"
  39. REG32(CTRL, 0)
  40. FIELD(CTRL, EN, 0, 1)
  41. FIELD(CTRL, SELEXTEN, 1, 1)
  42. FIELD(CTRL, SELEXTCLK, 2, 1)
  43. FIELD(CTRL, IRQEN, 3, 1)
  44. REG32(VALUE, 4)
  45. REG32(RELOAD, 8)
  46. REG32(INTSTATUS, 0xc)
  47. FIELD(INTSTATUS, IRQ, 0, 1)
  48. REG32(PID4, 0xFD0)
  49. REG32(PID5, 0xFD4)
  50. REG32(PID6, 0xFD8)
  51. REG32(PID7, 0xFDC)
  52. REG32(PID0, 0xFE0)
  53. REG32(PID1, 0xFE4)
  54. REG32(PID2, 0xFE8)
  55. REG32(PID3, 0xFEC)
  56. REG32(CID0, 0xFF0)
  57. REG32(CID1, 0xFF4)
  58. REG32(CID2, 0xFF8)
  59. REG32(CID3, 0xFFC)
  60. /* PID/CID values */
  61. static const int timer_id[] = {
  62. 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
  63. 0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
  64. 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
  65. };
  66. static void cmsdk_apb_timer_update(CMSDKAPBTimer *s)
  67. {
  68. qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
  69. }
  70. static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
  71. {
  72. CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
  73. uint64_t r;
  74. switch (offset) {
  75. case A_CTRL:
  76. r = s->ctrl;
  77. break;
  78. case A_VALUE:
  79. r = ptimer_get_count(s->timer);
  80. break;
  81. case A_RELOAD:
  82. r = ptimer_get_limit(s->timer);
  83. break;
  84. case A_INTSTATUS:
  85. r = s->intstatus;
  86. break;
  87. case A_PID4 ... A_CID3:
  88. r = timer_id[(offset - A_PID4) / 4];
  89. break;
  90. default:
  91. qemu_log_mask(LOG_GUEST_ERROR,
  92. "CMSDK APB timer read: bad offset %x\n", (int) offset);
  93. r = 0;
  94. break;
  95. }
  96. trace_cmsdk_apb_timer_read(offset, r, size);
  97. return r;
  98. }
  99. static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
  100. unsigned size)
  101. {
  102. CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
  103. trace_cmsdk_apb_timer_write(offset, value, size);
  104. switch (offset) {
  105. case A_CTRL:
  106. if (value & 6) {
  107. /* Bits [1] and [2] enable using EXTIN as either clock or
  108. * an enable line. We don't model this.
  109. */
  110. qemu_log_mask(LOG_UNIMP,
  111. "CMSDK APB timer: EXTIN input not supported\n");
  112. }
  113. s->ctrl = value & 0xf;
  114. ptimer_transaction_begin(s->timer);
  115. if (s->ctrl & R_CTRL_EN_MASK) {
  116. ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
  117. } else {
  118. ptimer_stop(s->timer);
  119. }
  120. ptimer_transaction_commit(s->timer);
  121. break;
  122. case A_RELOAD:
  123. /* Writing to reload also sets the current timer value */
  124. ptimer_transaction_begin(s->timer);
  125. if (!value) {
  126. ptimer_stop(s->timer);
  127. }
  128. ptimer_set_limit(s->timer, value, 1);
  129. if (value && (s->ctrl & R_CTRL_EN_MASK)) {
  130. /*
  131. * Make sure timer is running (it might have stopped if this
  132. * was an expired one-shot timer)
  133. */
  134. ptimer_run(s->timer, 0);
  135. }
  136. ptimer_transaction_commit(s->timer);
  137. break;
  138. case A_VALUE:
  139. ptimer_transaction_begin(s->timer);
  140. if (!value && !ptimer_get_limit(s->timer)) {
  141. ptimer_stop(s->timer);
  142. }
  143. ptimer_set_count(s->timer, value);
  144. if (value && (s->ctrl & R_CTRL_EN_MASK)) {
  145. ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
  146. }
  147. ptimer_transaction_commit(s->timer);
  148. break;
  149. case A_INTSTATUS:
  150. /* Just one bit, which is W1C. */
  151. value &= 1;
  152. s->intstatus &= ~value;
  153. cmsdk_apb_timer_update(s);
  154. break;
  155. case A_PID4 ... A_CID3:
  156. qemu_log_mask(LOG_GUEST_ERROR,
  157. "CMSDK APB timer write: write to RO offset 0x%x\n",
  158. (int)offset);
  159. break;
  160. default:
  161. qemu_log_mask(LOG_GUEST_ERROR,
  162. "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
  163. break;
  164. }
  165. }
  166. static const MemoryRegionOps cmsdk_apb_timer_ops = {
  167. .read = cmsdk_apb_timer_read,
  168. .write = cmsdk_apb_timer_write,
  169. .endianness = DEVICE_LITTLE_ENDIAN,
  170. };
  171. static void cmsdk_apb_timer_tick(void *opaque)
  172. {
  173. CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
  174. if (s->ctrl & R_CTRL_IRQEN_MASK) {
  175. s->intstatus |= R_INTSTATUS_IRQ_MASK;
  176. cmsdk_apb_timer_update(s);
  177. }
  178. }
  179. static void cmsdk_apb_timer_reset(DeviceState *dev)
  180. {
  181. CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
  182. trace_cmsdk_apb_timer_reset();
  183. s->ctrl = 0;
  184. s->intstatus = 0;
  185. ptimer_transaction_begin(s->timer);
  186. ptimer_stop(s->timer);
  187. /* Set the limit and the count */
  188. ptimer_set_limit(s->timer, 0, 1);
  189. ptimer_transaction_commit(s->timer);
  190. }
  191. static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
  192. {
  193. CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
  194. ptimer_transaction_begin(s->timer);
  195. ptimer_set_period_from_clock(s->timer, s->pclk, 1);
  196. ptimer_transaction_commit(s->timer);
  197. }
  198. static void cmsdk_apb_timer_init(Object *obj)
  199. {
  200. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  201. CMSDKAPBTimer *s = CMSDK_APB_TIMER(obj);
  202. memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
  203. s, "cmsdk-apb-timer", 0x1000);
  204. sysbus_init_mmio(sbd, &s->iomem);
  205. sysbus_init_irq(sbd, &s->timerint);
  206. s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
  207. cmsdk_apb_timer_clk_update, s, ClockUpdate);
  208. }
  209. static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
  210. {
  211. CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
  212. if (!clock_has_source(s->pclk)) {
  213. error_setg(errp, "CMSDK APB timer: pclk clock must be connected");
  214. return;
  215. }
  216. s->timer = ptimer_init(cmsdk_apb_timer_tick, s,
  217. PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
  218. PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
  219. PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
  220. PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
  221. ptimer_transaction_begin(s->timer);
  222. ptimer_set_period_from_clock(s->timer, s->pclk, 1);
  223. ptimer_transaction_commit(s->timer);
  224. }
  225. static const VMStateDescription cmsdk_apb_timer_vmstate = {
  226. .name = "cmsdk-apb-timer",
  227. .version_id = 2,
  228. .minimum_version_id = 2,
  229. .fields = (VMStateField[]) {
  230. VMSTATE_PTIMER(timer, CMSDKAPBTimer),
  231. VMSTATE_CLOCK(pclk, CMSDKAPBTimer),
  232. VMSTATE_UINT32(ctrl, CMSDKAPBTimer),
  233. VMSTATE_UINT32(value, CMSDKAPBTimer),
  234. VMSTATE_UINT32(reload, CMSDKAPBTimer),
  235. VMSTATE_UINT32(intstatus, CMSDKAPBTimer),
  236. VMSTATE_END_OF_LIST()
  237. }
  238. };
  239. static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
  240. {
  241. DeviceClass *dc = DEVICE_CLASS(klass);
  242. dc->realize = cmsdk_apb_timer_realize;
  243. dc->vmsd = &cmsdk_apb_timer_vmstate;
  244. dc->reset = cmsdk_apb_timer_reset;
  245. }
  246. static const TypeInfo cmsdk_apb_timer_info = {
  247. .name = TYPE_CMSDK_APB_TIMER,
  248. .parent = TYPE_SYS_BUS_DEVICE,
  249. .instance_size = sizeof(CMSDKAPBTimer),
  250. .instance_init = cmsdk_apb_timer_init,
  251. .class_init = cmsdk_apb_timer_class_init,
  252. };
  253. static void cmsdk_apb_timer_register_types(void)
  254. {
  255. type_register_static(&cmsdk_apb_timer_info);
  256. }
  257. type_init(cmsdk_apb_timer_register_types);