2
0

milkymist-softusb.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /*
  2. * QEMU model of the Milkymist SoftUSB block.
  3. *
  4. * Copyright (c) 2010 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. * not available yet
  22. */
  23. #include "hw.h"
  24. #include "sysbus.h"
  25. #include "trace.h"
  26. #include "console.h"
  27. #include "hid.h"
  28. #include "qemu-error.h"
  29. enum {
  30. R_CTRL = 0,
  31. R_MAX
  32. };
  33. enum {
  34. CTRL_RESET = (1<<0),
  35. };
  36. #define COMLOC_DEBUG_PRODUCE 0x1000
  37. #define COMLOC_DEBUG_BASE 0x1001
  38. #define COMLOC_MEVT_PRODUCE 0x1101
  39. #define COMLOC_MEVT_BASE 0x1102
  40. #define COMLOC_KEVT_PRODUCE 0x1142
  41. #define COMLOC_KEVT_BASE 0x1143
  42. struct MilkymistSoftUsbState {
  43. SysBusDevice busdev;
  44. HIDState hid_kbd;
  45. HIDState hid_mouse;
  46. MemoryRegion regs_region;
  47. MemoryRegion pmem;
  48. MemoryRegion dmem;
  49. qemu_irq irq;
  50. /* device properties */
  51. uint32_t pmem_base;
  52. uint32_t pmem_size;
  53. uint32_t dmem_base;
  54. uint32_t dmem_size;
  55. /* device registers */
  56. uint32_t regs[R_MAX];
  57. /* mouse state */
  58. uint8_t mouse_hid_buffer[4];
  59. /* keyboard state */
  60. uint8_t kbd_hid_buffer[8];
  61. };
  62. typedef struct MilkymistSoftUsbState MilkymistSoftUsbState;
  63. static uint64_t softusb_read(void *opaque, target_phys_addr_t addr,
  64. unsigned size)
  65. {
  66. MilkymistSoftUsbState *s = opaque;
  67. uint32_t r = 0;
  68. addr >>= 2;
  69. switch (addr) {
  70. case R_CTRL:
  71. r = s->regs[addr];
  72. break;
  73. default:
  74. error_report("milkymist_softusb: read access to unknown register 0x"
  75. TARGET_FMT_plx, addr << 2);
  76. break;
  77. }
  78. trace_milkymist_softusb_memory_read(addr << 2, r);
  79. return r;
  80. }
  81. static void
  82. softusb_write(void *opaque, target_phys_addr_t addr, uint64_t value,
  83. unsigned size)
  84. {
  85. MilkymistSoftUsbState *s = opaque;
  86. trace_milkymist_softusb_memory_write(addr, value);
  87. addr >>= 2;
  88. switch (addr) {
  89. case R_CTRL:
  90. s->regs[addr] = value;
  91. break;
  92. default:
  93. error_report("milkymist_softusb: write access to unknown register 0x"
  94. TARGET_FMT_plx, addr << 2);
  95. break;
  96. }
  97. }
  98. static const MemoryRegionOps softusb_mmio_ops = {
  99. .read = softusb_read,
  100. .write = softusb_write,
  101. .endianness = DEVICE_NATIVE_ENDIAN,
  102. .valid = {
  103. .min_access_size = 4,
  104. .max_access_size = 4,
  105. },
  106. };
  107. static inline void softusb_read_dmem(MilkymistSoftUsbState *s,
  108. uint32_t offset, uint8_t *buf, uint32_t len)
  109. {
  110. if (offset + len >= s->dmem_size) {
  111. error_report("milkymist_softusb: read dmem out of bounds "
  112. "at offset 0x%x, len %d", offset, len);
  113. return;
  114. }
  115. cpu_physical_memory_read(s->dmem_base + offset, buf, len);
  116. }
  117. static inline void softusb_write_dmem(MilkymistSoftUsbState *s,
  118. uint32_t offset, uint8_t *buf, uint32_t len)
  119. {
  120. if (offset + len >= s->dmem_size) {
  121. error_report("milkymist_softusb: write dmem out of bounds "
  122. "at offset 0x%x, len %d", offset, len);
  123. return;
  124. }
  125. cpu_physical_memory_write(s->dmem_base + offset, buf, len);
  126. }
  127. static inline void softusb_read_pmem(MilkymistSoftUsbState *s,
  128. uint32_t offset, uint8_t *buf, uint32_t len)
  129. {
  130. if (offset + len >= s->pmem_size) {
  131. error_report("milkymist_softusb: read pmem out of bounds "
  132. "at offset 0x%x, len %d", offset, len);
  133. return;
  134. }
  135. cpu_physical_memory_read(s->pmem_base + offset, buf, len);
  136. }
  137. static inline void softusb_write_pmem(MilkymistSoftUsbState *s,
  138. uint32_t offset, uint8_t *buf, uint32_t len)
  139. {
  140. if (offset + len >= s->pmem_size) {
  141. error_report("milkymist_softusb: write pmem out of bounds "
  142. "at offset 0x%x, len %d", offset, len);
  143. return;
  144. }
  145. cpu_physical_memory_write(s->pmem_base + offset, buf, len);
  146. }
  147. static void softusb_mouse_changed(MilkymistSoftUsbState *s)
  148. {
  149. uint8_t m;
  150. softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
  151. trace_milkymist_softusb_mevt(m);
  152. softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4);
  153. m = (m + 1) & 0xf;
  154. softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
  155. trace_milkymist_softusb_pulse_irq();
  156. qemu_irq_pulse(s->irq);
  157. }
  158. static void softusb_kbd_changed(MilkymistSoftUsbState *s)
  159. {
  160. uint8_t m;
  161. softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
  162. trace_milkymist_softusb_kevt(m);
  163. softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8);
  164. m = (m + 1) & 0x7;
  165. softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
  166. trace_milkymist_softusb_pulse_irq();
  167. qemu_irq_pulse(s->irq);
  168. }
  169. static void softusb_kbd_hid_datain(HIDState *hs)
  170. {
  171. MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd);
  172. int len;
  173. /* if device is in reset, do nothing */
  174. if (s->regs[R_CTRL] & CTRL_RESET) {
  175. return;
  176. }
  177. len = hid_keyboard_poll(hs, s->kbd_hid_buffer, sizeof(s->kbd_hid_buffer));
  178. if (len == 8) {
  179. softusb_kbd_changed(s);
  180. }
  181. }
  182. static void softusb_mouse_hid_datain(HIDState *hs)
  183. {
  184. MilkymistSoftUsbState *s =
  185. container_of(hs, MilkymistSoftUsbState, hid_mouse);
  186. int len;
  187. /* if device is in reset, do nothing */
  188. if (s->regs[R_CTRL] & CTRL_RESET) {
  189. return;
  190. }
  191. len = hid_pointer_poll(hs, s->mouse_hid_buffer,
  192. sizeof(s->mouse_hid_buffer));
  193. if (len == 4) {
  194. softusb_mouse_changed(s);
  195. }
  196. }
  197. static void milkymist_softusb_reset(DeviceState *d)
  198. {
  199. MilkymistSoftUsbState *s =
  200. container_of(d, MilkymistSoftUsbState, busdev.qdev);
  201. int i;
  202. for (i = 0; i < R_MAX; i++) {
  203. s->regs[i] = 0;
  204. }
  205. memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer));
  206. memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer));
  207. hid_reset(&s->hid_kbd);
  208. hid_reset(&s->hid_mouse);
  209. /* defaults */
  210. s->regs[R_CTRL] = CTRL_RESET;
  211. }
  212. static int milkymist_softusb_init(SysBusDevice *dev)
  213. {
  214. MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev);
  215. sysbus_init_irq(dev, &s->irq);
  216. memory_region_init_io(&s->regs_region, &softusb_mmio_ops, s,
  217. "milkymist-softusb", R_MAX * 4);
  218. sysbus_init_mmio_region(dev, &s->regs_region);
  219. /* register pmem and dmem */
  220. memory_region_init_ram(&s->pmem, NULL, "milkymist-softusb.pmem",
  221. s->pmem_size);
  222. sysbus_add_memory(dev, s->pmem_base, &s->pmem);
  223. memory_region_init_ram(&s->dmem, NULL, "milkymist-softusb.dmem",
  224. s->dmem_size);
  225. sysbus_add_memory(dev, s->dmem_base, &s->dmem);
  226. hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain);
  227. hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain);
  228. return 0;
  229. }
  230. static const VMStateDescription vmstate_milkymist_softusb = {
  231. .name = "milkymist-softusb",
  232. .version_id = 1,
  233. .minimum_version_id = 1,
  234. .minimum_version_id_old = 1,
  235. .fields = (VMStateField[]) {
  236. VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
  237. VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState),
  238. VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState),
  239. VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState),
  240. VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState),
  241. VMSTATE_END_OF_LIST()
  242. }
  243. };
  244. static SysBusDeviceInfo milkymist_softusb_info = {
  245. .init = milkymist_softusb_init,
  246. .qdev.name = "milkymist-softusb",
  247. .qdev.size = sizeof(MilkymistSoftUsbState),
  248. .qdev.vmsd = &vmstate_milkymist_softusb,
  249. .qdev.reset = milkymist_softusb_reset,
  250. .qdev.props = (Property[]) {
  251. DEFINE_PROP_UINT32(
  252. "pmem_base", MilkymistSoftUsbState, pmem_base, 0xa0000000
  253. ),
  254. DEFINE_PROP_UINT32(
  255. "pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000
  256. ),
  257. DEFINE_PROP_UINT32(
  258. "dmem_base", MilkymistSoftUsbState, dmem_base, 0xa0020000
  259. ),
  260. DEFINE_PROP_UINT32(
  261. "dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000
  262. ),
  263. DEFINE_PROP_END_OF_LIST(),
  264. }
  265. };
  266. static void milkymist_softusb_register(void)
  267. {
  268. sysbus_register_withprop(&milkymist_softusb_info);
  269. }
  270. device_init(milkymist_softusb_register)