pl061.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  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. * QEMU interface:
  11. * + sysbus MMIO region 0: the device registers
  12. * + sysbus IRQ: the GPIOINTR interrupt line
  13. * + unnamed GPIO inputs 0..7: inputs to connect to the emulated GPIO lines
  14. * + unnamed GPIO outputs 0..7: the emulated GPIO lines, considered as
  15. * outputs
  16. * + QOM property "pullups": an integer defining whether non-floating lines
  17. * configured as inputs should be pulled up to logical 1 (ie whether in
  18. * real hardware they have a pullup resistor on the line out of the PL061).
  19. * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should
  20. * be pulled high, bit 1 configures line 1, and so on. The default is 0xff,
  21. * indicating that all GPIO lines are pulled up to logical 1.
  22. * + QOM property "pulldowns": an integer defining whether non-floating lines
  23. * configured as inputs should be pulled down to logical 0 (ie whether in
  24. * real hardware they have a pulldown resistor on the line out of the PL061).
  25. * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should
  26. * be pulled low, bit 1 configures line 1, and so on. The default is 0x0.
  27. * It is an error to set a bit in both "pullups" and "pulldowns". If a bit
  28. * is 0 in both, then the line is considered to be floating, and it will
  29. * not have qemu_set_irq() called on it when it is configured as an input.
  30. */
  31. #include "qemu/osdep.h"
  32. #include "hw/irq.h"
  33. #include "hw/sysbus.h"
  34. #include "hw/qdev-properties.h"
  35. #include "migration/vmstate.h"
  36. #include "qapi/error.h"
  37. #include "qemu/log.h"
  38. #include "qemu/module.h"
  39. #include "qom/object.h"
  40. #include "trace.h"
  41. static const uint8_t pl061_id[12] =
  42. { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  43. static const uint8_t pl061_id_luminary[12] =
  44. { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
  45. #define TYPE_PL061 "pl061"
  46. OBJECT_DECLARE_SIMPLE_TYPE(PL061State, PL061)
  47. #define N_GPIOS 8
  48. struct PL061State {
  49. SysBusDevice parent_obj;
  50. MemoryRegion iomem;
  51. uint32_t locked;
  52. uint32_t data;
  53. uint32_t old_out_data;
  54. uint32_t old_in_data;
  55. uint32_t dir;
  56. uint32_t isense;
  57. uint32_t ibe;
  58. uint32_t iev;
  59. uint32_t im;
  60. uint32_t istate;
  61. uint32_t afsel;
  62. uint32_t dr2r;
  63. uint32_t dr4r;
  64. uint32_t dr8r;
  65. uint32_t odr;
  66. uint32_t pur;
  67. uint32_t pdr;
  68. uint32_t slr;
  69. uint32_t den;
  70. uint32_t cr;
  71. uint32_t amsel;
  72. qemu_irq irq;
  73. qemu_irq out[N_GPIOS];
  74. const unsigned char *id;
  75. /* Properties, for non-Luminary PL061 */
  76. uint32_t pullups;
  77. uint32_t pulldowns;
  78. };
  79. static const VMStateDescription vmstate_pl061 = {
  80. .name = "pl061",
  81. .version_id = 4,
  82. .minimum_version_id = 4,
  83. .fields = (const VMStateField[]) {
  84. VMSTATE_UINT32(locked, PL061State),
  85. VMSTATE_UINT32(data, PL061State),
  86. VMSTATE_UINT32(old_out_data, PL061State),
  87. VMSTATE_UINT32(old_in_data, PL061State),
  88. VMSTATE_UINT32(dir, PL061State),
  89. VMSTATE_UINT32(isense, PL061State),
  90. VMSTATE_UINT32(ibe, PL061State),
  91. VMSTATE_UINT32(iev, PL061State),
  92. VMSTATE_UINT32(im, PL061State),
  93. VMSTATE_UINT32(istate, PL061State),
  94. VMSTATE_UINT32(afsel, PL061State),
  95. VMSTATE_UINT32(dr2r, PL061State),
  96. VMSTATE_UINT32(dr4r, PL061State),
  97. VMSTATE_UINT32(dr8r, PL061State),
  98. VMSTATE_UINT32(odr, PL061State),
  99. VMSTATE_UINT32(pur, PL061State),
  100. VMSTATE_UINT32(pdr, PL061State),
  101. VMSTATE_UINT32(slr, PL061State),
  102. VMSTATE_UINT32(den, PL061State),
  103. VMSTATE_UINT32(cr, PL061State),
  104. VMSTATE_UINT32_V(amsel, PL061State, 2),
  105. VMSTATE_END_OF_LIST()
  106. }
  107. };
  108. static uint8_t pl061_floating(PL061State *s)
  109. {
  110. /*
  111. * Return mask of bits which correspond to pins configured as inputs
  112. * and which are floating (neither pulled up to 1 nor down to 0).
  113. */
  114. uint8_t floating;
  115. if (s->id == pl061_id_luminary) {
  116. /*
  117. * If both PUR and PDR bits are clear, there is neither a pullup
  118. * nor a pulldown in place, and the output truly floats.
  119. */
  120. floating = ~(s->pur | s->pdr);
  121. } else {
  122. floating = ~(s->pullups | s->pulldowns);
  123. }
  124. return floating & ~s->dir;
  125. }
  126. static uint8_t pl061_pullups(PL061State *s)
  127. {
  128. /*
  129. * Return mask of bits which correspond to pins configured as inputs
  130. * and which are pulled up to 1.
  131. */
  132. uint8_t pullups;
  133. if (s->id == pl061_id_luminary) {
  134. /*
  135. * The Luminary variant of the PL061 has an extra registers which
  136. * the guest can use to configure whether lines should be pullup
  137. * or pulldown.
  138. */
  139. pullups = s->pur;
  140. } else {
  141. pullups = s->pullups;
  142. }
  143. return pullups & ~s->dir;
  144. }
  145. static void pl061_update(PL061State *s)
  146. {
  147. uint8_t changed;
  148. uint8_t mask;
  149. uint8_t out;
  150. int i;
  151. uint8_t pullups = pl061_pullups(s);
  152. uint8_t floating = pl061_floating(s);
  153. trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data,
  154. pullups, floating);
  155. /*
  156. * Pins configured as output are driven from the data register;
  157. * otherwise if they're pulled up they're 1, and if they're floating
  158. * then we give them the same value they had previously, so we don't
  159. * report any change to the other end.
  160. */
  161. out = (s->data & s->dir) | pullups | (s->old_out_data & floating);
  162. changed = s->old_out_data ^ out;
  163. if (changed) {
  164. s->old_out_data = out;
  165. for (i = 0; i < N_GPIOS; i++) {
  166. mask = 1 << i;
  167. if (changed & mask) {
  168. int level = (out & mask) != 0;
  169. trace_pl061_set_output(DEVICE(s)->canonical_path, i, level);
  170. qemu_set_irq(s->out[i], level);
  171. }
  172. }
  173. }
  174. /* Inputs */
  175. changed = (s->old_in_data ^ s->data) & ~s->dir;
  176. if (changed) {
  177. s->old_in_data = s->data;
  178. for (i = 0; i < N_GPIOS; i++) {
  179. mask = 1 << i;
  180. if (changed & mask) {
  181. trace_pl061_input_change(DEVICE(s)->canonical_path, i,
  182. (s->data & mask) != 0);
  183. if (!(s->isense & mask)) {
  184. /* Edge interrupt */
  185. if (s->ibe & mask) {
  186. /* Any edge triggers the interrupt */
  187. s->istate |= mask;
  188. } else {
  189. /* Edge is selected by IEV */
  190. s->istate |= ~(s->data ^ s->iev) & mask;
  191. }
  192. }
  193. }
  194. }
  195. }
  196. /* Level interrupt */
  197. s->istate |= ~(s->data ^ s->iev) & s->isense;
  198. trace_pl061_update_istate(DEVICE(s)->canonical_path,
  199. s->istate, s->im, (s->istate & s->im) != 0);
  200. qemu_set_irq(s->irq, (s->istate & s->im) != 0);
  201. }
  202. static uint64_t pl061_read(void *opaque, hwaddr offset,
  203. unsigned size)
  204. {
  205. PL061State *s = (PL061State *)opaque;
  206. uint64_t r = 0;
  207. switch (offset) {
  208. case 0x0 ... 0x3ff: /* Data */
  209. r = s->data & (offset >> 2);
  210. break;
  211. case 0x400: /* Direction */
  212. r = s->dir;
  213. break;
  214. case 0x404: /* Interrupt sense */
  215. r = s->isense;
  216. break;
  217. case 0x408: /* Interrupt both edges */
  218. r = s->ibe;
  219. break;
  220. case 0x40c: /* Interrupt event */
  221. r = s->iev;
  222. break;
  223. case 0x410: /* Interrupt mask */
  224. r = s->im;
  225. break;
  226. case 0x414: /* Raw interrupt status */
  227. r = s->istate;
  228. break;
  229. case 0x418: /* Masked interrupt status */
  230. r = s->istate & s->im;
  231. break;
  232. case 0x420: /* Alternate function select */
  233. r = s->afsel;
  234. break;
  235. case 0x500: /* 2mA drive */
  236. if (s->id != pl061_id_luminary) {
  237. goto bad_offset;
  238. }
  239. r = s->dr2r;
  240. break;
  241. case 0x504: /* 4mA drive */
  242. if (s->id != pl061_id_luminary) {
  243. goto bad_offset;
  244. }
  245. r = s->dr4r;
  246. break;
  247. case 0x508: /* 8mA drive */
  248. if (s->id != pl061_id_luminary) {
  249. goto bad_offset;
  250. }
  251. r = s->dr8r;
  252. break;
  253. case 0x50c: /* Open drain */
  254. if (s->id != pl061_id_luminary) {
  255. goto bad_offset;
  256. }
  257. r = s->odr;
  258. break;
  259. case 0x510: /* Pull-up */
  260. if (s->id != pl061_id_luminary) {
  261. goto bad_offset;
  262. }
  263. r = s->pur;
  264. break;
  265. case 0x514: /* Pull-down */
  266. if (s->id != pl061_id_luminary) {
  267. goto bad_offset;
  268. }
  269. r = s->pdr;
  270. break;
  271. case 0x518: /* Slew rate control */
  272. if (s->id != pl061_id_luminary) {
  273. goto bad_offset;
  274. }
  275. r = s->slr;
  276. break;
  277. case 0x51c: /* Digital enable */
  278. if (s->id != pl061_id_luminary) {
  279. goto bad_offset;
  280. }
  281. r = s->den;
  282. break;
  283. case 0x520: /* Lock */
  284. if (s->id != pl061_id_luminary) {
  285. goto bad_offset;
  286. }
  287. r = s->locked;
  288. break;
  289. case 0x524: /* Commit */
  290. if (s->id != pl061_id_luminary) {
  291. goto bad_offset;
  292. }
  293. r = s->cr;
  294. break;
  295. case 0x528: /* Analog mode select */
  296. if (s->id != pl061_id_luminary) {
  297. goto bad_offset;
  298. }
  299. r = s->amsel;
  300. break;
  301. case 0xfd0 ... 0xfff: /* ID registers */
  302. r = s->id[(offset - 0xfd0) >> 2];
  303. break;
  304. default:
  305. bad_offset:
  306. qemu_log_mask(LOG_GUEST_ERROR,
  307. "pl061_read: Bad offset %x\n", (int)offset);
  308. break;
  309. }
  310. trace_pl061_read(DEVICE(s)->canonical_path, offset, r);
  311. return r;
  312. }
  313. static void pl061_write(void *opaque, hwaddr offset,
  314. uint64_t value, unsigned size)
  315. {
  316. PL061State *s = (PL061State *)opaque;
  317. uint8_t mask;
  318. trace_pl061_write(DEVICE(s)->canonical_path, offset, value);
  319. switch (offset) {
  320. case 0 ... 0x3ff:
  321. mask = (offset >> 2) & s->dir;
  322. s->data = (s->data & ~mask) | (value & mask);
  323. pl061_update(s);
  324. return;
  325. case 0x400: /* Direction */
  326. s->dir = value & 0xff;
  327. break;
  328. case 0x404: /* Interrupt sense */
  329. s->isense = value & 0xff;
  330. break;
  331. case 0x408: /* Interrupt both edges */
  332. s->ibe = value & 0xff;
  333. break;
  334. case 0x40c: /* Interrupt event */
  335. s->iev = value & 0xff;
  336. break;
  337. case 0x410: /* Interrupt mask */
  338. s->im = value & 0xff;
  339. break;
  340. case 0x41c: /* Interrupt clear */
  341. s->istate &= ~value;
  342. break;
  343. case 0x420: /* Alternate function select */
  344. mask = s->cr;
  345. s->afsel = (s->afsel & ~mask) | (value & mask);
  346. break;
  347. case 0x500: /* 2mA drive */
  348. if (s->id != pl061_id_luminary) {
  349. goto bad_offset;
  350. }
  351. s->dr2r = value & 0xff;
  352. break;
  353. case 0x504: /* 4mA drive */
  354. if (s->id != pl061_id_luminary) {
  355. goto bad_offset;
  356. }
  357. s->dr4r = value & 0xff;
  358. break;
  359. case 0x508: /* 8mA drive */
  360. if (s->id != pl061_id_luminary) {
  361. goto bad_offset;
  362. }
  363. s->dr8r = value & 0xff;
  364. break;
  365. case 0x50c: /* Open drain */
  366. if (s->id != pl061_id_luminary) {
  367. goto bad_offset;
  368. }
  369. s->odr = value & 0xff;
  370. break;
  371. case 0x510: /* Pull-up */
  372. if (s->id != pl061_id_luminary) {
  373. goto bad_offset;
  374. }
  375. s->pur = value & 0xff;
  376. break;
  377. case 0x514: /* Pull-down */
  378. if (s->id != pl061_id_luminary) {
  379. goto bad_offset;
  380. }
  381. s->pdr = value & 0xff;
  382. break;
  383. case 0x518: /* Slew rate control */
  384. if (s->id != pl061_id_luminary) {
  385. goto bad_offset;
  386. }
  387. s->slr = value & 0xff;
  388. break;
  389. case 0x51c: /* Digital enable */
  390. if (s->id != pl061_id_luminary) {
  391. goto bad_offset;
  392. }
  393. s->den = value & 0xff;
  394. break;
  395. case 0x520: /* Lock */
  396. if (s->id != pl061_id_luminary) {
  397. goto bad_offset;
  398. }
  399. s->locked = (value != 0xacce551);
  400. break;
  401. case 0x524: /* Commit */
  402. if (s->id != pl061_id_luminary) {
  403. goto bad_offset;
  404. }
  405. if (!s->locked)
  406. s->cr = value & 0xff;
  407. break;
  408. case 0x528:
  409. if (s->id != pl061_id_luminary) {
  410. goto bad_offset;
  411. }
  412. s->amsel = value & 0xff;
  413. break;
  414. default:
  415. bad_offset:
  416. qemu_log_mask(LOG_GUEST_ERROR,
  417. "pl061_write: Bad offset %x\n", (int)offset);
  418. return;
  419. }
  420. pl061_update(s);
  421. return;
  422. }
  423. static void pl061_enter_reset(Object *obj, ResetType type)
  424. {
  425. PL061State *s = PL061(obj);
  426. trace_pl061_reset(DEVICE(s)->canonical_path);
  427. /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */
  428. /*
  429. * FIXME: For the LM3S6965, not all of the PL061 instances have the
  430. * same reset values for GPIOPUR, GPIOAFSEL and GPIODEN, so in theory
  431. * we should allow the board to configure these via properties.
  432. * In practice, we don't wire anything up to the affected GPIO lines
  433. * (PB7, PC0, PC1, PC2, PC3 -- they're used for JTAG), so we can
  434. * get away with this inaccuracy.
  435. */
  436. s->data = 0;
  437. s->old_in_data = 0;
  438. s->dir = 0;
  439. s->isense = 0;
  440. s->ibe = 0;
  441. s->iev = 0;
  442. s->im = 0;
  443. s->istate = 0;
  444. s->afsel = 0;
  445. s->dr2r = 0xff;
  446. s->dr4r = 0;
  447. s->dr8r = 0;
  448. s->odr = 0;
  449. s->pur = 0;
  450. s->pdr = 0;
  451. s->slr = 0;
  452. s->den = 0;
  453. s->locked = 1;
  454. s->cr = 0xff;
  455. s->amsel = 0;
  456. }
  457. static void pl061_hold_reset(Object *obj, ResetType type)
  458. {
  459. PL061State *s = PL061(obj);
  460. int i, level;
  461. uint8_t floating = pl061_floating(s);
  462. uint8_t pullups = pl061_pullups(s);
  463. for (i = 0; i < N_GPIOS; i++) {
  464. if (extract32(floating, i, 1)) {
  465. continue;
  466. }
  467. level = extract32(pullups, i, 1);
  468. trace_pl061_set_output(DEVICE(s)->canonical_path, i, level);
  469. qemu_set_irq(s->out[i], level);
  470. }
  471. s->old_out_data = pullups;
  472. }
  473. static void pl061_set_irq(void * opaque, int irq, int level)
  474. {
  475. PL061State *s = (PL061State *)opaque;
  476. uint8_t mask;
  477. mask = 1 << irq;
  478. if ((s->dir & mask) == 0) {
  479. s->data &= ~mask;
  480. if (level)
  481. s->data |= mask;
  482. pl061_update(s);
  483. }
  484. }
  485. static const MemoryRegionOps pl061_ops = {
  486. .read = pl061_read,
  487. .write = pl061_write,
  488. .endianness = DEVICE_NATIVE_ENDIAN,
  489. };
  490. static void pl061_luminary_init(Object *obj)
  491. {
  492. PL061State *s = PL061(obj);
  493. s->id = pl061_id_luminary;
  494. }
  495. static void pl061_init(Object *obj)
  496. {
  497. PL061State *s = PL061(obj);
  498. DeviceState *dev = DEVICE(obj);
  499. SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
  500. s->id = pl061_id;
  501. memory_region_init_io(&s->iomem, obj, &pl061_ops, s, "pl061", 0x1000);
  502. sysbus_init_mmio(sbd, &s->iomem);
  503. sysbus_init_irq(sbd, &s->irq);
  504. qdev_init_gpio_in(dev, pl061_set_irq, N_GPIOS);
  505. qdev_init_gpio_out(dev, s->out, N_GPIOS);
  506. }
  507. static void pl061_realize(DeviceState *dev, Error **errp)
  508. {
  509. PL061State *s = PL061(dev);
  510. if (s->pullups > 0xff) {
  511. error_setg(errp, "pullups property must be between 0 and 0xff");
  512. return;
  513. }
  514. if (s->pulldowns > 0xff) {
  515. error_setg(errp, "pulldowns property must be between 0 and 0xff");
  516. return;
  517. }
  518. if (s->pullups & s->pulldowns) {
  519. error_setg(errp, "no bit may be set both in pullups and pulldowns");
  520. return;
  521. }
  522. }
  523. static const Property pl061_props[] = {
  524. DEFINE_PROP_UINT32("pullups", PL061State, pullups, 0xff),
  525. DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0),
  526. };
  527. static void pl061_class_init(ObjectClass *klass, void *data)
  528. {
  529. DeviceClass *dc = DEVICE_CLASS(klass);
  530. ResettableClass *rc = RESETTABLE_CLASS(klass);
  531. dc->vmsd = &vmstate_pl061;
  532. dc->realize = pl061_realize;
  533. device_class_set_props(dc, pl061_props);
  534. rc->phases.enter = pl061_enter_reset;
  535. rc->phases.hold = pl061_hold_reset;
  536. }
  537. static const TypeInfo pl061_info = {
  538. .name = TYPE_PL061,
  539. .parent = TYPE_SYS_BUS_DEVICE,
  540. .instance_size = sizeof(PL061State),
  541. .instance_init = pl061_init,
  542. .class_init = pl061_class_init,
  543. };
  544. static const TypeInfo pl061_luminary_info = {
  545. .name = "pl061_luminary",
  546. .parent = TYPE_PL061,
  547. .instance_init = pl061_luminary_init,
  548. };
  549. static void pl061_register_types(void)
  550. {
  551. type_register_static(&pl061_info);
  552. type_register_static(&pl061_luminary_info);
  553. }
  554. type_init(pl061_register_types)