cmsdk-apb-timer.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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/timer/cmsdk-apb-timer.h"
  37. #include "migration/vmstate.h"
  38. REG32(CTRL, 0)
  39. FIELD(CTRL, EN, 0, 1)
  40. FIELD(CTRL, SELEXTEN, 1, 1)
  41. FIELD(CTRL, SELEXTCLK, 2, 1)
  42. FIELD(CTRL, IRQEN, 3, 1)
  43. REG32(VALUE, 4)
  44. REG32(RELOAD, 8)
  45. REG32(INTSTATUS, 0xc)
  46. FIELD(INTSTATUS, IRQ, 0, 1)
  47. REG32(PID4, 0xFD0)
  48. REG32(PID5, 0xFD4)
  49. REG32(PID6, 0xFD8)
  50. REG32(PID7, 0xFDC)
  51. REG32(PID0, 0xFE0)
  52. REG32(PID1, 0xFE4)
  53. REG32(PID2, 0xFE8)
  54. REG32(PID3, 0xFEC)
  55. REG32(CID0, 0xFF0)
  56. REG32(CID1, 0xFF4)
  57. REG32(CID2, 0xFF8)
  58. REG32(CID3, 0xFFC)
  59. /* PID/CID values */
  60. static const int timer_id[] = {
  61. 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
  62. 0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
  63. 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
  64. };
  65. static void cmsdk_apb_timer_update(CMSDKAPBTIMER *s)
  66. {
  67. qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
  68. }
  69. static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
  70. {
  71. CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
  72. uint64_t r;
  73. switch (offset) {
  74. case A_CTRL:
  75. r = s->ctrl;
  76. break;
  77. case A_VALUE:
  78. r = ptimer_get_count(s->timer);
  79. break;
  80. case A_RELOAD:
  81. r = ptimer_get_limit(s->timer);
  82. break;
  83. case A_INTSTATUS:
  84. r = s->intstatus;
  85. break;
  86. case A_PID4 ... A_CID3:
  87. r = timer_id[(offset - A_PID4) / 4];
  88. break;
  89. default:
  90. qemu_log_mask(LOG_GUEST_ERROR,
  91. "CMSDK APB timer read: bad offset %x\n", (int) offset);
  92. r = 0;
  93. break;
  94. }
  95. trace_cmsdk_apb_timer_read(offset, r, size);
  96. return r;
  97. }
  98. static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
  99. unsigned size)
  100. {
  101. CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
  102. trace_cmsdk_apb_timer_write(offset, value, size);
  103. switch (offset) {
  104. case A_CTRL:
  105. if (value & 6) {
  106. /* Bits [1] and [2] enable using EXTIN as either clock or
  107. * an enable line. We don't model this.
  108. */
  109. qemu_log_mask(LOG_UNIMP,
  110. "CMSDK APB timer: EXTIN input not supported\n");
  111. }
  112. s->ctrl = value & 0xf;
  113. ptimer_transaction_begin(s->timer);
  114. if (s->ctrl & R_CTRL_EN_MASK) {
  115. ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
  116. } else {
  117. ptimer_stop(s->timer);
  118. }
  119. ptimer_transaction_commit(s->timer);
  120. break;
  121. case A_RELOAD:
  122. /* Writing to reload also sets the current timer value */
  123. ptimer_transaction_begin(s->timer);
  124. if (!value) {
  125. ptimer_stop(s->timer);
  126. }
  127. ptimer_set_limit(s->timer, value, 1);
  128. if (value && (s->ctrl & R_CTRL_EN_MASK)) {
  129. /*
  130. * Make sure timer is running (it might have stopped if this
  131. * was an expired one-shot timer)
  132. */
  133. ptimer_run(s->timer, 0);
  134. }
  135. ptimer_transaction_commit(s->timer);
  136. break;
  137. case A_VALUE:
  138. ptimer_transaction_begin(s->timer);
  139. if (!value && !ptimer_get_limit(s->timer)) {
  140. ptimer_stop(s->timer);
  141. }
  142. ptimer_set_count(s->timer, value);
  143. if (value && (s->ctrl & R_CTRL_EN_MASK)) {
  144. ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0);
  145. }
  146. ptimer_transaction_commit(s->timer);
  147. break;
  148. case A_INTSTATUS:
  149. /* Just one bit, which is W1C. */
  150. value &= 1;
  151. s->intstatus &= ~value;
  152. cmsdk_apb_timer_update(s);
  153. break;
  154. case A_PID4 ... A_CID3:
  155. qemu_log_mask(LOG_GUEST_ERROR,
  156. "CMSDK APB timer write: write to RO offset 0x%x\n",
  157. (int)offset);
  158. break;
  159. default:
  160. qemu_log_mask(LOG_GUEST_ERROR,
  161. "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
  162. break;
  163. }
  164. }
  165. static const MemoryRegionOps cmsdk_apb_timer_ops = {
  166. .read = cmsdk_apb_timer_read,
  167. .write = cmsdk_apb_timer_write,
  168. .endianness = DEVICE_LITTLE_ENDIAN,
  169. };
  170. static void cmsdk_apb_timer_tick(void *opaque)
  171. {
  172. CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
  173. if (s->ctrl & R_CTRL_IRQEN_MASK) {
  174. s->intstatus |= R_INTSTATUS_IRQ_MASK;
  175. cmsdk_apb_timer_update(s);
  176. }
  177. }
  178. static void cmsdk_apb_timer_reset(DeviceState *dev)
  179. {
  180. CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
  181. trace_cmsdk_apb_timer_reset();
  182. s->ctrl = 0;
  183. s->intstatus = 0;
  184. ptimer_transaction_begin(s->timer);
  185. ptimer_stop(s->timer);
  186. /* Set the limit and the count */
  187. ptimer_set_limit(s->timer, 0, 1);
  188. ptimer_transaction_commit(s->timer);
  189. }
  190. static void cmsdk_apb_timer_init(Object *obj)
  191. {
  192. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  193. CMSDKAPBTIMER *s = CMSDK_APB_TIMER(obj);
  194. memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
  195. s, "cmsdk-apb-timer", 0x1000);
  196. sysbus_init_mmio(sbd, &s->iomem);
  197. sysbus_init_irq(sbd, &s->timerint);
  198. }
  199. static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
  200. {
  201. CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
  202. if (s->pclk_frq == 0) {
  203. error_setg(errp, "CMSDK APB timer: pclk-frq property must be set");
  204. return;
  205. }
  206. s->timer = ptimer_init(cmsdk_apb_timer_tick, s,
  207. PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
  208. PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT |
  209. PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
  210. PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
  211. ptimer_transaction_begin(s->timer);
  212. ptimer_set_freq(s->timer, s->pclk_frq);
  213. ptimer_transaction_commit(s->timer);
  214. }
  215. static const VMStateDescription cmsdk_apb_timer_vmstate = {
  216. .name = "cmsdk-apb-timer",
  217. .version_id = 1,
  218. .minimum_version_id = 1,
  219. .fields = (VMStateField[]) {
  220. VMSTATE_PTIMER(timer, CMSDKAPBTIMER),
  221. VMSTATE_UINT32(ctrl, CMSDKAPBTIMER),
  222. VMSTATE_UINT32(value, CMSDKAPBTIMER),
  223. VMSTATE_UINT32(reload, CMSDKAPBTIMER),
  224. VMSTATE_UINT32(intstatus, CMSDKAPBTIMER),
  225. VMSTATE_END_OF_LIST()
  226. }
  227. };
  228. static Property cmsdk_apb_timer_properties[] = {
  229. DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBTIMER, pclk_frq, 0),
  230. DEFINE_PROP_END_OF_LIST(),
  231. };
  232. static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
  233. {
  234. DeviceClass *dc = DEVICE_CLASS(klass);
  235. dc->realize = cmsdk_apb_timer_realize;
  236. dc->vmsd = &cmsdk_apb_timer_vmstate;
  237. dc->reset = cmsdk_apb_timer_reset;
  238. dc->props = cmsdk_apb_timer_properties;
  239. }
  240. static const TypeInfo cmsdk_apb_timer_info = {
  241. .name = TYPE_CMSDK_APB_TIMER,
  242. .parent = TYPE_SYS_BUS_DEVICE,
  243. .instance_size = sizeof(CMSDKAPBTIMER),
  244. .instance_init = cmsdk_apb_timer_init,
  245. .class_init = cmsdk_apb_timer_class_init,
  246. };
  247. static void cmsdk_apb_timer_register_types(void)
  248. {
  249. type_register_static(&cmsdk_apb_timer_info);
  250. }
  251. type_init(cmsdk_apb_timer_register_types);