pl061.c 10 KB

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