pl061.c 10 KB

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