allwinner-a10-pit.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. * Allwinner A10 timer device emulation
  3. *
  4. * Copyright (C) 2013 Li Guang
  5. * Written by Li Guang <lig.fnst@cn.fujitsu.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but WITHOUT
  13. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  15. * for more details.
  16. */
  17. #include "qemu/osdep.h"
  18. #include "hw/irq.h"
  19. #include "hw/qdev-properties.h"
  20. #include "hw/sysbus.h"
  21. #include "hw/timer/allwinner-a10-pit.h"
  22. #include "migration/vmstate.h"
  23. #include "qemu/log.h"
  24. #include "qemu/module.h"
  25. static void a10_pit_update_irq(AwA10PITState *s)
  26. {
  27. int i;
  28. for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
  29. qemu_set_irq(s->irq[i], !!(s->irq_status & s->irq_enable & (1 << i)));
  30. }
  31. }
  32. static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size)
  33. {
  34. AwA10PITState *s = AW_A10_PIT(opaque);
  35. uint8_t index;
  36. switch (offset) {
  37. case AW_A10_PIT_TIMER_IRQ_EN:
  38. return s->irq_enable;
  39. case AW_A10_PIT_TIMER_IRQ_ST:
  40. return s->irq_status;
  41. case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
  42. index = offset & 0xf0;
  43. index >>= 4;
  44. index -= 1;
  45. switch (offset & 0x0f) {
  46. case AW_A10_PIT_TIMER_CONTROL:
  47. return s->control[index];
  48. case AW_A10_PIT_TIMER_INTERVAL:
  49. return s->interval[index];
  50. case AW_A10_PIT_TIMER_COUNT:
  51. s->count[index] = ptimer_get_count(s->timer[index]);
  52. return s->count[index];
  53. default:
  54. qemu_log_mask(LOG_GUEST_ERROR,
  55. "%s: Bad offset 0x%x\n", __func__, (int)offset);
  56. break;
  57. }
  58. case AW_A10_PIT_WDOG_CONTROL:
  59. break;
  60. case AW_A10_PIT_WDOG_MODE:
  61. break;
  62. case AW_A10_PIT_COUNT_LO:
  63. return s->count_lo;
  64. case AW_A10_PIT_COUNT_HI:
  65. return s->count_hi;
  66. case AW_A10_PIT_COUNT_CTL:
  67. return s->count_ctl;
  68. default:
  69. qemu_log_mask(LOG_GUEST_ERROR,
  70. "%s: Bad offset 0x%x\n", __func__, (int)offset);
  71. break;
  72. }
  73. return 0;
  74. }
  75. /* Must be called inside a ptimer transaction block for s->timer[index] */
  76. static void a10_pit_set_freq(AwA10PITState *s, int index)
  77. {
  78. uint32_t prescaler, source, source_freq;
  79. prescaler = 1 << extract32(s->control[index], 4, 3);
  80. source = extract32(s->control[index], 2, 2);
  81. source_freq = s->clk_freq[source];
  82. if (source_freq) {
  83. ptimer_set_freq(s->timer[index], source_freq / prescaler);
  84. } else {
  85. qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid clock source %u\n",
  86. __func__, source);
  87. }
  88. }
  89. static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
  90. unsigned size)
  91. {
  92. AwA10PITState *s = AW_A10_PIT(opaque);
  93. uint8_t index;
  94. switch (offset) {
  95. case AW_A10_PIT_TIMER_IRQ_EN:
  96. s->irq_enable = value;
  97. a10_pit_update_irq(s);
  98. break;
  99. case AW_A10_PIT_TIMER_IRQ_ST:
  100. s->irq_status &= ~value;
  101. a10_pit_update_irq(s);
  102. break;
  103. case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
  104. index = offset & 0xf0;
  105. index >>= 4;
  106. index -= 1;
  107. switch (offset & 0x0f) {
  108. case AW_A10_PIT_TIMER_CONTROL:
  109. s->control[index] = value;
  110. ptimer_transaction_begin(s->timer[index]);
  111. a10_pit_set_freq(s, index);
  112. if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
  113. ptimer_set_count(s->timer[index], s->interval[index]);
  114. }
  115. if (s->control[index] & AW_A10_PIT_TIMER_EN) {
  116. int oneshot = 0;
  117. if (s->control[index] & AW_A10_PIT_TIMER_MODE) {
  118. oneshot = 1;
  119. }
  120. ptimer_run(s->timer[index], oneshot);
  121. } else {
  122. ptimer_stop(s->timer[index]);
  123. }
  124. ptimer_transaction_commit(s->timer[index]);
  125. break;
  126. case AW_A10_PIT_TIMER_INTERVAL:
  127. s->interval[index] = value;
  128. ptimer_transaction_begin(s->timer[index]);
  129. ptimer_set_limit(s->timer[index], s->interval[index], 1);
  130. ptimer_transaction_commit(s->timer[index]);
  131. break;
  132. case AW_A10_PIT_TIMER_COUNT:
  133. s->count[index] = value;
  134. break;
  135. default:
  136. qemu_log_mask(LOG_GUEST_ERROR,
  137. "%s: Bad offset 0x%x\n", __func__, (int)offset);
  138. }
  139. break;
  140. case AW_A10_PIT_WDOG_CONTROL:
  141. s->watch_dog_control = value;
  142. break;
  143. case AW_A10_PIT_WDOG_MODE:
  144. s->watch_dog_mode = value;
  145. break;
  146. case AW_A10_PIT_COUNT_LO:
  147. s->count_lo = value;
  148. break;
  149. case AW_A10_PIT_COUNT_HI:
  150. s->count_hi = value;
  151. break;
  152. case AW_A10_PIT_COUNT_CTL:
  153. s->count_ctl = value;
  154. if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) {
  155. uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  156. s->count_lo = tmp_count;
  157. s->count_hi = tmp_count >> 32;
  158. s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN;
  159. }
  160. if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) {
  161. s->count_lo = 0;
  162. s->count_hi = 0;
  163. s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN;
  164. }
  165. break;
  166. default:
  167. qemu_log_mask(LOG_GUEST_ERROR,
  168. "%s: Bad offset 0x%x\n", __func__, (int)offset);
  169. break;
  170. }
  171. }
  172. static const MemoryRegionOps a10_pit_ops = {
  173. .read = a10_pit_read,
  174. .write = a10_pit_write,
  175. .endianness = DEVICE_NATIVE_ENDIAN,
  176. };
  177. static Property a10_pit_properties[] = {
  178. DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0),
  179. DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0),
  180. DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0),
  181. DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0),
  182. DEFINE_PROP_END_OF_LIST(),
  183. };
  184. static const VMStateDescription vmstate_a10_pit = {
  185. .name = "a10.pit",
  186. .version_id = 1,
  187. .minimum_version_id = 1,
  188. .fields = (VMStateField[]) {
  189. VMSTATE_UINT32(irq_enable, AwA10PITState),
  190. VMSTATE_UINT32(irq_status, AwA10PITState),
  191. VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR),
  192. VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR),
  193. VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR),
  194. VMSTATE_UINT32(watch_dog_mode, AwA10PITState),
  195. VMSTATE_UINT32(watch_dog_control, AwA10PITState),
  196. VMSTATE_UINT32(count_lo, AwA10PITState),
  197. VMSTATE_UINT32(count_hi, AwA10PITState),
  198. VMSTATE_UINT32(count_ctl, AwA10PITState),
  199. VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR),
  200. VMSTATE_END_OF_LIST()
  201. }
  202. };
  203. static void a10_pit_reset(DeviceState *dev)
  204. {
  205. AwA10PITState *s = AW_A10_PIT(dev);
  206. uint8_t i;
  207. s->irq_enable = 0;
  208. s->irq_status = 0;
  209. a10_pit_update_irq(s);
  210. for (i = 0; i < 6; i++) {
  211. s->control[i] = AW_A10_PIT_DEFAULT_CLOCK;
  212. s->interval[i] = 0;
  213. s->count[i] = 0;
  214. ptimer_transaction_begin(s->timer[i]);
  215. ptimer_stop(s->timer[i]);
  216. a10_pit_set_freq(s, i);
  217. ptimer_transaction_commit(s->timer[i]);
  218. }
  219. s->watch_dog_mode = 0;
  220. s->watch_dog_control = 0;
  221. s->count_lo = 0;
  222. s->count_hi = 0;
  223. s->count_ctl = 0;
  224. }
  225. static void a10_pit_timer_cb(void *opaque)
  226. {
  227. AwA10TimerContext *tc = opaque;
  228. AwA10PITState *s = tc->container;
  229. uint8_t i = tc->index;
  230. if (s->control[i] & AW_A10_PIT_TIMER_EN) {
  231. s->irq_status |= 1 << i;
  232. if (s->control[i] & AW_A10_PIT_TIMER_MODE) {
  233. ptimer_stop(s->timer[i]);
  234. s->control[i] &= ~AW_A10_PIT_TIMER_EN;
  235. }
  236. a10_pit_update_irq(s);
  237. }
  238. }
  239. static void a10_pit_init(Object *obj)
  240. {
  241. AwA10PITState *s = AW_A10_PIT(obj);
  242. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  243. uint8_t i;
  244. for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
  245. sysbus_init_irq(sbd, &s->irq[i]);
  246. }
  247. memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s,
  248. TYPE_AW_A10_PIT, 0x400);
  249. sysbus_init_mmio(sbd, &s->iomem);
  250. for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
  251. AwA10TimerContext *tc = &s->timer_context[i];
  252. tc->container = s;
  253. tc->index = i;
  254. s->timer[i] = ptimer_init(a10_pit_timer_cb, tc, PTIMER_POLICY_DEFAULT);
  255. }
  256. }
  257. static void a10_pit_class_init(ObjectClass *klass, void *data)
  258. {
  259. DeviceClass *dc = DEVICE_CLASS(klass);
  260. dc->reset = a10_pit_reset;
  261. device_class_set_props(dc, a10_pit_properties);
  262. dc->desc = "allwinner a10 timer";
  263. dc->vmsd = &vmstate_a10_pit;
  264. }
  265. static const TypeInfo a10_pit_info = {
  266. .name = TYPE_AW_A10_PIT,
  267. .parent = TYPE_SYS_BUS_DEVICE,
  268. .instance_size = sizeof(AwA10PITState),
  269. .instance_init = a10_pit_init,
  270. .class_init = a10_pit_class_init,
  271. };
  272. static void a10_register_types(void)
  273. {
  274. type_register_static(&a10_pit_info);
  275. }
  276. type_init(a10_register_types);