diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index 1f55d12bb..a8b536d5f 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -241,6 +241,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/system.h endif if COND_arm_coreboot +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/keyboard_layouts.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/system.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video_fb.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 0f198fab8..8b549627a 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -161,6 +161,9 @@ kernel = { arm_coreboot = kern/arm/coreboot/coreboot.S; arm_coreboot = lib/fdt.c; arm_coreboot = bus/fdt.c; + arm_coreboot = term/ps2.c; + arm_coreboot = term/arm/pl050.c; + arm_coreboot = commands/keylayouts.c; terminfoinkernel = term/terminfo.c; terminfoinkernel = term/tparm.c; diff --git a/grub-core/kern/arm/coreboot/init.c b/grub-core/kern/arm/coreboot/init.c index aec75c672..a06ccb72f 100644 --- a/grub-core/kern/arm/coreboot/init.c +++ b/grub-core/kern/arm/coreboot/init.c @@ -133,6 +133,7 @@ grub_machine_init (void) grub_fdtbus_init (dtb, dtb_size); grub_machine_timer_init (); + grub_pl050_init (); } void diff --git a/grub-core/term/arm/pl050.c b/grub-core/term/arm/pl050.c new file mode 100644 index 000000000..e4cda3056 --- /dev/null +++ b/grub-core/term/arm/pl050.c @@ -0,0 +1,189 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static volatile grub_uint32_t *pl050_regs; + +struct grub_ps2_state ps2_state; + +static void +keyboard_controller_wait_until_ready (void) +{ + while (! (pl050_regs[1] & 0x40)); +} + +static grub_uint8_t +wait_ack (void) +{ + grub_uint64_t endtime; + grub_uint8_t ack; + + endtime = grub_get_time_ms () + 20; + do + ack = pl050_regs[2]; + while (ack != GRUB_AT_ACK && ack != GRUB_AT_NACK + && grub_get_time_ms () < endtime); + return ack; +} + + +static int +write_mode (int mode) +{ + unsigned i; + for (i = 0; i < GRUB_AT_TRIES; i++) + { + grub_uint8_t ack; + keyboard_controller_wait_until_ready (); + pl050_regs[2] = 0xf0; + keyboard_controller_wait_until_ready (); + pl050_regs[2] = mode; + keyboard_controller_wait_until_ready (); + ack = wait_ack (); + if (ack == GRUB_AT_NACK) + continue; + if (ack == GRUB_AT_ACK) + break; + return 0; + } + + return (i != GRUB_AT_TRIES); +} + +static int +query_mode (void) +{ + grub_uint8_t ret; + int e; + + e = write_mode (0); + if (!e) + return 0; + + keyboard_controller_wait_until_ready (); + + do + ret = pl050_regs[2]; + while (ret == GRUB_AT_ACK); + + /* QEMU translates the set even in no-translate mode. */ + if (ret == 0x43 || ret == 1) + return 1; + if (ret == 0x41 || ret == 2) + return 2; + if (ret == 0x3f || ret == 3) + return 3; + return 0; +} + +static void +set_scancodes (void) +{ + write_mode (2); + ps2_state.current_set = query_mode (); + grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); + if (ps2_state.current_set == 2) + return; + + write_mode (1); + ps2_state.current_set = query_mode (); + grub_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set); + if (ps2_state.current_set == 1) + return; + grub_dprintf ("atkeyb", "no supported scancode set found\n"); +} + +static void +keyboard_controller_led (grub_uint8_t leds) +{ + keyboard_controller_wait_until_ready (); + pl050_regs[2] = 0xed; + keyboard_controller_wait_until_ready (); + pl050_regs[2] = leds & 0x7; +} + +/* If there is a character pending, return it; + otherwise return GRUB_TERM_NO_KEY. */ +static int +grub_pl050_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused))) +{ + grub_uint8_t at_key; + int ret; + grub_uint8_t old_led; + + if (!(pl050_regs[1] & 0x10)) + return -1; + at_key = pl050_regs[2]; + old_led = ps2_state.led_status; + + ret = grub_ps2_process_incoming_byte (&ps2_state, at_key); + if (old_led != ps2_state.led_status) + keyboard_controller_led (ps2_state.led_status); + return ret; +} + +static struct grub_term_input grub_pl050_keyboard_term = + { + .name = "pl050_keyboard", + .getkey = grub_pl050_keyboard_getkey + }; + +static grub_err_t +pl050_attach(const struct grub_fdtbus_dev *dev) +{ + const grub_uint32_t *reg; + reg = grub_fdtbus_get_prop (dev, "reg", 0); + + /* Mouse. Nothing to do. */ + if (grub_be_to_cpu32 (*reg) == 0x7000) + return 0; + + pl050_regs = grub_fdtbus_map_reg (dev, 0, 0); + + if (!grub_fdtbus_is_mapping_valid (pl050_regs)) + return grub_error (GRUB_ERR_IO, "could not map pl050"); + + ps2_state.at_keyboard_status = 0; + set_scancodes (); + keyboard_controller_led (ps2_state.led_status); + + grub_term_register_input ("pl050_keyboard", &grub_pl050_keyboard_term); + return GRUB_ERR_NONE; +} + +struct grub_fdtbus_driver pl050 = +{ + .compatible = "arm,pl050", + .attach = pl050_attach +}; + +void +grub_pl050_init (void) +{ + grub_fdtbus_register (&pl050); +}