milkymist-sysctl.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. * QEMU model of the Milkymist System Controller.
  3. *
  4. * Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18. *
  19. *
  20. * Specification available at:
  21. * http://www.milkymist.org/socdoc/sysctl.pdf
  22. */
  23. #include "hw.h"
  24. #include "sysbus.h"
  25. #include "sysemu/sysemu.h"
  26. #include "trace.h"
  27. #include "qemu/timer.h"
  28. #include "ptimer.h"
  29. #include "qemu/error-report.h"
  30. enum {
  31. CTRL_ENABLE = (1<<0),
  32. CTRL_AUTORESTART = (1<<1),
  33. };
  34. enum {
  35. ICAP_READY = (1<<0),
  36. };
  37. enum {
  38. R_GPIO_IN = 0,
  39. R_GPIO_OUT,
  40. R_GPIO_INTEN,
  41. R_TIMER0_CONTROL = 4,
  42. R_TIMER0_COMPARE,
  43. R_TIMER0_COUNTER,
  44. R_TIMER1_CONTROL = 8,
  45. R_TIMER1_COMPARE,
  46. R_TIMER1_COUNTER,
  47. R_ICAP = 16,
  48. R_DBG_SCRATCHPAD = 20,
  49. R_DBG_WRITE_LOCK,
  50. R_CLK_FREQUENCY = 29,
  51. R_CAPABILITIES,
  52. R_SYSTEM_ID,
  53. R_MAX
  54. };
  55. struct MilkymistSysctlState {
  56. SysBusDevice busdev;
  57. MemoryRegion regs_region;
  58. QEMUBH *bh0;
  59. QEMUBH *bh1;
  60. ptimer_state *ptimer0;
  61. ptimer_state *ptimer1;
  62. uint32_t freq_hz;
  63. uint32_t capabilities;
  64. uint32_t systemid;
  65. uint32_t strappings;
  66. uint32_t regs[R_MAX];
  67. qemu_irq gpio_irq;
  68. qemu_irq timer0_irq;
  69. qemu_irq timer1_irq;
  70. };
  71. typedef struct MilkymistSysctlState MilkymistSysctlState;
  72. static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value)
  73. {
  74. trace_milkymist_sysctl_icap_write(value);
  75. switch (value & 0xffff) {
  76. case 0x000e:
  77. qemu_system_shutdown_request();
  78. break;
  79. }
  80. }
  81. static uint64_t sysctl_read(void *opaque, hwaddr addr,
  82. unsigned size)
  83. {
  84. MilkymistSysctlState *s = opaque;
  85. uint32_t r = 0;
  86. addr >>= 2;
  87. switch (addr) {
  88. case R_TIMER0_COUNTER:
  89. r = (uint32_t)ptimer_get_count(s->ptimer0);
  90. /* milkymist timer counts up */
  91. r = s->regs[R_TIMER0_COMPARE] - r;
  92. break;
  93. case R_TIMER1_COUNTER:
  94. r = (uint32_t)ptimer_get_count(s->ptimer1);
  95. /* milkymist timer counts up */
  96. r = s->regs[R_TIMER1_COMPARE] - r;
  97. break;
  98. case R_GPIO_IN:
  99. case R_GPIO_OUT:
  100. case R_GPIO_INTEN:
  101. case R_TIMER0_CONTROL:
  102. case R_TIMER0_COMPARE:
  103. case R_TIMER1_CONTROL:
  104. case R_TIMER1_COMPARE:
  105. case R_ICAP:
  106. case R_DBG_SCRATCHPAD:
  107. case R_DBG_WRITE_LOCK:
  108. case R_CLK_FREQUENCY:
  109. case R_CAPABILITIES:
  110. case R_SYSTEM_ID:
  111. r = s->regs[addr];
  112. break;
  113. default:
  114. error_report("milkymist_sysctl: read access to unknown register 0x"
  115. TARGET_FMT_plx, addr << 2);
  116. break;
  117. }
  118. trace_milkymist_sysctl_memory_read(addr << 2, r);
  119. return r;
  120. }
  121. static void sysctl_write(void *opaque, hwaddr addr, uint64_t value,
  122. unsigned size)
  123. {
  124. MilkymistSysctlState *s = opaque;
  125. trace_milkymist_sysctl_memory_write(addr, value);
  126. addr >>= 2;
  127. switch (addr) {
  128. case R_GPIO_OUT:
  129. case R_GPIO_INTEN:
  130. case R_TIMER0_COUNTER:
  131. case R_TIMER1_COUNTER:
  132. case R_DBG_SCRATCHPAD:
  133. s->regs[addr] = value;
  134. break;
  135. case R_TIMER0_COMPARE:
  136. ptimer_set_limit(s->ptimer0, value, 0);
  137. s->regs[addr] = value;
  138. break;
  139. case R_TIMER1_COMPARE:
  140. ptimer_set_limit(s->ptimer1, value, 0);
  141. s->regs[addr] = value;
  142. break;
  143. case R_TIMER0_CONTROL:
  144. s->regs[addr] = value;
  145. if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) {
  146. trace_milkymist_sysctl_start_timer0();
  147. ptimer_set_count(s->ptimer0,
  148. s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]);
  149. ptimer_run(s->ptimer0, 0);
  150. } else {
  151. trace_milkymist_sysctl_stop_timer0();
  152. ptimer_stop(s->ptimer0);
  153. }
  154. break;
  155. case R_TIMER1_CONTROL:
  156. s->regs[addr] = value;
  157. if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) {
  158. trace_milkymist_sysctl_start_timer1();
  159. ptimer_set_count(s->ptimer1,
  160. s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]);
  161. ptimer_run(s->ptimer1, 0);
  162. } else {
  163. trace_milkymist_sysctl_stop_timer1();
  164. ptimer_stop(s->ptimer1);
  165. }
  166. break;
  167. case R_ICAP:
  168. sysctl_icap_write(s, value);
  169. break;
  170. case R_DBG_WRITE_LOCK:
  171. s->regs[addr] = 1;
  172. break;
  173. case R_SYSTEM_ID:
  174. qemu_system_reset_request();
  175. break;
  176. case R_GPIO_IN:
  177. case R_CLK_FREQUENCY:
  178. case R_CAPABILITIES:
  179. error_report("milkymist_sysctl: write to read-only register 0x"
  180. TARGET_FMT_plx, addr << 2);
  181. break;
  182. default:
  183. error_report("milkymist_sysctl: write access to unknown register 0x"
  184. TARGET_FMT_plx, addr << 2);
  185. break;
  186. }
  187. }
  188. static const MemoryRegionOps sysctl_mmio_ops = {
  189. .read = sysctl_read,
  190. .write = sysctl_write,
  191. .valid = {
  192. .min_access_size = 4,
  193. .max_access_size = 4,
  194. },
  195. .endianness = DEVICE_NATIVE_ENDIAN,
  196. };
  197. static void timer0_hit(void *opaque)
  198. {
  199. MilkymistSysctlState *s = opaque;
  200. if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) {
  201. s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE;
  202. trace_milkymist_sysctl_stop_timer0();
  203. ptimer_stop(s->ptimer0);
  204. }
  205. trace_milkymist_sysctl_pulse_irq_timer0();
  206. qemu_irq_pulse(s->timer0_irq);
  207. }
  208. static void timer1_hit(void *opaque)
  209. {
  210. MilkymistSysctlState *s = opaque;
  211. if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) {
  212. s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE;
  213. trace_milkymist_sysctl_stop_timer1();
  214. ptimer_stop(s->ptimer1);
  215. }
  216. trace_milkymist_sysctl_pulse_irq_timer1();
  217. qemu_irq_pulse(s->timer1_irq);
  218. }
  219. static void milkymist_sysctl_reset(DeviceState *d)
  220. {
  221. MilkymistSysctlState *s =
  222. container_of(d, MilkymistSysctlState, busdev.qdev);
  223. int i;
  224. for (i = 0; i < R_MAX; i++) {
  225. s->regs[i] = 0;
  226. }
  227. ptimer_stop(s->ptimer0);
  228. ptimer_stop(s->ptimer1);
  229. /* defaults */
  230. s->regs[R_ICAP] = ICAP_READY;
  231. s->regs[R_SYSTEM_ID] = s->systemid;
  232. s->regs[R_CLK_FREQUENCY] = s->freq_hz;
  233. s->regs[R_CAPABILITIES] = s->capabilities;
  234. s->regs[R_GPIO_IN] = s->strappings;
  235. }
  236. static int milkymist_sysctl_init(SysBusDevice *dev)
  237. {
  238. MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev);
  239. sysbus_init_irq(dev, &s->gpio_irq);
  240. sysbus_init_irq(dev, &s->timer0_irq);
  241. sysbus_init_irq(dev, &s->timer1_irq);
  242. s->bh0 = qemu_bh_new(timer0_hit, s);
  243. s->bh1 = qemu_bh_new(timer1_hit, s);
  244. s->ptimer0 = ptimer_init(s->bh0);
  245. s->ptimer1 = ptimer_init(s->bh1);
  246. ptimer_set_freq(s->ptimer0, s->freq_hz);
  247. ptimer_set_freq(s->ptimer1, s->freq_hz);
  248. memory_region_init_io(&s->regs_region, &sysctl_mmio_ops, s,
  249. "milkymist-sysctl", R_MAX * 4);
  250. sysbus_init_mmio(dev, &s->regs_region);
  251. return 0;
  252. }
  253. static const VMStateDescription vmstate_milkymist_sysctl = {
  254. .name = "milkymist-sysctl",
  255. .version_id = 1,
  256. .minimum_version_id = 1,
  257. .minimum_version_id_old = 1,
  258. .fields = (VMStateField[]) {
  259. VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX),
  260. VMSTATE_PTIMER(ptimer0, MilkymistSysctlState),
  261. VMSTATE_PTIMER(ptimer1, MilkymistSysctlState),
  262. VMSTATE_END_OF_LIST()
  263. }
  264. };
  265. static Property milkymist_sysctl_properties[] = {
  266. DEFINE_PROP_UINT32("frequency", MilkymistSysctlState,
  267. freq_hz, 80000000),
  268. DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState,
  269. capabilities, 0x00000000),
  270. DEFINE_PROP_UINT32("systemid", MilkymistSysctlState,
  271. systemid, 0x10014d31),
  272. DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState,
  273. strappings, 0x00000001),
  274. DEFINE_PROP_END_OF_LIST(),
  275. };
  276. static void milkymist_sysctl_class_init(ObjectClass *klass, void *data)
  277. {
  278. DeviceClass *dc = DEVICE_CLASS(klass);
  279. SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
  280. k->init = milkymist_sysctl_init;
  281. dc->reset = milkymist_sysctl_reset;
  282. dc->vmsd = &vmstate_milkymist_sysctl;
  283. dc->props = milkymist_sysctl_properties;
  284. }
  285. static const TypeInfo milkymist_sysctl_info = {
  286. .name = "milkymist-sysctl",
  287. .parent = TYPE_SYS_BUS_DEVICE,
  288. .instance_size = sizeof(MilkymistSysctlState),
  289. .class_init = milkymist_sysctl_class_init,
  290. };
  291. static void milkymist_sysctl_register_types(void)
  292. {
  293. type_register_static(&milkymist_sysctl_info);
  294. }
  295. type_init(milkymist_sysctl_register_types)