vaseboot/VasEBoot-core/term/arm/pl050.c

190 lines
4.5 KiB
C

/*
* VAS_EBOOT -- GRand Unified Bootloader
* Copyright (C) 2007,2008,2009 Free Software Foundation, Inc.
*
* VAS_EBOOT 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.
*
* VAS_EBOOT 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 VAS_EBOOT. If not, see <http://www.gnu.org/licenses/>.
*/
#include <VasEBoot/ps2.h>
#include <VasEBoot/fdtbus.h>
#include <VasEBoot/err.h>
#include <VasEBoot/machine/kernel.h>
#include <VasEBoot/at_keyboard.h>
#include <VasEBoot/misc.h>
#include <VasEBoot/term.h>
#include <VasEBoot/time.h>
#include <VasEBoot/ps2.h>
#include <VasEBoot/fdtbus.h>
static volatile VasEBoot_uint32_t *pl050_regs;
static struct VasEBoot_ps2_state ps2_state;
static void
keyboard_controller_wait_until_ready (void)
{
while (! (pl050_regs[1] & 0x40));
}
static VasEBoot_uint8_t
wait_ack (void)
{
VasEBoot_uint64_t endtime;
VasEBoot_uint8_t ack;
endtime = VasEBoot_get_time_ms () + 20;
do
ack = pl050_regs[2];
while (ack != VAS_EBOOT_AT_ACK && ack != VAS_EBOOT_AT_NACK
&& VasEBoot_get_time_ms () < endtime);
return ack;
}
static int
write_mode (int mode)
{
unsigned i;
for (i = 0; i < VAS_EBOOT_AT_TRIES; i++)
{
VasEBoot_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 == VAS_EBOOT_AT_NACK)
continue;
if (ack == VAS_EBOOT_AT_ACK)
break;
return 0;
}
return (i != VAS_EBOOT_AT_TRIES);
}
static int
query_mode (void)
{
VasEBoot_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 == VAS_EBOOT_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 ();
VasEBoot_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 ();
VasEBoot_dprintf ("atkeyb", "returned set %d\n", ps2_state.current_set);
if (ps2_state.current_set == 1)
return;
VasEBoot_dprintf ("atkeyb", "no supported scancode set found\n");
}
static void
keyboard_controller_led (VasEBoot_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 VAS_EBOOT_TERM_NO_KEY. */
static int
VasEBoot_pl050_keyboard_getkey (struct VasEBoot_term_input *term __attribute__ ((unused)))
{
VasEBoot_uint8_t at_key;
int ret;
VasEBoot_uint8_t old_led;
if (!(pl050_regs[1] & 0x10))
return -1;
at_key = pl050_regs[2];
old_led = ps2_state.led_status;
ret = VasEBoot_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 VasEBoot_term_input VasEBoot_pl050_keyboard_term =
{
.name = "pl050_keyboard",
.getkey = VasEBoot_pl050_keyboard_getkey
};
static VasEBoot_err_t
pl050_attach(const struct VasEBoot_fdtbus_dev *dev)
{
const VasEBoot_uint32_t *reg;
reg = VasEBoot_fdtbus_get_prop (dev, "reg", 0);
/* Mouse. Nothing to do. */
if (VasEBoot_be_to_cpu32 (*reg) == 0x7000)
return 0;
pl050_regs = VasEBoot_fdtbus_map_reg (dev, 0, 0);
if (!VasEBoot_fdtbus_is_mapping_valid (pl050_regs))
return VasEBoot_error (VAS_EBOOT_ERR_IO, "could not map pl050");
ps2_state.at_keyboard_status = 0;
set_scancodes ();
keyboard_controller_led (ps2_state.led_status);
VasEBoot_term_register_input ("pl050_keyboard", &VasEBoot_pl050_keyboard_term);
return VAS_EBOOT_ERR_NONE;
}
struct VasEBoot_fdtbus_driver pl050 =
{
.compatible = "arm,pl050",
.attach = pl050_attach
};
void
VasEBoot_pl050_init (void)
{
VasEBoot_fdtbus_register (&pl050);
}