pl061.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. * Arm PrimeCell PL061 General Purpose IO with additional
  3. * Luminary Micro Stellaris bits.
  4. *
  5. * Copyright (c) 2007 CodeSourcery.
  6. * Written by Paul Brook
  7. *
  8. * This code is licenced under the GPL.
  9. */
  10. #include "hw.h"
  11. #include "primecell.h"
  12. //#define DEBUG_PL061 1
  13. #ifdef DEBUG_PL061
  14. #define DPRINTF(fmt, args...) \
  15. do { printf("pl061: " fmt , ##args); } while (0)
  16. #define BADF(fmt, args...) \
  17. do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0)
  18. #else
  19. #define DPRINTF(fmt, args...) do {} while(0)
  20. #define BADF(fmt, args...) \
  21. do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0)
  22. #endif
  23. static const uint8_t pl061_id[12] =
  24. { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
  25. typedef struct {
  26. int locked;
  27. uint8_t data;
  28. uint8_t old_data;
  29. uint8_t dir;
  30. uint8_t isense;
  31. uint8_t ibe;
  32. uint8_t iev;
  33. uint8_t im;
  34. uint8_t istate;
  35. uint8_t afsel;
  36. uint8_t dr2r;
  37. uint8_t dr4r;
  38. uint8_t dr8r;
  39. uint8_t odr;
  40. uint8_t pur;
  41. uint8_t pdr;
  42. uint8_t slr;
  43. uint8_t den;
  44. uint8_t cr;
  45. uint8_t float_high;
  46. qemu_irq irq;
  47. qemu_irq out[8];
  48. } pl061_state;
  49. static void pl061_update(pl061_state *s)
  50. {
  51. uint8_t changed;
  52. uint8_t mask;
  53. uint8_t out;
  54. int i;
  55. /* Outputs float high. */
  56. /* FIXME: This is board dependent. */
  57. out = (s->data & s->dir) | ~s->dir;
  58. changed = s->old_data ^ out;
  59. if (!changed)
  60. return;
  61. s->old_data = out;
  62. for (i = 0; i < 8; i++) {
  63. mask = 1 << i;
  64. if ((changed & mask) && s->out) {
  65. DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
  66. qemu_set_irq(s->out[i], (out & mask) != 0);
  67. }
  68. }
  69. /* FIXME: Implement input interrupts. */
  70. }
  71. static uint32_t pl061_read(void *opaque, target_phys_addr_t offset)
  72. {
  73. pl061_state *s = (pl061_state *)opaque;
  74. if (offset >= 0xfd0 && offset < 0x1000) {
  75. return pl061_id[(offset - 0xfd0) >> 2];
  76. }
  77. if (offset < 0x400) {
  78. return s->data & (offset >> 2);
  79. }
  80. switch (offset) {
  81. case 0x400: /* Direction */
  82. return s->dir;
  83. case 0x404: /* Interrupt sense */
  84. return s->isense;
  85. case 0x408: /* Interrupt both edges */
  86. return s->ibe;
  87. case 0x40c: /* Interupt event */
  88. return s->iev;
  89. case 0x410: /* Interrupt mask */
  90. return s->im;
  91. case 0x414: /* Raw interrupt status */
  92. return s->istate;
  93. case 0x418: /* Masked interrupt status */
  94. return s->istate | s->im;
  95. case 0x420: /* Alternate function select */
  96. return s->afsel;
  97. case 0x500: /* 2mA drive */
  98. return s->dr2r;
  99. case 0x504: /* 4mA drive */
  100. return s->dr4r;
  101. case 0x508: /* 8mA drive */
  102. return s->dr8r;
  103. case 0x50c: /* Open drain */
  104. return s->odr;
  105. case 0x510: /* Pull-up */
  106. return s->pur;
  107. case 0x514: /* Pull-down */
  108. return s->pdr;
  109. case 0x518: /* Slew rate control */
  110. return s->slr;
  111. case 0x51c: /* Digital enable */
  112. return s->den;
  113. case 0x520: /* Lock */
  114. return s->locked;
  115. case 0x524: /* Commit */
  116. return s->cr;
  117. default:
  118. cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n",
  119. (int)offset);
  120. return 0;
  121. }
  122. }
  123. static void pl061_write(void *opaque, target_phys_addr_t offset,
  124. uint32_t value)
  125. {
  126. pl061_state *s = (pl061_state *)opaque;
  127. uint8_t mask;
  128. if (offset < 0x400) {
  129. mask = (offset >> 2) & s->dir;
  130. s->data = (s->data & ~mask) | (value & mask);
  131. pl061_update(s);
  132. return;
  133. }
  134. switch (offset) {
  135. case 0x400: /* Direction */
  136. s->dir = value;
  137. break;
  138. case 0x404: /* Interrupt sense */
  139. s->isense = value;
  140. break;
  141. case 0x408: /* Interrupt both edges */
  142. s->ibe = value;
  143. break;
  144. case 0x40c: /* Interupt event */
  145. s->iev = value;
  146. break;
  147. case 0x410: /* Interrupt mask */
  148. s->im = value;
  149. break;
  150. case 0x41c: /* Interrupt clear */
  151. s->istate &= ~value;
  152. break;
  153. case 0x420: /* Alternate function select */
  154. mask = s->cr;
  155. s->afsel = (s->afsel & ~mask) | (value & mask);
  156. break;
  157. case 0x500: /* 2mA drive */
  158. s->dr2r = value;
  159. break;
  160. case 0x504: /* 4mA drive */
  161. s->dr4r = value;
  162. break;
  163. case 0x508: /* 8mA drive */
  164. s->dr8r = value;
  165. break;
  166. case 0x50c: /* Open drain */
  167. s->odr = value;
  168. break;
  169. case 0x510: /* Pull-up */
  170. s->pur = value;
  171. break;
  172. case 0x514: /* Pull-down */
  173. s->pdr = value;
  174. break;
  175. case 0x518: /* Slew rate control */
  176. s->slr = value;
  177. break;
  178. case 0x51c: /* Digital enable */
  179. s->den = value;
  180. break;
  181. case 0x520: /* Lock */
  182. s->locked = (value != 0xacce551);
  183. break;
  184. case 0x524: /* Commit */
  185. if (!s->locked)
  186. s->cr = value;
  187. break;
  188. default:
  189. cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n",
  190. (int)offset);
  191. }
  192. pl061_update(s);
  193. }
  194. static void pl061_reset(pl061_state *s)
  195. {
  196. s->locked = 1;
  197. s->cr = 0xff;
  198. }
  199. static void pl061_set_irq(void * opaque, int irq, int level)
  200. {
  201. pl061_state *s = (pl061_state *)opaque;
  202. uint8_t mask;
  203. mask = 1 << irq;
  204. if ((s->dir & mask) == 0) {
  205. s->data &= ~mask;
  206. if (level)
  207. s->data |= mask;
  208. pl061_update(s);
  209. }
  210. }
  211. static CPUReadMemoryFunc *pl061_readfn[] = {
  212. pl061_read,
  213. pl061_read,
  214. pl061_read
  215. };
  216. static CPUWriteMemoryFunc *pl061_writefn[] = {
  217. pl061_write,
  218. pl061_write,
  219. pl061_write
  220. };
  221. static void pl061_save(QEMUFile *f, void *opaque)
  222. {
  223. pl061_state *s = (pl061_state *)opaque;
  224. qemu_put_be32(f, s->locked);
  225. qemu_put_be32(f, s->data);
  226. qemu_put_be32(f, s->old_data);
  227. qemu_put_be32(f, s->dir);
  228. qemu_put_be32(f, s->isense);
  229. qemu_put_be32(f, s->ibe);
  230. qemu_put_be32(f, s->iev);
  231. qemu_put_be32(f, s->im);
  232. qemu_put_be32(f, s->istate);
  233. qemu_put_be32(f, s->afsel);
  234. qemu_put_be32(f, s->dr2r);
  235. qemu_put_be32(f, s->dr4r);
  236. qemu_put_be32(f, s->dr8r);
  237. qemu_put_be32(f, s->odr);
  238. qemu_put_be32(f, s->pur);
  239. qemu_put_be32(f, s->pdr);
  240. qemu_put_be32(f, s->slr);
  241. qemu_put_be32(f, s->den);
  242. qemu_put_be32(f, s->cr);
  243. qemu_put_be32(f, s->float_high);
  244. }
  245. static int pl061_load(QEMUFile *f, void *opaque, int version_id)
  246. {
  247. pl061_state *s = (pl061_state *)opaque;
  248. if (version_id != 1)
  249. return -EINVAL;
  250. s->locked = qemu_get_be32(f);
  251. s->data = qemu_get_be32(f);
  252. s->old_data = qemu_get_be32(f);
  253. s->dir = qemu_get_be32(f);
  254. s->isense = qemu_get_be32(f);
  255. s->ibe = qemu_get_be32(f);
  256. s->iev = qemu_get_be32(f);
  257. s->im = qemu_get_be32(f);
  258. s->istate = qemu_get_be32(f);
  259. s->afsel = qemu_get_be32(f);
  260. s->dr2r = qemu_get_be32(f);
  261. s->dr4r = qemu_get_be32(f);
  262. s->dr8r = qemu_get_be32(f);
  263. s->odr = qemu_get_be32(f);
  264. s->pur = qemu_get_be32(f);
  265. s->pdr = qemu_get_be32(f);
  266. s->slr = qemu_get_be32(f);
  267. s->den = qemu_get_be32(f);
  268. s->cr = qemu_get_be32(f);
  269. s->float_high = qemu_get_be32(f);
  270. return 0;
  271. }
  272. /* Returns an array of inputs. */
  273. qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out)
  274. {
  275. int iomemtype;
  276. pl061_state *s;
  277. s = (pl061_state *)qemu_mallocz(sizeof(pl061_state));
  278. iomemtype = cpu_register_io_memory(0, pl061_readfn,
  279. pl061_writefn, s);
  280. cpu_register_physical_memory(base, 0x00001000, iomemtype);
  281. s->irq = irq;
  282. pl061_reset(s);
  283. if (out)
  284. *out = s->out;
  285. register_savevm("pl061_gpio", -1, 1, pl061_save, pl061_load, s);
  286. return qemu_allocate_irqs(pl061_set_irq, s, 8);
  287. }