allwinner-a10-pit.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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 "hw/sysbus.h"
  18. #include "sysemu/sysemu.h"
  19. #include "hw/timer/allwinner-a10-pit.h"
  20. static void a10_pit_update_irq(AwA10PITState *s)
  21. {
  22. int i;
  23. for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
  24. qemu_set_irq(s->irq[i], !!(s->irq_status & s->irq_enable & (1 << i)));
  25. }
  26. }
  27. static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size)
  28. {
  29. AwA10PITState *s = AW_A10_PIT(opaque);
  30. uint8_t index;
  31. switch (offset) {
  32. case AW_A10_PIT_TIMER_IRQ_EN:
  33. return s->irq_enable;
  34. case AW_A10_PIT_TIMER_IRQ_ST:
  35. return s->irq_status;
  36. case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
  37. index = offset & 0xf0;
  38. index >>= 4;
  39. index -= 1;
  40. switch (offset & 0x0f) {
  41. case AW_A10_PIT_TIMER_CONTROL:
  42. return s->control[index];
  43. case AW_A10_PIT_TIMER_INTERVAL:
  44. return s->interval[index];
  45. case AW_A10_PIT_TIMER_COUNT:
  46. s->count[index] = ptimer_get_count(s->timer[index]);
  47. return s->count[index];
  48. default:
  49. qemu_log_mask(LOG_GUEST_ERROR,
  50. "%s: Bad offset 0x%x\n", __func__, (int)offset);
  51. break;
  52. }
  53. case AW_A10_PIT_WDOG_CONTROL:
  54. break;
  55. case AW_A10_PIT_WDOG_MODE:
  56. break;
  57. case AW_A10_PIT_COUNT_LO:
  58. return s->count_lo;
  59. case AW_A10_PIT_COUNT_HI:
  60. return s->count_hi;
  61. case AW_A10_PIT_COUNT_CTL:
  62. return s->count_ctl;
  63. default:
  64. qemu_log_mask(LOG_GUEST_ERROR,
  65. "%s: Bad offset 0x%x\n", __func__, (int)offset);
  66. break;
  67. }
  68. return 0;
  69. }
  70. static void a10_pit_set_freq(AwA10PITState *s, int index)
  71. {
  72. uint32_t prescaler, source, source_freq;
  73. prescaler = 1 << extract32(s->control[index], 4, 3);
  74. source = extract32(s->control[index], 2, 2);
  75. source_freq = s->clk_freq[source];
  76. if (source_freq) {
  77. ptimer_set_freq(s->timer[index], source_freq / prescaler);
  78. } else {
  79. qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid clock source %u\n",
  80. __func__, source);
  81. }
  82. }
  83. static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
  84. unsigned size)
  85. {
  86. AwA10PITState *s = AW_A10_PIT(opaque);
  87. uint8_t index;
  88. switch (offset) {
  89. case AW_A10_PIT_TIMER_IRQ_EN:
  90. s->irq_enable = value;
  91. a10_pit_update_irq(s);
  92. break;
  93. case AW_A10_PIT_TIMER_IRQ_ST:
  94. s->irq_status &= ~value;
  95. a10_pit_update_irq(s);
  96. break;
  97. case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
  98. index = offset & 0xf0;
  99. index >>= 4;
  100. index -= 1;
  101. switch (offset & 0x0f) {
  102. case AW_A10_PIT_TIMER_CONTROL:
  103. s->control[index] = value;
  104. a10_pit_set_freq(s, index);
  105. if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
  106. ptimer_set_count(s->timer[index], s->interval[index]);
  107. }
  108. if (s->control[index] & AW_A10_PIT_TIMER_EN) {
  109. int oneshot = 0;
  110. if (s->control[index] & AW_A10_PIT_TIMER_MODE) {
  111. oneshot = 1;
  112. }
  113. ptimer_run(s->timer[index], oneshot);
  114. } else {
  115. ptimer_stop(s->timer[index]);
  116. }
  117. break;
  118. case AW_A10_PIT_TIMER_INTERVAL:
  119. s->interval[index] = value;
  120. ptimer_set_limit(s->timer[index], s->interval[index], 1);
  121. break;
  122. case AW_A10_PIT_TIMER_COUNT:
  123. s->count[index] = value;
  124. break;
  125. default:
  126. qemu_log_mask(LOG_GUEST_ERROR,
  127. "%s: Bad offset 0x%x\n", __func__, (int)offset);
  128. }
  129. break;
  130. case AW_A10_PIT_WDOG_CONTROL:
  131. s->watch_dog_control = value;
  132. break;
  133. case AW_A10_PIT_WDOG_MODE:
  134. s->watch_dog_mode = value;
  135. break;
  136. case AW_A10_PIT_COUNT_LO:
  137. s->count_lo = value;
  138. break;
  139. case AW_A10_PIT_COUNT_HI:
  140. s->count_hi = value;
  141. break;
  142. case AW_A10_PIT_COUNT_CTL:
  143. s->count_ctl = value;
  144. if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) {
  145. uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  146. s->count_lo = tmp_count;
  147. s->count_hi = tmp_count >> 32;
  148. s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN;
  149. }
  150. if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) {
  151. s->count_lo = 0;
  152. s->count_hi = 0;
  153. s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN;
  154. }
  155. break;
  156. default:
  157. qemu_log_mask(LOG_GUEST_ERROR,
  158. "%s: Bad offset 0x%x\n", __func__, (int)offset);
  159. break;
  160. }
  161. }
  162. static const MemoryRegionOps a10_pit_ops = {
  163. .read = a10_pit_read,
  164. .write = a10_pit_write,
  165. .endianness = DEVICE_NATIVE_ENDIAN,
  166. };
  167. static Property a10_pit_properties[] = {
  168. DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0),
  169. DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0),
  170. DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0),
  171. DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0),
  172. DEFINE_PROP_END_OF_LIST(),
  173. };
  174. static const VMStateDescription vmstate_a10_pit = {
  175. .name = "a10.pit",
  176. .version_id = 1,
  177. .minimum_version_id = 1,
  178. .fields = (VMStateField[]) {
  179. VMSTATE_UINT32(irq_enable, AwA10PITState),
  180. VMSTATE_UINT32(irq_status, AwA10PITState),
  181. VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR),
  182. VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR),
  183. VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR),
  184. VMSTATE_UINT32(watch_dog_mode, AwA10PITState),
  185. VMSTATE_UINT32(watch_dog_control, AwA10PITState),
  186. VMSTATE_UINT32(count_lo, AwA10PITState),
  187. VMSTATE_UINT32(count_hi, AwA10PITState),
  188. VMSTATE_UINT32(count_ctl, AwA10PITState),
  189. VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR),
  190. VMSTATE_END_OF_LIST()
  191. }
  192. };
  193. static void a10_pit_reset(DeviceState *dev)
  194. {
  195. AwA10PITState *s = AW_A10_PIT(dev);
  196. uint8_t i;
  197. s->irq_enable = 0;
  198. s->irq_status = 0;
  199. a10_pit_update_irq(s);
  200. for (i = 0; i < 6; i++) {
  201. s->control[i] = AW_A10_PIT_DEFAULT_CLOCK;
  202. s->interval[i] = 0;
  203. s->count[i] = 0;
  204. ptimer_stop(s->timer[i]);
  205. a10_pit_set_freq(s, i);
  206. }
  207. s->watch_dog_mode = 0;
  208. s->watch_dog_control = 0;
  209. s->count_lo = 0;
  210. s->count_hi = 0;
  211. s->count_ctl = 0;
  212. }
  213. static void a10_pit_timer_cb(void *opaque)
  214. {
  215. AwA10TimerContext *tc = opaque;
  216. AwA10PITState *s = tc->container;
  217. uint8_t i = tc->index;
  218. if (s->control[i] & AW_A10_PIT_TIMER_EN) {
  219. s->irq_status |= 1 << i;
  220. if (s->control[i] & AW_A10_PIT_TIMER_MODE) {
  221. ptimer_stop(s->timer[i]);
  222. s->control[i] &= ~AW_A10_PIT_TIMER_EN;
  223. }
  224. a10_pit_update_irq(s);
  225. }
  226. }
  227. static void a10_pit_init(Object *obj)
  228. {
  229. AwA10PITState *s = AW_A10_PIT(obj);
  230. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  231. QEMUBH * bh[AW_A10_PIT_TIMER_NR];
  232. uint8_t i;
  233. for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
  234. sysbus_init_irq(sbd, &s->irq[i]);
  235. }
  236. memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s,
  237. TYPE_AW_A10_PIT, 0x400);
  238. sysbus_init_mmio(sbd, &s->iomem);
  239. for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
  240. AwA10TimerContext *tc = &s->timer_context[i];
  241. tc->container = s;
  242. tc->index = i;
  243. bh[i] = qemu_bh_new(a10_pit_timer_cb, tc);
  244. s->timer[i] = ptimer_init(bh[i]);
  245. }
  246. }
  247. static void a10_pit_class_init(ObjectClass *klass, void *data)
  248. {
  249. DeviceClass *dc = DEVICE_CLASS(klass);
  250. dc->reset = a10_pit_reset;
  251. dc->props = a10_pit_properties;
  252. dc->desc = "allwinner a10 timer";
  253. dc->vmsd = &vmstate_a10_pit;
  254. }
  255. static const TypeInfo a10_pit_info = {
  256. .name = TYPE_AW_A10_PIT,
  257. .parent = TYPE_SYS_BUS_DEVICE,
  258. .instance_size = sizeof(AwA10PITState),
  259. .instance_init = a10_pit_init,
  260. .class_init = a10_pit_class_init,
  261. };
  262. static void a10_register_types(void)
  263. {
  264. type_register_static(&a10_pit_info);
  265. }
  266. type_init(a10_register_types);