pl061.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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 licensed under the GPL.
  9. */
  10. #include "qemu/osdep.h"
  11. #include "hw/irq.h"
  12. #include "hw/sysbus.h"
  13. #include "migration/vmstate.h"
  14. #include "qemu/log.h"
  15. #include "qemu/module.h"
  16. //#define DEBUG_PL061 1
  17. #ifdef DEBUG_PL061
  18. #define DPRINTF(fmt, ...) \
  19. do { printf("pl061: " fmt , ## __VA_ARGS__); } while (0)
  20. #define BADF(fmt, ...) \
  21. do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
  22. #else
  23. #define DPRINTF(fmt, ...) do {} while(0)
  24. #define BADF(fmt, ...) \
  25. do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__);} while (0)
  26. #endif
  27. static const uint8_t pl061_id[12] =
  28. { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  29. static const uint8_t pl061_id_luminary[12] =
  30. { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
  31. #define TYPE_PL061 "pl061"
  32. #define PL061(obj) OBJECT_CHECK(PL061State, (obj), TYPE_PL061)
  33. typedef struct PL061State {
  34. SysBusDevice parent_obj;
  35. MemoryRegion iomem;
  36. uint32_t locked;
  37. uint32_t data;
  38. uint32_t old_out_data;
  39. uint32_t old_in_data;
  40. uint32_t dir;
  41. uint32_t isense;
  42. uint32_t ibe;
  43. uint32_t iev;
  44. uint32_t im;
  45. uint32_t istate;
  46. uint32_t afsel;
  47. uint32_t dr2r;
  48. uint32_t dr4r;
  49. uint32_t dr8r;
  50. uint32_t odr;
  51. uint32_t pur;
  52. uint32_t pdr;
  53. uint32_t slr;
  54. uint32_t den;
  55. uint32_t cr;
  56. uint32_t amsel;
  57. qemu_irq irq;
  58. qemu_irq out[8];
  59. const unsigned char *id;
  60. uint32_t rsvd_start; /* reserved area: [rsvd_start, 0xfcc] */
  61. } PL061State;
  62. static const VMStateDescription vmstate_pl061 = {
  63. .name = "pl061",
  64. .version_id = 4,
  65. .minimum_version_id = 4,
  66. .fields = (VMStateField[]) {
  67. VMSTATE_UINT32(locked, PL061State),
  68. VMSTATE_UINT32(data, PL061State),
  69. VMSTATE_UINT32(old_out_data, PL061State),
  70. VMSTATE_UINT32(old_in_data, PL061State),
  71. VMSTATE_UINT32(dir, PL061State),
  72. VMSTATE_UINT32(isense, PL061State),
  73. VMSTATE_UINT32(ibe, PL061State),
  74. VMSTATE_UINT32(iev, PL061State),
  75. VMSTATE_UINT32(im, PL061State),
  76. VMSTATE_UINT32(istate, PL061State),
  77. VMSTATE_UINT32(afsel, PL061State),
  78. VMSTATE_UINT32(dr2r, PL061State),
  79. VMSTATE_UINT32(dr4r, PL061State),
  80. VMSTATE_UINT32(dr8r, PL061State),
  81. VMSTATE_UINT32(odr, PL061State),
  82. VMSTATE_UINT32(pur, PL061State),
  83. VMSTATE_UINT32(pdr, PL061State),
  84. VMSTATE_UINT32(slr, PL061State),
  85. VMSTATE_UINT32(den, PL061State),
  86. VMSTATE_UINT32(cr, PL061State),
  87. VMSTATE_UINT32_V(amsel, PL061State, 2),
  88. VMSTATE_END_OF_LIST()
  89. }
  90. };
  91. static void pl061_update(PL061State *s)
  92. {
  93. uint8_t changed;
  94. uint8_t mask;
  95. uint8_t out;
  96. int i;
  97. DPRINTF("dir = %d, data = %d\n", s->dir, s->data);
  98. /* Outputs float high. */
  99. /* FIXME: This is board dependent. */
  100. out = (s->data & s->dir) | ~s->dir;
  101. changed = s->old_out_data ^ out;
  102. if (changed) {
  103. s->old_out_data = out;
  104. for (i = 0; i < 8; i++) {
  105. mask = 1 << i;
  106. if (changed & mask) {
  107. DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
  108. qemu_set_irq(s->out[i], (out & mask) != 0);
  109. }
  110. }
  111. }
  112. /* Inputs */
  113. changed = (s->old_in_data ^ s->data) & ~s->dir;
  114. if (changed) {
  115. s->old_in_data = s->data;
  116. for (i = 0; i < 8; i++) {
  117. mask = 1 << i;
  118. if (changed & mask) {
  119. DPRINTF("Changed input %d = %d\n", i, (s->data & mask) != 0);
  120. if (!(s->isense & mask)) {
  121. /* Edge interrupt */
  122. if (s->ibe & mask) {
  123. /* Any edge triggers the interrupt */
  124. s->istate |= mask;
  125. } else {
  126. /* Edge is selected by IEV */
  127. s->istate |= ~(s->data ^ s->iev) & mask;
  128. }
  129. }
  130. }
  131. }
  132. }
  133. /* Level interrupt */
  134. s->istate |= ~(s->data ^ s->iev) & s->isense;
  135. DPRINTF("istate = %02X\n", s->istate);
  136. qemu_set_irq(s->irq, (s->istate & s->im) != 0);
  137. }
  138. static uint64_t pl061_read(void *opaque, hwaddr offset,
  139. unsigned size)
  140. {
  141. PL061State *s = (PL061State *)opaque;
  142. if (offset < 0x400) {
  143. return s->data & (offset >> 2);
  144. }
  145. if (offset >= s->rsvd_start && offset <= 0xfcc) {
  146. goto err_out;
  147. }
  148. if (offset >= 0xfd0 && offset < 0x1000) {
  149. return s->id[(offset - 0xfd0) >> 2];
  150. }
  151. switch (offset) {
  152. case 0x400: /* Direction */
  153. return s->dir;
  154. case 0x404: /* Interrupt sense */
  155. return s->isense;
  156. case 0x408: /* Interrupt both edges */
  157. return s->ibe;
  158. case 0x40c: /* Interrupt event */
  159. return s->iev;
  160. case 0x410: /* Interrupt mask */
  161. return s->im;
  162. case 0x414: /* Raw interrupt status */
  163. return s->istate;
  164. case 0x418: /* Masked interrupt status */
  165. return s->istate & s->im;
  166. case 0x420: /* Alternate function select */
  167. return s->afsel;
  168. case 0x500: /* 2mA drive */
  169. return s->dr2r;
  170. case 0x504: /* 4mA drive */
  171. return s->dr4r;
  172. case 0x508: /* 8mA drive */
  173. return s->dr8r;
  174. case 0x50c: /* Open drain */
  175. return s->odr;
  176. case 0x510: /* Pull-up */
  177. return s->pur;
  178. case 0x514: /* Pull-down */
  179. return s->pdr;
  180. case 0x518: /* Slew rate control */
  181. return s->slr;
  182. case 0x51c: /* Digital enable */
  183. return s->den;
  184. case 0x520: /* Lock */
  185. return s->locked;
  186. case 0x524: /* Commit */
  187. return s->cr;
  188. case 0x528: /* Analog mode select */
  189. return s->amsel;
  190. default:
  191. break;
  192. }
  193. err_out:
  194. qemu_log_mask(LOG_GUEST_ERROR,
  195. "pl061_read: Bad offset %x\n", (int)offset);
  196. return 0;
  197. }
  198. static void pl061_write(void *opaque, hwaddr offset,
  199. uint64_t value, unsigned size)
  200. {
  201. PL061State *s = (PL061State *)opaque;
  202. uint8_t mask;
  203. if (offset < 0x400) {
  204. mask = (offset >> 2) & s->dir;
  205. s->data = (s->data & ~mask) | (value & mask);
  206. pl061_update(s);
  207. return;
  208. }
  209. if (offset >= s->rsvd_start) {
  210. goto err_out;
  211. }
  212. switch (offset) {
  213. case 0x400: /* Direction */
  214. s->dir = value & 0xff;
  215. break;
  216. case 0x404: /* Interrupt sense */
  217. s->isense = value & 0xff;
  218. break;
  219. case 0x408: /* Interrupt both edges */
  220. s->ibe = value & 0xff;
  221. break;
  222. case 0x40c: /* Interrupt event */
  223. s->iev = value & 0xff;
  224. break;
  225. case 0x410: /* Interrupt mask */
  226. s->im = value & 0xff;
  227. break;
  228. case 0x41c: /* Interrupt clear */
  229. s->istate &= ~value;
  230. break;
  231. case 0x420: /* Alternate function select */
  232. mask = s->cr;
  233. s->afsel = (s->afsel & ~mask) | (value & mask);
  234. break;
  235. case 0x500: /* 2mA drive */
  236. s->dr2r = value & 0xff;
  237. break;
  238. case 0x504: /* 4mA drive */
  239. s->dr4r = value & 0xff;
  240. break;
  241. case 0x508: /* 8mA drive */
  242. s->dr8r = value & 0xff;
  243. break;
  244. case 0x50c: /* Open drain */
  245. s->odr = value & 0xff;
  246. break;
  247. case 0x510: /* Pull-up */
  248. s->pur = value & 0xff;
  249. break;
  250. case 0x514: /* Pull-down */
  251. s->pdr = value & 0xff;
  252. break;
  253. case 0x518: /* Slew rate control */
  254. s->slr = value & 0xff;
  255. break;
  256. case 0x51c: /* Digital enable */
  257. s->den = value & 0xff;
  258. break;
  259. case 0x520: /* Lock */
  260. s->locked = (value != 0xacce551);
  261. break;
  262. case 0x524: /* Commit */
  263. if (!s->locked)
  264. s->cr = value & 0xff;
  265. break;
  266. case 0x528:
  267. s->amsel = value & 0xff;
  268. break;
  269. default:
  270. goto err_out;
  271. }
  272. pl061_update(s);
  273. return;
  274. err_out:
  275. qemu_log_mask(LOG_GUEST_ERROR,
  276. "pl061_write: Bad offset %x\n", (int)offset);
  277. }
  278. static void pl061_reset(DeviceState *dev)
  279. {
  280. PL061State *s = PL061(dev);
  281. /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */
  282. s->data = 0;
  283. s->old_out_data = 0;
  284. s->old_in_data = 0;
  285. s->dir = 0;
  286. s->isense = 0;
  287. s->ibe = 0;
  288. s->iev = 0;
  289. s->im = 0;
  290. s->istate = 0;
  291. s->afsel = 0;
  292. s->dr2r = 0xff;
  293. s->dr4r = 0;
  294. s->dr8r = 0;
  295. s->odr = 0;
  296. s->pur = 0;
  297. s->pdr = 0;
  298. s->slr = 0;
  299. s->den = 0;
  300. s->locked = 1;
  301. s->cr = 0xff;
  302. s->amsel = 0;
  303. }
  304. static void pl061_set_irq(void * opaque, int irq, int level)
  305. {
  306. PL061State *s = (PL061State *)opaque;
  307. uint8_t mask;
  308. mask = 1 << irq;
  309. if ((s->dir & mask) == 0) {
  310. s->data &= ~mask;
  311. if (level)
  312. s->data |= mask;
  313. pl061_update(s);
  314. }
  315. }
  316. static const MemoryRegionOps pl061_ops = {
  317. .read = pl061_read,
  318. .write = pl061_write,
  319. .endianness = DEVICE_NATIVE_ENDIAN,
  320. };
  321. static void pl061_luminary_init(Object *obj)
  322. {
  323. PL061State *s = PL061(obj);
  324. s->id = pl061_id_luminary;
  325. s->rsvd_start = 0x52c;
  326. }
  327. static void pl061_init(Object *obj)
  328. {
  329. PL061State *s = PL061(obj);
  330. DeviceState *dev = DEVICE(obj);
  331. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  332. s->id = pl061_id;
  333. s->rsvd_start = 0x424;
  334. memory_region_init_io(&s->iomem, obj, &pl061_ops, s, "pl061", 0x1000);
  335. sysbus_init_mmio(sbd, &s->iomem);
  336. sysbus_init_irq(sbd, &s->irq);
  337. qdev_init_gpio_in(dev, pl061_set_irq, 8);
  338. qdev_init_gpio_out(dev, s->out, 8);
  339. }
  340. static void pl061_class_init(ObjectClass *klass, void *data)
  341. {
  342. DeviceClass *dc = DEVICE_CLASS(klass);
  343. dc->vmsd = &vmstate_pl061;
  344. dc->reset = &pl061_reset;
  345. }
  346. static const TypeInfo pl061_info = {
  347. .name = TYPE_PL061,
  348. .parent = TYPE_SYS_BUS_DEVICE,
  349. .instance_size = sizeof(PL061State),
  350. .instance_init = pl061_init,
  351. .class_init = pl061_class_init,
  352. };
  353. static const TypeInfo pl061_luminary_info = {
  354. .name = "pl061_luminary",
  355. .parent = TYPE_PL061,
  356. .instance_init = pl061_luminary_init,
  357. };
  358. static void pl061_register_types(void)
  359. {
  360. type_register_static(&pl061_info);
  361. type_register_static(&pl061_luminary_info);
  362. }
  363. type_init(pl061_register_types)