2
0

stellaris-gptm.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. * Luminary Micro Stellaris General Purpose Timer Module
  3. *
  4. * Copyright (c) 2006 CodeSourcery.
  5. * Written by Paul Brook
  6. *
  7. * This code is licensed under the GPL.
  8. */
  9. #include "qemu/osdep.h"
  10. #include "qemu/log.h"
  11. #include "qemu/timer.h"
  12. #include "qapi/error.h"
  13. #include "migration/vmstate.h"
  14. #include "hw/qdev-clock.h"
  15. #include "hw/timer/stellaris-gptm.h"
  16. static void gptm_update_irq(gptm_state *s)
  17. {
  18. int level;
  19. level = (s->state & s->mask) != 0;
  20. qemu_set_irq(s->irq, level);
  21. }
  22. static void gptm_stop(gptm_state *s, int n)
  23. {
  24. timer_del(s->timer[n]);
  25. }
  26. static void gptm_reload(gptm_state *s, int n, int reset)
  27. {
  28. int64_t tick;
  29. if (reset) {
  30. tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  31. } else {
  32. tick = s->tick[n];
  33. }
  34. if (s->config == 0) {
  35. /* 32-bit CountDown. */
  36. uint32_t count;
  37. count = s->load[0] | (s->load[1] << 16);
  38. tick += clock_ticks_to_ns(s->clk, count);
  39. } else if (s->config == 1) {
  40. /* 32-bit RTC. 1Hz tick. */
  41. tick += NANOSECONDS_PER_SECOND;
  42. } else if (s->mode[n] == 0xa) {
  43. /* PWM mode. Not implemented. */
  44. } else {
  45. qemu_log_mask(LOG_UNIMP,
  46. "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
  47. s->mode[n]);
  48. return;
  49. }
  50. s->tick[n] = tick;
  51. timer_mod(s->timer[n], tick);
  52. }
  53. static void gptm_tick(void *opaque)
  54. {
  55. gptm_state **p = (gptm_state **)opaque;
  56. gptm_state *s;
  57. int n;
  58. s = *p;
  59. n = p - s->opaque;
  60. if (s->config == 0) {
  61. s->state |= 1;
  62. if ((s->control & 0x20)) {
  63. /* Output trigger. */
  64. qemu_irq_pulse(s->trigger);
  65. }
  66. if (s->mode[0] & 1) {
  67. /* One-shot. */
  68. s->control &= ~1;
  69. } else {
  70. /* Periodic. */
  71. gptm_reload(s, 0, 0);
  72. }
  73. } else if (s->config == 1) {
  74. /* RTC. */
  75. uint32_t match;
  76. s->rtc++;
  77. match = s->match[0] | (s->match[1] << 16);
  78. if (s->rtc > match)
  79. s->rtc = 0;
  80. if (s->rtc == 0) {
  81. s->state |= 8;
  82. }
  83. gptm_reload(s, 0, 0);
  84. } else if (s->mode[n] == 0xa) {
  85. /* PWM mode. Not implemented. */
  86. } else {
  87. qemu_log_mask(LOG_UNIMP,
  88. "GPTM: 16-bit timer mode unimplemented: 0x%x\n",
  89. s->mode[n]);
  90. }
  91. gptm_update_irq(s);
  92. }
  93. static uint64_t gptm_read(void *opaque, hwaddr offset,
  94. unsigned size)
  95. {
  96. gptm_state *s = (gptm_state *)opaque;
  97. switch (offset) {
  98. case 0x00: /* CFG */
  99. return s->config;
  100. case 0x04: /* TAMR */
  101. return s->mode[0];
  102. case 0x08: /* TBMR */
  103. return s->mode[1];
  104. case 0x0c: /* CTL */
  105. return s->control;
  106. case 0x18: /* IMR */
  107. return s->mask;
  108. case 0x1c: /* RIS */
  109. return s->state;
  110. case 0x20: /* MIS */
  111. return s->state & s->mask;
  112. case 0x24: /* CR */
  113. return 0;
  114. case 0x28: /* TAILR */
  115. return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
  116. case 0x2c: /* TBILR */
  117. return s->load[1];
  118. case 0x30: /* TAMARCHR */
  119. return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
  120. case 0x34: /* TBMATCHR */
  121. return s->match[1];
  122. case 0x38: /* TAPR */
  123. return s->prescale[0];
  124. case 0x3c: /* TBPR */
  125. return s->prescale[1];
  126. case 0x40: /* TAPMR */
  127. return s->match_prescale[0];
  128. case 0x44: /* TBPMR */
  129. return s->match_prescale[1];
  130. case 0x48: /* TAR */
  131. if (s->config == 1) {
  132. return s->rtc;
  133. }
  134. qemu_log_mask(LOG_UNIMP,
  135. "GPTM: read of TAR but timer read not supported\n");
  136. return 0;
  137. case 0x4c: /* TBR */
  138. qemu_log_mask(LOG_UNIMP,
  139. "GPTM: read of TBR but timer read not supported\n");
  140. return 0;
  141. default:
  142. qemu_log_mask(LOG_GUEST_ERROR,
  143. "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n",
  144. offset);
  145. return 0;
  146. }
  147. }
  148. static void gptm_write(void *opaque, hwaddr offset,
  149. uint64_t value, unsigned size)
  150. {
  151. gptm_state *s = (gptm_state *)opaque;
  152. uint32_t oldval;
  153. /*
  154. * The timers should be disabled before changing the configuration.
  155. * We take advantage of this and defer everything until the timer
  156. * is enabled.
  157. */
  158. switch (offset) {
  159. case 0x00: /* CFG */
  160. s->config = value;
  161. break;
  162. case 0x04: /* TAMR */
  163. s->mode[0] = value;
  164. break;
  165. case 0x08: /* TBMR */
  166. s->mode[1] = value;
  167. break;
  168. case 0x0c: /* CTL */
  169. oldval = s->control;
  170. s->control = value;
  171. /* TODO: Implement pause. */
  172. if ((oldval ^ value) & 1) {
  173. if (value & 1) {
  174. gptm_reload(s, 0, 1);
  175. } else {
  176. gptm_stop(s, 0);
  177. }
  178. }
  179. if (((oldval ^ value) & 0x100) && s->config >= 4) {
  180. if (value & 0x100) {
  181. gptm_reload(s, 1, 1);
  182. } else {
  183. gptm_stop(s, 1);
  184. }
  185. }
  186. break;
  187. case 0x18: /* IMR */
  188. s->mask = value & 0x77;
  189. gptm_update_irq(s);
  190. break;
  191. case 0x24: /* CR */
  192. s->state &= ~value;
  193. break;
  194. case 0x28: /* TAILR */
  195. s->load[0] = value & 0xffff;
  196. if (s->config < 4) {
  197. s->load[1] = value >> 16;
  198. }
  199. break;
  200. case 0x2c: /* TBILR */
  201. s->load[1] = value & 0xffff;
  202. break;
  203. case 0x30: /* TAMARCHR */
  204. s->match[0] = value & 0xffff;
  205. if (s->config < 4) {
  206. s->match[1] = value >> 16;
  207. }
  208. break;
  209. case 0x34: /* TBMATCHR */
  210. s->match[1] = value >> 16;
  211. break;
  212. case 0x38: /* TAPR */
  213. s->prescale[0] = value;
  214. break;
  215. case 0x3c: /* TBPR */
  216. s->prescale[1] = value;
  217. break;
  218. case 0x40: /* TAPMR */
  219. s->match_prescale[0] = value;
  220. break;
  221. case 0x44: /* TBPMR */
  222. s->match_prescale[0] = value;
  223. break;
  224. default:
  225. qemu_log_mask(LOG_GUEST_ERROR,
  226. "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n",
  227. offset);
  228. }
  229. gptm_update_irq(s);
  230. }
  231. static const MemoryRegionOps gptm_ops = {
  232. .read = gptm_read,
  233. .write = gptm_write,
  234. .endianness = DEVICE_NATIVE_ENDIAN,
  235. };
  236. static const VMStateDescription vmstate_stellaris_gptm = {
  237. .name = "stellaris_gptm",
  238. .version_id = 2,
  239. .minimum_version_id = 2,
  240. .fields = (const VMStateField[]) {
  241. VMSTATE_UINT32(config, gptm_state),
  242. VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
  243. VMSTATE_UINT32(control, gptm_state),
  244. VMSTATE_UINT32(state, gptm_state),
  245. VMSTATE_UINT32(mask, gptm_state),
  246. VMSTATE_UNUSED(8),
  247. VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
  248. VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
  249. VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
  250. VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
  251. VMSTATE_UINT32(rtc, gptm_state),
  252. VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
  253. VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2),
  254. VMSTATE_CLOCK(clk, gptm_state),
  255. VMSTATE_END_OF_LIST()
  256. }
  257. };
  258. static void stellaris_gptm_init(Object *obj)
  259. {
  260. DeviceState *dev = DEVICE(obj);
  261. gptm_state *s = STELLARIS_GPTM(obj);
  262. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  263. sysbus_init_irq(sbd, &s->irq);
  264. qdev_init_gpio_out(dev, &s->trigger, 1);
  265. memory_region_init_io(&s->iomem, obj, &gptm_ops, s,
  266. "gptm", 0x1000);
  267. sysbus_init_mmio(sbd, &s->iomem);
  268. s->opaque[0] = s->opaque[1] = s;
  269. /*
  270. * TODO: in an ideal world we would model the effects of changing
  271. * the input clock frequency while the countdown timer is active.
  272. * The best way to do this would be to convert the device to use
  273. * ptimer instead of hand-rolling its own timer. This would also
  274. * make it easy to implement reading the current count from the
  275. * TAR and TBR registers.
  276. */
  277. s->clk = qdev_init_clock_in(dev, "clk", NULL, NULL, 0);
  278. }
  279. static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
  280. {
  281. gptm_state *s = STELLARIS_GPTM(dev);
  282. if (!clock_has_source(s->clk)) {
  283. error_setg(errp, "stellaris-gptm: clk must be connected");
  284. return;
  285. }
  286. s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]);
  287. s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]);
  288. }
  289. static void stellaris_gptm_class_init(ObjectClass *klass, void *data)
  290. {
  291. DeviceClass *dc = DEVICE_CLASS(klass);
  292. dc->vmsd = &vmstate_stellaris_gptm;
  293. dc->realize = stellaris_gptm_realize;
  294. }
  295. static const TypeInfo stellaris_gptm_info = {
  296. .name = TYPE_STELLARIS_GPTM,
  297. .parent = TYPE_SYS_BUS_DEVICE,
  298. .instance_size = sizeof(gptm_state),
  299. .instance_init = stellaris_gptm_init,
  300. .class_init = stellaris_gptm_class_init,
  301. };
  302. static void stellaris_gptm_register_types(void)
  303. {
  304. type_register_static(&stellaris_gptm_info);
  305. }
  306. type_init(stellaris_gptm_register_types)