pl050.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. * Arm PrimeCell PL050 Keyboard / Mouse Interface
  3. *
  4. * Copyright (c) 2006-2007 CodeSourcery.
  5. * Written by Paul Brook
  6. *
  7. * This code is licenced under the GPL.
  8. */
  9. #include "hw.h"
  10. #include "primecell.h"
  11. #include "ps2.h"
  12. typedef struct {
  13. void *dev;
  14. uint32_t cr;
  15. uint32_t clk;
  16. uint32_t last;
  17. int pending;
  18. qemu_irq irq;
  19. int is_mouse;
  20. } pl050_state;
  21. #define PL050_TXEMPTY (1 << 6)
  22. #define PL050_TXBUSY (1 << 5)
  23. #define PL050_RXFULL (1 << 4)
  24. #define PL050_RXBUSY (1 << 3)
  25. #define PL050_RXPARITY (1 << 2)
  26. #define PL050_KMIC (1 << 1)
  27. #define PL050_KMID (1 << 0)
  28. static const unsigned char pl050_id[] =
  29. { 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  30. static void pl050_update(void *opaque, int level)
  31. {
  32. pl050_state *s = (pl050_state *)opaque;
  33. int raise;
  34. s->pending = level;
  35. raise = (s->pending && (s->cr & 0x10) != 0)
  36. || (s->cr & 0x08) != 0;
  37. qemu_set_irq(s->irq, raise);
  38. }
  39. static uint32_t pl050_read(void *opaque, target_phys_addr_t offset)
  40. {
  41. pl050_state *s = (pl050_state *)opaque;
  42. if (offset >= 0xfe0 && offset < 0x1000)
  43. return pl050_id[(offset - 0xfe0) >> 2];
  44. switch (offset >> 2) {
  45. case 0: /* KMICR */
  46. return s->cr;
  47. case 1: /* KMISTAT */
  48. {
  49. uint8_t val;
  50. uint32_t stat;
  51. val = s->last;
  52. val = val ^ (val >> 4);
  53. val = val ^ (val >> 2);
  54. val = (val ^ (val >> 1)) & 1;
  55. stat = PL050_TXEMPTY;
  56. if (val)
  57. stat |= PL050_RXPARITY;
  58. if (s->pending)
  59. stat |= PL050_RXFULL;
  60. return stat;
  61. }
  62. case 2: /* KMIDATA */
  63. if (s->pending)
  64. s->last = ps2_read_data(s->dev);
  65. return s->last;
  66. case 3: /* KMICLKDIV */
  67. return s->clk;
  68. case 4: /* KMIIR */
  69. return s->pending | 2;
  70. default:
  71. cpu_abort (cpu_single_env, "pl050_read: Bad offset %x\n", (int)offset);
  72. return 0;
  73. }
  74. }
  75. static void pl050_write(void *opaque, target_phys_addr_t offset,
  76. uint32_t value)
  77. {
  78. pl050_state *s = (pl050_state *)opaque;
  79. switch (offset >> 2) {
  80. case 0: /* KMICR */
  81. s->cr = value;
  82. pl050_update(s, s->pending);
  83. /* ??? Need to implement the enable/disable bit. */
  84. break;
  85. case 2: /* KMIDATA */
  86. /* ??? This should toggle the TX interrupt line. */
  87. /* ??? This means kbd/mouse can block each other. */
  88. if (s->is_mouse) {
  89. ps2_write_mouse(s->dev, value);
  90. } else {
  91. ps2_write_keyboard(s->dev, value);
  92. }
  93. break;
  94. case 3: /* KMICLKDIV */
  95. s->clk = value;
  96. return;
  97. default:
  98. cpu_abort (cpu_single_env, "pl050_write: Bad offset %x\n", (int)offset);
  99. }
  100. }
  101. static CPUReadMemoryFunc *pl050_readfn[] = {
  102. pl050_read,
  103. pl050_read,
  104. pl050_read
  105. };
  106. static CPUWriteMemoryFunc *pl050_writefn[] = {
  107. pl050_write,
  108. pl050_write,
  109. pl050_write
  110. };
  111. void pl050_init(uint32_t base, qemu_irq irq, int is_mouse)
  112. {
  113. int iomemtype;
  114. pl050_state *s;
  115. s = (pl050_state *)qemu_mallocz(sizeof(pl050_state));
  116. iomemtype = cpu_register_io_memory(0, pl050_readfn,
  117. pl050_writefn, s);
  118. cpu_register_physical_memory(base, 0x00001000, iomemtype);
  119. s->irq = irq;
  120. s->is_mouse = is_mouse;
  121. if (is_mouse)
  122. s->dev = ps2_mouse_init(pl050_update, s);
  123. else
  124. s->dev = ps2_kbd_init(pl050_update, s);
  125. /* ??? Save/restore. */
  126. }