pl050.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/ps2.h>
  19. #include <grub/fdtbus.h>
  20. #include <grub/err.h>
  21. #include <grub/machine/kernel.h>
  22. #include <grub/at_keyboard.h>
  23. #include <grub/misc.h>
  24. #include <grub/term.h>
  25. #include <grub/time.h>
  26. #include <grub/ps2.h>
  27. #include <grub/fdtbus.h>
  28. static volatile grub_uint32_t *pl050_regs;
  29. static struct grub_ps2_state ps2_state;
  30. static void
  31. keyboard_controller_wait_until_ready (void)
  32. {
  33. while (! (pl050_regs[1] & 0x40));
  34. }
  35. static grub_uint8_t
  36. wait_ack (void)
  37. {
  38. grub_uint64_t endtime;
  39. grub_uint8_t ack;
  40. endtime = grub_get_time_ms () + 20;
  41. do
  42. ack = pl050_regs[2];
  43. while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK
  44. && grub_get_time_ms () < endtime);
  45. return ack;
  46. }
  47. static int
  48. write_mode (int mode)
  49. {
  50. unsigned i;
  51. for (i = 0; i < GRUB_AT_TRIES; i++)
  52. {
  53. grub_uint8_t ack;
  54. keyboard_controller_wait_until_ready ();
  55. pl050_regs[2] = 0xf0;
  56. keyboard_controller_wait_until_ready ();
  57. pl050_regs[2] = mode;
  58. keyboard_controller_wait_until_ready ();
  59. ack = wait_ack ();
  60. if (ack == GRUB_AT_NACK)
  61. continue;
  62. if (ack == GRUB_AT_ACK)
  63. break;
  64. return 0;
  65. }
  66. return (i != GRUB_AT_TRIES);
  67. }
  68. static int
  69. query_mode (void)
  70. {
  71. grub_uint8_t ret;
  72. int e;
  73. e = write_mode (0);
  74. if (!e)
  75. return 0;
  76. keyboard_controller_wait_until_ready ();
  77. do
  78. ret = pl050_regs[2];
  79. while (ret == GRUB_AT_ACK);
  80. /* QEMU translates the set even in no-translate mode. */
  81. if (ret == 0x43 || ret == 1)
  82. return 1;
  83. if (ret == 0x41 || ret == 2)
  84. return 2;
  85. if (ret == 0x3f || ret == 3)
  86. return 3;
  87. return 0;
  88. }
  89. static void
  90. set_scancodes (void)
  91. {
  92. write_mode (2);
  93. ps2_state.current_set = query_mode ();
  94. grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
  95. if (ps2_state.current_set == 2)
  96. return;
  97. write_mode (1);
  98. ps2_state.current_set = query_mode ();
  99. grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
  100. if (ps2_state.current_set == 1)
  101. return;
  102. grub_dprintf ("atkeyb", "no supported scancode set found\n");
  103. }
  104. static void
  105. keyboard_controller_led (grub_uint8_t leds)
  106. {
  107. keyboard_controller_wait_until_ready ();
  108. pl050_regs[2] = 0xed;
  109. keyboard_controller_wait_until_ready ();
  110. pl050_regs[2] = leds & 0x7;
  111. }
  112. /* If there is a character pending, return it;
  113. otherwise return GRUB_TERM_NO_KEY. */
  114. static int
  115. grub_pl050_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
  116. {
  117. grub_uint8_t at_key;
  118. int ret;
  119. grub_uint8_t old_led;
  120. if (!(pl050_regs[1] & 0x10))
  121. return -1;
  122. at_key = pl050_regs[2];
  123. old_led = ps2_state.led_status;
  124. ret = grub_ps2_process_incoming_byte (&ps2_state, at_key);
  125. if (old_led != ps2_state.led_status)
  126. keyboard_controller_led (ps2_state.led_status);
  127. return ret;
  128. }
  129. static struct grub_term_input grub_pl050_keyboard_term =
  130. {
  131. .name = "pl050_keyboard",
  132. .getkey = grub_pl050_keyboard_getkey
  133. };
  134. static grub_err_t
  135. pl050_attach(const struct grub_fdtbus_dev *dev)
  136. {
  137. const grub_uint32_t *reg;
  138. reg = grub_fdtbus_get_prop (dev, "reg", 0);
  139. /* Mouse. Nothing to do. */
  140. if (grub_be_to_cpu32 (*reg) == 0x7000)
  141. return 0;
  142. pl050_regs = grub_fdtbus_map_reg (dev, 0, 0);
  143. if (!grub_fdtbus_is_mapping_valid (pl050_regs))
  144. return grub_error (GRUB_ERR_IO, "could not map pl050");
  145. ps2_state.at_keyboard_status = 0;
  146. set_scancodes ();
  147. keyboard_controller_led (ps2_state.led_status);
  148. grub_term_register_input ("pl050_keyboard", &grub_pl050_keyboard_term);
  149. return GRUB_ERR_NONE;
  150. }
  151. struct grub_fdtbus_driver pl050 =
  152. {
  153. .compatible = "arm,pl050",
  154. .attach = pl050_attach
  155. };
  156. void
  157. grub_pl050_init (void)
  158. {
  159. grub_fdtbus_register (&pl050);
  160. }