1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Kernel 5.4 RUTX support

This commit is contained in:
Ycarus (Yannick Chabanois) 2023-08-14 17:47:02 +02:00
parent 839fcf1cab
commit cfce9f52b2
7376 changed files with 3902 additions and 546 deletions

View file

@ -0,0 +1,53 @@
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program 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 2 of
# the License, or (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB := $(obj)libinput.o
COBJS-$(CONFIG_I8042_KBD) += i8042.o
COBJS-$(CONFIG_TEGRA2_KEYBOARD) += tegra-kbc.o
ifdef CONFIG_PS2KBD
COBJS-y += keyboard.o pc_keyb.o
COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o
endif
COBJS-y += input.o
COBJS-$(CONFIG_OF_CONTROL) += key_matrix.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,654 @@
/*
* (C) Copyright 2002 ELTEC Elektronik AG
* Frank Gottschling <fgottschling@eltec.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/* i8042.c - Intel 8042 keyboard driver routines */
/* includes */
#include <common.h>
#ifdef CONFIG_USE_CPCIDVI
extern u8 gt_cpcidvi_in8(u32 offset);
extern void gt_cpcidvi_out8(u32 offset, u8 data);
#define in8(a) gt_cpcidvi_in8(a)
#define out8(a, b) gt_cpcidvi_out8(a, b)
#endif
#include <i8042.h>
/* defines */
#ifdef CONFIG_CONSOLE_CURSOR
extern void console_cursor(int state);
static int blinkCount = CONFIG_SYS_CONSOLE_BLINK_COUNT;
static int cursor_state;
#endif
/* locals */
static int kbd_input = -1; /* no input yet */
static int kbd_mapping = KBD_US; /* default US keyboard */
static int kbd_flags = NORMAL; /* after reset */
static int kbd_state; /* unshift code */
static void kbd_conv_char(unsigned char scan_code);
static void kbd_led_set(void);
static void kbd_normal(unsigned char scan_code);
static void kbd_shift(unsigned char scan_code);
static void kbd_ctrl(unsigned char scan_code);
static void kbd_num(unsigned char scan_code);
static void kbd_caps(unsigned char scan_code);
static void kbd_scroll(unsigned char scan_code);
static void kbd_alt(unsigned char scan_code);
static int kbd_input_empty(void);
static int kbd_reset(void);
static unsigned char kbd_fct_map[144] = {
/* kbd_fct_map table for scan code */
0, AS, AS, AS, AS, AS, AS, AS, /* scan 0- 7 */
AS, AS, AS, AS, AS, AS, AS, AS, /* scan 8- F */
AS, AS, AS, AS, AS, AS, AS, AS, /* scan 10-17 */
AS, AS, AS, AS, AS, CN, AS, AS, /* scan 18-1F */
AS, AS, AS, AS, AS, AS, AS, AS, /* scan 20-27 */
AS, AS, SH, AS, AS, AS, AS, AS, /* scan 28-2F */
AS, AS, AS, AS, AS, AS, SH, AS, /* scan 30-37 */
AS, AS, CP, 0, 0, 0, 0, 0, /* scan 38-3F */
0, 0, 0, 0, 0, NM, ST, ES, /* scan 40-47 */
ES, ES, ES, ES, ES, ES, ES, ES, /* scan 48-4F */
ES, ES, ES, ES, 0, 0, AS, 0, /* scan 50-57 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */
AS, 0, 0, AS, 0, 0, AS, 0, /* scan 70-77 */
0, AS, 0, 0, 0, AS, 0, 0, /* scan 78-7F */
AS, CN, AS, AS, AK, ST, EX, EX, /* enhanced */
AS, EX, EX, AS, EX, AS, EX, EX /* enhanced */
};
static unsigned char kbd_key_map[2][5][144] = {
{ /* US keyboard */
{ /* unshift code */
0, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 0- 7 */
'7', '8', '9', '0', '-', '=', 0x08, '\t', /* scan 8- F */
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /* scan 10-17 */
'o', 'p', '[', ']', '\r', CN, 'a', 's', /* scan 18-1F */
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* scan 20-27 */
'\'', '`', SH, '\\', 'z', 'x', 'c', 'v', /* scan 28-2F */
'b', 'n', 'm', ',', '.', '/', SH, '*', /* scan 30-37 */
' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */
0, 0, 0, 0, 0, NM, ST, '7', /* scan 40-47 */
'8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */
'2', '3', '0', '.', 0, 0, 0, 0, /* scan 50-57 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */
'\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */
0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */
},
{ /* shift code */
0, 0x1b, '!', '@', '#', '$', '%', '^', /* scan 0- 7 */
'&', '*', '(', ')', '_', '+', 0x08, '\t', /* scan 8- F */
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', /* scan 10-17 */
'O', 'P', '{', '}', '\r', CN, 'A', 'S', /* scan 18-1F */
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /* scan 20-27 */
'"', '~', SH, '|', 'Z', 'X', 'C', 'V', /* scan 28-2F */
'B', 'N', 'M', '<', '>', '?', SH, '*', /* scan 30-37 */
' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */
0, 0, 0, 0, 0, NM, ST, '7', /* scan 40-47 */
'8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */
'2', '3', '0', '.', 0, 0, 0, 0, /* scan 50-57 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */
'\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */
0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */
},
{ /* control code */
0xff, 0x1b, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, /* scan 0- 7 */
0x1e, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, '\t', /* scan 8- F */
0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, /* scan 10-17 */
0x0f, 0x10, 0x1b, 0x1d, '\r', CN, 0x01, 0x13, /* scan 18-1F */
0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0xff, /* scan 20-27 */
0xff, 0x1c, SH, 0xff, 0x1a, 0x18, 0x03, 0x16, /* scan 28-2F */
0x02, 0x0e, 0x0d, 0xff, 0xff, 0xff, SH, 0xff, /* scan 30-37 */
0xff, 0xff, CP, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38-3F */
0xff, 0xff, 0xff, 0xff, 0xff, NM, ST, 0xff, /* scan 40-47 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48-4F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 50-57 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58-5F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60-67 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68-6F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70-77 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78-7F */
'\r', CN, '/', '*', ' ', ST, 0xff, 0xff, /* extended */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* extended */
},
{ /* non numeric code */
0, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 0- 7 */
'7', '8', '9', '0', '-', '=', 0x08, '\t', /* scan 8- F */
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /* scan 10-17 */
'o', 'p', '[', ']', '\r', CN, 'a', 's', /* scan 18-1F */
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* scan 20-27 */
'\'', '`', SH, '\\', 'z', 'x', 'c', 'v', /* scan 28-2F */
'b', 'n', 'm', ',', '.', '/', SH, '*', /* scan 30-37 */
' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */
0, 0, 0, 0, 0, NM, ST, 'w', /* scan 40-47 */
'x', 'y', 'l', 't', 'u', 'v', 'm', 'q', /* scan 48-4F */
'r', 's', 'p', 'n', 0, 0, 0, 0, /* scan 50-57 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */
'\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */
0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */
},
{ /* right alt mode - not used in US keyboard */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 0 - 7 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 8 - F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 10 -17 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 18 -1F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 20 -27 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 28 -2F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 30 -37 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38 -3F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 40 -47 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48 -4F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 50 -57 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58 -5F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60 -67 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68 -6F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70 -77 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78 -7F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* extended */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* extended */
}
},
{ /* german keyboard */
{ /* unshift code */
0, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 0- 7 */
'7', '8', '9', '0', 0xe1, '\'', 0x08, '\t', /* scan 8- F */
'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', /* scan 10-17 */
'o', 'p', 0x81, '+', '\r', CN, 'a', 's', /* scan 18-1F */
'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x94, /* scan 20-27 */
0x84, '^', SH, '#', 'y', 'x', 'c', 'v', /* scan 28-2F */
'b', 'n', 'm', ',', '.', '-', SH, '*', /* scan 30-37 */
' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */
0, 0, 0, 0, 0, NM, ST, '7', /* scan 40-47 */
'8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */
'2', '3', '0', ',', 0, 0, '<', 0, /* scan 50-57 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */
'\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */
0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */
},
{ /* shift code */
0, 0x1b, '!', '"', 0x15, '$', '%', '&', /* scan 0- 7 */
'/', '(', ')', '=', '?', '`', 0x08, '\t', /* scan 8- F */
'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', /* scan 10-17 */
'O', 'P', 0x9a, '*', '\r', CN, 'A', 'S', /* scan 18-1F */
'D', 'F', 'G', 'H', 'J', 'K', 'L', 0x99, /* scan 20-27 */
0x8e, 0xf8, SH, '\'', 'Y', 'X', 'C', 'V', /* scan 28-2F */
'B', 'N', 'M', ';', ':', '_', SH, '*', /* scan 30-37 */
' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */
0, 0, 0, 0, 0, NM, ST, '7', /* scan 40-47 */
'8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */
'2', '3', '0', ',', 0, 0, '>', 0, /* scan 50-57 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */
'\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */
0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */
},
{ /* control code */
0xff, 0x1b, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, /* scan 0- 7 */
0x1e, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, '\t', /* scan 8- F */
0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, /* scan 10-17 */
0x0f, 0x10, 0x1b, 0x1d, '\r', CN, 0x01, 0x13, /* scan 18-1F */
0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0xff, /* scan 20-27 */
0xff, 0x1c, SH, 0xff, 0x1a, 0x18, 0x03, 0x16, /* scan 28-2F */
0x02, 0x0e, 0x0d, 0xff, 0xff, 0xff, SH, 0xff, /* scan 30-37 */
0xff, 0xff, CP, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38-3F */
0xff, 0xff, 0xff, 0xff, 0xff, NM, ST, 0xff, /* scan 40-47 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48-4F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 50-57 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58-5F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60-67 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68-6F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70-77 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78-7F */
'\r', CN, '/', '*', ' ', ST, 0xff, 0xff, /* extended */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* extended */
},
{ /* non numeric code */
0, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 0- 7 */
'7', '8', '9', '0', 0xe1, '\'', 0x08, '\t', /* scan 8- F */
'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', /* scan 10-17 */
'o', 'p', 0x81, '+', '\r', CN, 'a', 's', /* scan 18-1F */
'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x94, /* scan 20-27 */
0x84, '^', SH, 0, 'y', 'x', 'c', 'v', /* scan 28-2F */
'b', 'n', 'm', ',', '.', '-', SH, '*', /* scan 30-37 */
' ', ' ', CP, 0, 0, 0, 0, 0, /* scan 38-3F */
0, 0, 0, 0, 0, NM, ST, 'w', /* scan 40-47 */
'x', 'y', 'l', 't', 'u', 'v', 'm', 'q', /* scan 48-4F */
'r', 's', 'p', 'n', 0, 0, '<', 0, /* scan 50-57 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 58-5F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 60-67 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 68-6F */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 70-77 */
0, 0, 0, 0, 0, 0, 0, 0, /* scan 78-7F */
'\r', CN, '/', '*', ' ', ST, 'F', 'A', /* extended */
0, 'D', 'C', 0, 'B', 0, '@', 'P' /* extended */
},
{ /* Right alt mode - is used in German keyboard */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 0 - 7 */
'{', '[', ']', '}', '\\', 0xff, 0xff, 0xff, /* scan 8 - F */
'@', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 10 -17 */
0xff, 0xff, 0xff, '~', 0xff, 0xff, 0xff, 0xff, /* scan 18 -1F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 20 -27 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 28 -2F */
0xff, 0xff, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 30 -37 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38 -3F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 40 -47 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48 -4F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '|', 0xff, /* scan 50 -57 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58 -5F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60 -67 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68 -6F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70 -77 */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78 -7F */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* extended */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff /* extended */
}
}
};
static unsigned char ext_key_map[] = {
0x1c, /* keypad enter */
0x1d, /* right control */
0x35, /* keypad slash */
0x37, /* print screen */
0x38, /* right alt */
0x46, /* break */
0x47, /* editpad home */
0x48, /* editpad up */
0x49, /* editpad pgup */
0x4b, /* editpad left */
0x4d, /* editpad right */
0x4f, /* editpad end */
0x50, /* editpad dn */
0x51, /* editpad pgdn */
0x52, /* editpad ins */
0x53, /* editpad del */
0x00 /* map end */
};
/******************************************************************************/
static int kbd_controller_present(void)
{
return in8(I8042_STATUS_REG) != 0xff;
}
/*******************************************************************************
*
* i8042_kbd_init - reset keyboard and init state flags
*/
int i8042_kbd_init(void)
{
int keymap, try;
char *penv;
if (!kbd_controller_present())
return -1;
#ifdef CONFIG_USE_CPCIDVI
penv = getenv("console");
if (penv != NULL) {
if (strncmp(penv, "serial", 7) == 0)
return -1;
}
#endif
/* Init keyboard device (default US layout) */
keymap = KBD_US;
penv = getenv("keymap");
if (penv != NULL) {
if (strncmp(penv, "de", 3) == 0)
keymap = KBD_GER;
}
for (try = 0; try < KBD_RESET_TRIES; try++) {
if (kbd_reset() == 0) {
kbd_mapping = keymap;
kbd_flags = NORMAL;
kbd_state = 0;
kbd_led_set();
return 0;
}
}
return -1;
}
/*******************************************************************************
*
* i8042_tstc - test if keyboard input is available
* option: cursor blinking if called in a loop
*/
int i8042_tstc(void)
{
unsigned char scan_code = 0;
#ifdef CONFIG_CONSOLE_CURSOR
if (--blinkCount == 0) {
cursor_state ^= 1;
console_cursor(cursor_state);
blinkCount = CONFIG_SYS_CONSOLE_BLINK_COUNT;
udelay(10);
}
#endif
if ((in8(I8042_STATUS_REG) & 0x01) == 0) {
return 0;
} else {
scan_code = in8(I8042_DATA_REG);
if (scan_code == 0xfa)
return 0;
kbd_conv_char(scan_code);
if (kbd_input != -1)
return 1;
}
return 0;
}
/*******************************************************************************
*
* i8042_getc - wait till keyboard input is available
* option: turn on/off cursor while waiting
*/
int i8042_getc(void)
{
int ret_chr;
unsigned char scan_code;
while (kbd_input == -1) {
while ((in8(I8042_STATUS_REG) & 0x01) == 0) {
#ifdef CONFIG_CONSOLE_CURSOR
if (--blinkCount == 0) {
cursor_state ^= 1;
console_cursor(cursor_state);
blinkCount = CONFIG_SYS_CONSOLE_BLINK_COUNT;
}
udelay(10);
#endif
}
scan_code = in8(I8042_DATA_REG);
if (scan_code != 0xfa)
kbd_conv_char (scan_code);
}
ret_chr = kbd_input;
kbd_input = -1;
return ret_chr;
}
/******************************************************************************/
static void kbd_conv_char(unsigned char scan_code)
{
if (scan_code == 0xe0) {
kbd_flags |= EXT;
return;
}
/* if high bit of scan_code, set break flag */
if (scan_code & 0x80)
kbd_flags |= BRK;
else
kbd_flags &= ~BRK;
if ((scan_code == 0xe1) || (kbd_flags & E1)) {
if (scan_code == 0xe1) {
kbd_flags ^= BRK; /* reset the break flag */
kbd_flags ^= E1; /* bitwise EXOR with E1 flag */
}
return;
}
scan_code &= 0x7f;
if (kbd_flags & EXT) {
int i;
kbd_flags ^= EXT;
for (i = 0; ext_key_map[i]; i++) {
if (ext_key_map[i] == scan_code) {
scan_code = 0x80 + i;
break;
}
}
/* not found ? */
if (!ext_key_map[i])
return;
}
switch (kbd_fct_map[scan_code]) {
case AS:
kbd_normal(scan_code);
break;
case SH:
kbd_shift(scan_code);
break;
case CN:
kbd_ctrl(scan_code);
break;
case NM:
kbd_num(scan_code);
break;
case CP:
kbd_caps(scan_code);
break;
case ST:
kbd_scroll(scan_code);
break;
case AK:
kbd_alt(scan_code);
break;
}
return;
}
/******************************************************************************/
static void kbd_normal(unsigned char scan_code)
{
unsigned char chr;
if ((kbd_flags & BRK) == NORMAL) {
chr = kbd_key_map[kbd_mapping][kbd_state][scan_code];
if ((chr == 0xff) || (chr == 0x00))
return;
/* if caps lock convert upper to lower */
if (((kbd_flags & CAPS) == CAPS) &&
(chr >= 'a' && chr <= 'z')) {
chr -= 'a' - 'A';
}
kbd_input = chr;
}
}
/******************************************************************************/
static void kbd_shift(unsigned char scan_code)
{
if ((kbd_flags & BRK) == BRK) {
kbd_state = AS;
kbd_flags &= (~SHIFT);
} else {
kbd_state = SH;
kbd_flags |= SHIFT;
}
}
/******************************************************************************/
static void kbd_ctrl(unsigned char scan_code)
{
if ((kbd_flags & BRK) == BRK) {
kbd_state = AS;
kbd_flags &= (~CTRL);
} else {
kbd_state = CN;
kbd_flags |= CTRL;
}
}
/******************************************************************************/
static void kbd_caps(unsigned char scan_code)
{
if ((kbd_flags & BRK) == NORMAL) {
kbd_flags ^= CAPS;
kbd_led_set(); /* update keyboard LED */
}
}
/******************************************************************************/
static void kbd_num(unsigned char scan_code)
{
if ((kbd_flags & BRK) == NORMAL) {
kbd_flags ^= NUM;
kbd_state = (kbd_flags & NUM) ? AS : NM;
kbd_led_set(); /* update keyboard LED */
}
}
/******************************************************************************/
static void kbd_scroll(unsigned char scan_code)
{
if ((kbd_flags & BRK) == NORMAL) {
kbd_flags ^= STP;
kbd_led_set(); /* update keyboard LED */
if (kbd_flags & STP)
kbd_input = 0x13;
else
kbd_input = 0x11;
}
}
/******************************************************************************/
static void kbd_alt(unsigned char scan_code)
{
if ((kbd_flags & BRK) == BRK) {
kbd_state = AS;
kbd_flags &= (~ALT);
} else {
kbd_state = AK;
kbd_flags &= ALT;
}
}
/******************************************************************************/
static void kbd_led_set(void)
{
kbd_input_empty();
out8(I8042_DATA_REG, 0xed); /* SET LED command */
kbd_input_empty();
out8(I8042_DATA_REG, (kbd_flags & 0x7)); /* LED bits only */
}
/******************************************************************************/
static int kbd_input_empty(void)
{
int kbdTimeout = KBD_TIMEOUT;
/* wait for input buf empty */
while ((in8(I8042_STATUS_REG) & 0x02) && kbdTimeout--)
udelay(1000);
return kbdTimeout != -1;
}
/******************************************************************************/
static int kbd_reset(void)
{
if (kbd_input_empty() == 0)
return -1;
out8(I8042_DATA_REG, 0xff);
udelay(250000);
if (kbd_input_empty() == 0)
return -1;
#ifdef CONFIG_USE_CPCIDVI
out8(I8042_COMMAND_REG, 0x60);
#else
out8(I8042_DATA_REG, 0x60);
#endif
if (kbd_input_empty() == 0)
return -1;
out8(I8042_DATA_REG, 0x45);
if (kbd_input_empty() == 0)
return -1;
out8(I8042_COMMAND_REG, 0xae);
if (kbd_input_empty() == 0)
return -1;
return 0;
}

View file

@ -0,0 +1,430 @@
/*
* Translate key codes into ASCII
*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <stdio_dev.h>
#include <input.h>
#include <linux/input.h>
enum {
/* These correspond to the lights on the keyboard */
FLAG_NUM_LOCK = 1 << 0,
FLAG_CAPS_LOCK = 1 << 1,
FLAG_SCROLL_LOCK = 1 << 2,
/* Special flag ORed with key code to indicate release */
KEY_RELEASE = 1 << 15,
KEY_MASK = 0xfff,
};
/*
* These takes map key codes to ASCII. 0xff means no key, or special key.
* Three tables are provided - one for plain keys, one for when the shift
* 'modifier' key is pressed and one for when the ctrl modifier key is
* pressed.
*/
static const uchar kbd_plain_xlate[] = {
0xff, 0x1b, '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', '\b', '\t', /* 0x00 - 0x0f */
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', '\r', 0xff, 'a', 's', /* 0x10 - 0x1f */
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
'\'', '`', 0xff, '\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */
'b', 'n', 'm', ',' , '.', '/', 0xff, 0xff, 0xff,
' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7',
'8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
'2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */
'\r', 0xff, 0xff
};
static unsigned char kbd_shift_xlate[] = {
0xff, 0x1b, '!', '@', '#', '$', '%', '^',
'&', '*', '(', ')', '_', '+', '\b', '\t', /* 0x00 - 0x0f */
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
'O', 'P', '{', '}', '\r', 0xff, 'A', 'S', /* 0x10 - 0x1f */
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
'"', '~', 0xff, '|', 'Z', 'X', 'C', 'V', /* 0x20 - 0x2f */
'B', 'N', 'M', '<', '>', '?', 0xff, 0xff, 0xff,
' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7',
'8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
'2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */
'\r', 0xff, 0xff
};
static unsigned char kbd_ctrl_xlate[] = {
0xff, 0x1b, '1', 0x00, '3', '4', '5', 0x1E,
'7', '8', '9', '0', 0x1F, '=', '\b', '\t', /* 0x00 - 0x0f */
0x11, 0x17, 0x05, 0x12, 0x14, 0x18, 0x15, 0x09,
0x0f, 0x10, 0x1b, 0x1d, '\n', 0xff, 0x01, 0x13, /* 0x10 - 0x1f */
0x04, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, ';',
'\'', '~', 0x00, 0x1c, 0x1a, 0x18, 0x03, 0x16, /* 0x20 - 0x2f */
0x02, 0x0e, 0x0d, '<', '>', '?', 0xff, 0xff,
0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7',
'8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
'2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */
'\r', 0xff, 0xff
};
int input_queue_ascii(struct input_config *config, int ch)
{
if (config->fifo_in + 1 == INPUT_BUFFER_LEN) {
if (!config->fifo_out)
return -1; /* buffer full */
else
config->fifo_in = 0;
} else {
if (config->fifo_in + 1 == config->fifo_out)
return -1; /* buffer full */
config->fifo_in++;
}
config->fifo[config->fifo_in] = (uchar)ch;
return 0;
}
int input_tstc(struct input_config *config)
{
if (config->fifo_in == config->fifo_out && config->read_keys) {
if (!(*config->read_keys)(config))
return 0;
}
return config->fifo_in != config->fifo_out;
}
int input_getc(struct input_config *config)
{
int err = 0;
while (config->fifo_in == config->fifo_out) {
if (config->read_keys)
err = (*config->read_keys)(config);
if (err)
return -1;
}
if (++config->fifo_out == INPUT_BUFFER_LEN)
config->fifo_out = 0;
return config->fifo[config->fifo_out];
}
/**
* Process a modifier/special key press or release and decide which key
* translation array should be used as a result.
*
* TODO: Should keep track of modifier press/release
*
* @param config Input state
* @param key Key code to process
* @param release 0 if a press, 1 if a release
* @return pointer to keycode->ascii translation table that should be used
*/
static struct input_key_xlate *process_modifier(struct input_config *config,
int key, int release)
{
struct input_key_xlate *table;
int flip = -1;
int i;
/* Start with the main table, and see what modifiers change it */
assert(config->num_tables > 0);
table = &config->table[0];
for (i = 1; i < config->num_tables; i++) {
struct input_key_xlate *tab = &config->table[i];
if (key == tab->left_keycode || key == tab->right_keycode)
table = tab;
}
/* Handle the lighted keys */
if (!release) {
switch (key) {
case KEY_SCROLLLOCK:
flip = FLAG_SCROLL_LOCK;
break;
case KEY_NUMLOCK:
flip = FLAG_NUM_LOCK;
break;
case KEY_CAPSLOCK:
flip = FLAG_CAPS_LOCK;
break;
}
}
if (flip != -1) {
int leds = 0;
config->leds ^= flip;
if (config->flags & FLAG_NUM_LOCK)
leds |= INPUT_LED_NUM;
if (config->flags & FLAG_CAPS_LOCK)
leds |= INPUT_LED_CAPS;
if (config->flags & FLAG_SCROLL_LOCK)
leds |= INPUT_LED_SCROLL;
config->leds = leds;
}
return table;
}
/**
* Search an int array for a key value
*
* @param array Array to search
* @param count Number of elements in array
* @param key Key value to find
* @return element where value was first found, -1 if none
*/
static int array_search(int *array, int count, int key)
{
int i;
for (i = 0; i < count; i++) {
if (array[i] == key)
return i;
}
return -1;
}
/**
* Sort an array so that those elements that exist in the ordering are
* first in the array, and in the same order as the ordering. The algorithm
* is O(count * ocount) and designed for small arrays.
*
* TODO: Move this to common / lib?
*
* @param dest Array with elements to sort, also destination array
* @param count Number of elements to sort
* @param order Array containing ordering elements
* @param ocount Number of ordering elements
* @return number of elements in dest that are in order (these will be at the
* start of dest).
*/
static int sort_array_by_ordering(int *dest, int count, int *order,
int ocount)
{
int temp[count];
int dest_count;
int same; /* number of elements which are the same */
int i;
/* setup output items, copy items to be sorted into our temp area */
memcpy(temp, dest, count * sizeof(*dest));
dest_count = 0;
/* work through the ordering, move over the elements we agree on */
for (i = 0; i < ocount; i++) {
if (array_search(temp, count, order[i]) != -1)
dest[dest_count++] = order[i];
}
same = dest_count;
/* now move over the elements that are not in the ordering */
for (i = 0; i < count; i++) {
if (array_search(order, ocount, temp[i]) == -1)
dest[dest_count++] = temp[i];
}
assert(dest_count == count);
return same;
}
/**
* Check a list of key codes against the previous key scan
*
* Given a list of new key codes, we check how many of these are the same
* as last time.
*
* @param config Input state
* @param keycode List of key codes to examine
* @param num_keycodes Number of key codes
* @param same Returns number of key codes which are the same
*/
static int input_check_keycodes(struct input_config *config,
int keycode[], int num_keycodes, int *same)
{
/* Select the 'plain' xlate table to start with */
if (!config->num_tables) {
debug("%s: No xlate tables: cannot decode keys\n", __func__);
return -1;
}
/* sort the keycodes into the same order as the previous ones */
*same = sort_array_by_ordering(keycode, num_keycodes,
config->prev_keycodes, config->num_prev_keycodes);
memcpy(config->prev_keycodes, keycode, num_keycodes * sizeof(int));
config->num_prev_keycodes = num_keycodes;
return *same != num_keycodes;
}
/**
* Convert a list of key codes into ASCII
*
* You must call input_check_keycodes() before this. It turns the keycode
* list into a list of ASCII characters which are ready to send to the
* input layer.
*
* Characters which were seen last time do not generate fresh ASCII output.
*
* @param config Input state
* @param keycode List of key codes to examine
* @param num_keycodes Number of key codes
* @param same Number of key codes which are the same
*/
static int input_keycodes_to_ascii(struct input_config *config,
int keycode[], int num_keycodes, char output_ch[], int same)
{
struct input_key_xlate *table;
int ch_count;
int i;
table = &config->table[0];
/* deal with modifiers first */
for (i = 0; i < num_keycodes; i++) {
int key = keycode[i] & KEY_MASK;
if (key >= table->num_entries || table->xlate[key] == 0xff) {
table = process_modifier(config, key,
keycode[i] & KEY_RELEASE);
}
}
/* now find normal keys */
for (i = ch_count = 0; i < num_keycodes; i++) {
int key = keycode[i];
if (key < table->num_entries && i >= same) {
int ch = table->xlate[key];
/* If a normal key with an ASCII value, add it! */
if (ch != 0xff)
output_ch[ch_count++] = (uchar)ch;
}
}
/* ok, so return keys */
return ch_count;
}
int input_send_keycodes(struct input_config *config,
int keycode[], int num_keycodes)
{
char ch[num_keycodes];
int count, i, same = 0;
int is_repeat = 0;
unsigned delay_ms;
config->modifiers = 0;
if (!input_check_keycodes(config, keycode, num_keycodes, &same)) {
/*
* Same as last time - is it time for another repeat?
* TODO(sjg@chromium.org) We drop repeats here and since
* the caller may not call in again for a while, our
* auto-repeat speed is not quite correct. We should
* insert another character if we later realise that we
* have missed a repeat slot.
*/
is_repeat = (int)get_timer(config->next_repeat_ms) >= 0;
if (!is_repeat)
return 0;
}
count = input_keycodes_to_ascii(config, keycode, num_keycodes,
ch, is_repeat ? 0 : same);
for (i = 0; i < count; i++)
input_queue_ascii(config, ch[i]);
delay_ms = is_repeat ?
config->repeat_rate_ms :
config->repeat_delay_ms;
config->next_repeat_ms = get_timer(0) + delay_ms;
return 0;
}
int input_add_table(struct input_config *config, int left_keycode,
int right_keycode, const uchar *xlate, int num_entries)
{
struct input_key_xlate *table;
if (config->num_tables == INPUT_MAX_MODIFIERS) {
debug("%s: Too many modifier tables\n", __func__);
return -1;
}
table = &config->table[config->num_tables++];
table->left_keycode = left_keycode;
table->right_keycode = right_keycode;
table->xlate = xlate;
table->num_entries = num_entries;
return 0;
}
int input_init(struct input_config *config, int leds, int repeat_delay_ms,
int repeat_rate_ms)
{
memset(config, '\0', sizeof(*config));
config->leds = leds;
config->repeat_delay_ms = repeat_delay_ms;
config->repeat_rate_ms = repeat_rate_ms;
if (input_add_table(config, -1, -1,
kbd_plain_xlate, ARRAY_SIZE(kbd_plain_xlate)) ||
input_add_table(config, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
kbd_shift_xlate, ARRAY_SIZE(kbd_shift_xlate)) ||
input_add_table(config, KEY_LEFTCTRL, KEY_RIGHTCTRL,
kbd_ctrl_xlate, ARRAY_SIZE(kbd_ctrl_xlate))) {
debug("%s: Could not add modifier tables\n", __func__);
return -1;
}
return 0;
}
int input_stdio_register(struct stdio_dev *dev)
{
int error;
error = stdio_register(dev);
/* check if this is the standard input device */
if (!error && strcmp(getenv("stdin"), dev->name) == 0) {
/* reassign the console */
if (OVERWRITE_CONSOLE ||
console_assign(stdin, dev->name))
return -1;
}
return 0;
}

View file

@ -0,0 +1,208 @@
/*
* Manage Keyboard Matrices
*
* Copyright (c) 2012 The Chromium OS Authors.
* (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <fdtdec.h>
#include <key_matrix.h>
#include <malloc.h>
#include <linux/input.h>
/**
* Determine if the current keypress configuration can cause key ghosting
*
* We figure this out by seeing if we have two or more keys in the same
* column, as well as two or more keys in the same row.
*
* @param config Keyboard matrix config
* @param keys List of keys to check
* @param valid Number of valid keypresses to check
* @return 0 if no ghosting is possible, 1 if it is
*/
static int has_ghosting(struct key_matrix *config, struct key_matrix_key *keys,
int valid)
{
int key_in_same_col = 0, key_in_same_row = 0;
int i, j;
for (i = 0; i < valid; i++) {
/*
* Find 2 keys such that one key is in the same row
* and the other is in the same column as the i-th key.
*/
for (j = i + 1; j < valid; j++) {
if (keys[j].col == keys[i].col)
key_in_same_col = 1;
if (keys[j].row == keys[i].row)
key_in_same_row = 1;
}
}
if (key_in_same_col && key_in_same_row)
return 1;
else
return 0;
}
int key_matrix_decode(struct key_matrix *config, struct key_matrix_key keys[],
int num_keys, int keycode[], int max_keycodes)
{
const u8 *keymap;
int valid, upto;
int pos;
debug("%s: num_keys = %d\n", __func__, num_keys);
keymap = config->plain_keycode;
for (valid = upto = 0; upto < num_keys; upto++) {
struct key_matrix_key *key = &keys[upto];
debug(" valid=%d, row=%d, col=%d\n", key->valid, key->row,
key->col);
if (!key->valid)
continue;
pos = key->row * config->num_cols + key->col;
if (config->fn_keycode && pos == config->fn_pos)
keymap = config->fn_keycode;
/* Convert the (row, col) values into a keycode */
if (valid < max_keycodes)
keycode[valid++] = keymap[pos];
debug(" keycode=%d\n", keymap[pos]);
}
/* For a ghost key config, ignore the keypresses for this iteration. */
if (valid >= 3 && has_ghosting(config, keys, valid)) {
valid = 0;
debug(" ghosting detected!\n");
}
debug(" %d valid keycodes found\n", valid);
return valid;
}
/**
* Create a new keycode map from some provided data
*
* This decodes a keycode map in the format used by the fdt, which is one
* word per entry, with the row, col and keycode encoded in that word.
*
* We create a (row x col) size byte array with each entry containing the
* keycode for that (row, col). We also search for map_keycode and return
* its position if found (this is used for finding the Fn key).
*
* @param config Key matrix dimensions structure
* @param data Keycode data
* @param len Number of entries in keycode table
* @param map_keycode Key code to find in the map
* @param pos Returns position of map_keycode, if found, else -1
* @return map Pointer to allocated map
*/
static uchar *create_keymap(struct key_matrix *config, u32 *data, int len,
int map_keycode, int *pos)
{
uchar *map;
if (pos)
*pos = -1;
map = (uchar *)calloc(1, config->key_count);
if (!map) {
debug("%s: failed to malloc %d bytes\n", __func__,
config->key_count);
return NULL;
}
for (; len >= sizeof(u32); data++, len -= 4) {
u32 tmp = fdt32_to_cpu(*data);
int key_code, row, col;
int entry;
row = (tmp >> 24) & 0xff;
col = (tmp >> 16) & 0xff;
key_code = tmp & 0xffff;
entry = row * config->num_cols + col;
map[entry] = key_code;
if (pos && map_keycode == key_code)
*pos = entry;
}
return map;
}
int key_matrix_decode_fdt(struct key_matrix *config, const void *blob,
int node)
{
const struct fdt_property *prop;
int offset;
/* Check each property name for ones that we understand */
for (offset = fdt_first_property_offset(blob, node);
offset > 0;
offset = fdt_next_property_offset(blob, offset)) {
const char *name;
int len;
prop = fdt_get_property_by_offset(blob, offset, NULL);
name = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
len = strlen(name);
/* Name needs to match "1,<type>keymap" */
debug("%s: property '%s'\n", __func__, name);
if (strncmp(name, "1,", 2) || len < 8 ||
strcmp(name + len - 6, "keymap"))
continue;
len -= 8;
if (len == 0) {
config->plain_keycode = create_keymap(config,
(u32 *)prop->data, fdt32_to_cpu(prop->len),
KEY_FN, &config->fn_pos);
} else if (0 == strncmp(name + 2, "fn-", len)) {
config->fn_keycode = create_keymap(config,
(u32 *)prop->data, fdt32_to_cpu(prop->len),
-1, NULL);
} else {
debug("%s: unrecognised property '%s'\n", __func__,
name);
}
}
debug("%s: Decoded key maps %p, %p from fdt\n", __func__,
config->plain_keycode, config->fn_keycode);
if (!config->plain_keycode) {
debug("%s: cannot find keycode-plain map\n", __func__);
return -1;
}
return 0;
}
int key_matrix_init(struct key_matrix *config, int rows, int cols)
{
memset(config, '\0', sizeof(*config));
config->num_rows = rows;
config->num_cols = cols;
config->key_count = rows * cols;
assert(config->key_count > 0);
return 0;
}

View file

@ -0,0 +1,300 @@
/***********************************************************************
*
* (C) Copyright 2004
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* Keyboard driver
*
***********************************************************************/
#include <common.h>
#include <stdio_dev.h>
#include <keyboard.h>
#undef KBG_DEBUG
#ifdef KBG_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
#define DEVNAME "kbd"
#define LED_SCR 0x01 /* scroll lock led */
#define LED_CAP 0x04 /* caps lock led */
#define LED_NUM 0x02 /* num lock led */
#define KBD_BUFFER_LEN 0x20 /* size of the keyboardbuffer */
#if defined(CONFIG_MPC5xxx) || defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555)
int ps2ser_check(void);
#endif
static volatile char kbd_buffer[KBD_BUFFER_LEN];
static volatile int in_pointer = 0;
static volatile int out_pointer = 0;
static unsigned char leds = 0;
static unsigned char num_lock = 0;
static unsigned char caps_lock = 0;
static unsigned char scroll_lock = 0;
static unsigned char shift = 0;
static unsigned char ctrl = 0;
static unsigned char alt = 0;
static unsigned char e0 = 0;
/******************************************************************
* Queue handling
******************************************************************/
/* puts character in the queue and sets up the in and out pointer */
static void kbd_put_queue(char data)
{
if((in_pointer+1)==KBD_BUFFER_LEN) {
if(out_pointer==0) {
return; /* buffer full */
} else{
in_pointer=0;
}
} else {
if((in_pointer+1)==out_pointer)
return; /* buffer full */
in_pointer++;
}
kbd_buffer[in_pointer]=data;
return;
}
/* test if a character is in the queue */
static int kbd_testc(void)
{
#if defined(CONFIG_MPC5xxx) || defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555)
/* no ISR is used, so received chars must be polled */
ps2ser_check();
#endif
if(in_pointer==out_pointer)
return(0); /* no data */
else
return(1);
}
/* gets the character from the queue */
static int kbd_getc(void)
{
char c;
while(in_pointer==out_pointer) {
#if defined(CONFIG_MPC5xxx) || defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555)
/* no ISR is used, so received chars must be polled */
ps2ser_check();
#endif
;}
if((out_pointer+1)==KBD_BUFFER_LEN)
out_pointer=0;
else
out_pointer++;
c=kbd_buffer[out_pointer];
return (int)c;
}
/* Simple translation table for the keys */
static unsigned char kbd_plain_xlate[] = {
0xff,0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=','\b','\t', /* 0x00 - 0x0f */
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']','\r',0xff, 'a', 's', /* 0x10 - 0x1f */
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`',0xff,'\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */
'b', 'n', 'm', ',', '.', '/',0xff,0xff,0xff, ' ',0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */
0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
'2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */
'\r',0xff,0xff
};
static unsigned char kbd_shift_xlate[] = {
0xff,0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+','\b','\t', /* 0x00 - 0x0f */
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}','\r',0xff, 'A', 'S', /* 0x10 - 0x1f */
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',0xff, '|', 'Z', 'X', 'C', 'V', /* 0x20 - 0x2f */
'B', 'N', 'M', '<', '>', '?',0xff,0xff,0xff, ' ',0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */
0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
'2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */
'\r',0xff,0xff
};
static unsigned char kbd_ctrl_xlate[] = {
0xff,0x1b, '1',0x00, '3', '4', '5',0x1E, '7', '8', '9', '0',0x1F, '=','\b','\t', /* 0x00 - 0x0f */
0x11,0x17,0x05,0x12,0x14,0x18,0x15,0x09,0x0f,0x10,0x1b,0x1d,'\n',0xff,0x01,0x13, /* 0x10 - 0x1f */
0x04,0x06,0x08,0x09,0x0a,0x0b,0x0c, ';','\'', '~',0x00,0x1c,0x1a,0x18,0x03,0x16, /* 0x20 - 0x2f */
0x02,0x0e,0x0d, '<', '>', '?',0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x30 - 0x3f */
0xff,0xff,0xff,0xff,0xff,0xff,0xff, '7', '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
'2', '3', '0', '.',0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0x50 - 0x5F */
'\r',0xff,0xff
};
void handle_scancode(unsigned char scancode)
{
unsigned char keycode;
/* Convert scancode to keycode */
PRINTF("scancode %x\n",scancode);
if(scancode==0xe0) {
e0=1; /* special charakters */
return;
}
if(e0==1) {
e0=0; /* delete flag */
if(!( ((scancode&0x7F)==0x38)|| /* the right ctrl key */
((scancode&0x7F)==0x1D)|| /* the right alt key */
((scancode&0x7F)==0x35)|| /* the right '/' key */
((scancode&0x7F)==0x1C) )) /* the right enter key */
/* we swallow unknown e0 codes */
return;
}
/* special cntrl keys */
switch(scancode) {
case 0x2A:
case 0x36: /* shift pressed */
shift=1;
return; /* do nothing else */
case 0xAA:
case 0xB6: /* shift released */
shift=0;
return; /* do nothing else */
case 0x38: /* alt pressed */
alt=1;
return; /* do nothing else */
case 0xB8: /* alt released */
alt=0;
return; /* do nothing else */
case 0x1d: /* ctrl pressed */
ctrl=1;
return; /* do nothing else */
case 0x9d: /* ctrl released */
ctrl=0;
return; /* do nothing else */
case 0x46: /* scrollock pressed */
scroll_lock=~scroll_lock;
if(scroll_lock==0)
leds&=~LED_SCR; /* switch LED off */
else
leds|=LED_SCR; /* switch on LED */
pckbd_leds(leds);
return; /* do nothing else */
case 0x3A: /* capslock pressed */
caps_lock=~caps_lock;
if(caps_lock==0)
leds&=~LED_CAP; /* switch caps_lock off */
else
leds|=LED_CAP; /* switch on LED */
pckbd_leds(leds);
return;
case 0x45: /* numlock pressed */
num_lock=~num_lock;
if(num_lock==0)
leds&=~LED_NUM; /* switch LED off */
else
leds|=LED_NUM; /* switch on LED */
pckbd_leds(leds);
return;
case 0xC6: /* scroll lock released */
case 0xC5: /* num lock released */
case 0xBA: /* caps lock released */
return; /* just swallow */
}
#if 1
if((scancode&0x80)==0x80) /* key released */
return;
#else
if((scancode&0x80)==0x00) /* key pressed */
return;
scancode &= ~0x80;
#endif
/* now, decide which table we need */
if(scancode > (sizeof(kbd_plain_xlate)/sizeof(kbd_plain_xlate[0]))) { /* scancode not in list */
PRINTF("unkown scancode %X\n",scancode);
return; /* swallow it */
}
/* setup plain code first */
keycode=kbd_plain_xlate[scancode];
if(caps_lock==1) { /* caps_lock is pressed, overwrite plain code */
if(scancode > (sizeof(kbd_shift_xlate)/sizeof(kbd_shift_xlate[0]))) { /* scancode not in list */
PRINTF("unkown caps-locked scancode %X\n",scancode);
return; /* swallow it */
}
keycode=kbd_shift_xlate[scancode];
if(keycode<'A') { /* we only want the alphas capital */
keycode=kbd_plain_xlate[scancode];
}
}
if(shift==1) { /* shift overwrites caps_lock */
if(scancode > (sizeof(kbd_shift_xlate)/sizeof(kbd_shift_xlate[0]))) { /* scancode not in list */
PRINTF("unkown shifted scancode %X\n",scancode);
return; /* swallow it */
}
keycode=kbd_shift_xlate[scancode];
}
if(ctrl==1) { /* ctrl overwrites caps_lock and shift */
if(scancode > (sizeof(kbd_ctrl_xlate)/sizeof(kbd_ctrl_xlate[0]))) { /* scancode not in list */
PRINTF("unkown ctrl scancode %X\n",scancode);
return; /* swallow it */
}
keycode=kbd_ctrl_xlate[scancode];
}
/* check if valid keycode */
if(keycode==0xff) {
PRINTF("unkown scancode %X\n",scancode);
return; /* swallow unknown codes */
}
kbd_put_queue(keycode);
PRINTF("%x\n",keycode);
}
/******************************************************************
* Init
******************************************************************/
#ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE
extern int overwrite_console (void);
#define OVERWRITE_CONSOLE overwrite_console ()
#else
#define OVERWRITE_CONSOLE 0
#endif /* CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE */
int kbd_init (void)
{
int error;
struct stdio_dev kbddev ;
char *stdinname = getenv ("stdin");
if(kbd_init_hw()==-1)
return -1;
memset (&kbddev, 0, sizeof(kbddev));
strcpy(kbddev.name, DEVNAME);
kbddev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
kbddev.putc = NULL ;
kbddev.puts = NULL ;
kbddev.getc = kbd_getc ;
kbddev.tstc = kbd_testc ;
error = stdio_register (&kbddev);
if(error==0) {
/* check if this is the standard input device */
if(strcmp(stdinname,DEVNAME)==0) {
/* reassign the console */
if(OVERWRITE_CONSOLE) {
return 1;
}
error=console_assign(stdin,DEVNAME);
if(error==0)
return 1;
else
return error;
}
return 1;
}
return error;
}

View file

@ -0,0 +1,251 @@
/***********************************************************************
*
* (C) Copyright 2004
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* PS/2 keyboard driver
*
* Originally from linux source (drivers/char/pc_keyb.c)
*
***********************************************************************/
#include <common.h>
#include <keyboard.h>
#include <pc_keyb.h>
#undef KBG_DEBUG
#ifdef KBG_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
/*
* This reads the keyboard status port, and does the
* appropriate action.
*
*/
static unsigned char handle_kbd_event(void)
{
unsigned char status = kbd_read_status();
unsigned int work = 10000;
while ((--work > 0) && (status & KBD_STAT_OBF)) {
unsigned char scancode;
scancode = kbd_read_input();
/* Error bytes must be ignored to make the
Synaptics touchpads compaq use work */
/* Ignore error bytes */
if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) {
if (status & KBD_STAT_MOUSE_OBF)
; /* not supported: handle_mouse_event(scancode); */
else
handle_scancode(scancode);
}
status = kbd_read_status();
}
if (!work)
PRINTF("pc_keyb: controller jammed (0x%02X).\n", status);
return status;
}
static int kbd_read_data(void)
{
int val;
unsigned char status;
val = -1;
status = kbd_read_status();
if (status & KBD_STAT_OBF) {
val = kbd_read_input();
if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
val = -2;
}
return val;
}
static int kbd_wait_for_input(void)
{
unsigned long timeout;
int val;
timeout = KBD_TIMEOUT;
val=kbd_read_data();
while(val < 0) {
if(timeout--==0)
return -1;
udelay(1000);
val=kbd_read_data();
}
return val;
}
static int kb_wait(void)
{
unsigned long timeout = KBC_TIMEOUT * 10;
do {
unsigned char status = handle_kbd_event();
if (!(status & KBD_STAT_IBF))
return 0; /* ok */
udelay(1000);
timeout--;
} while (timeout);
return 1;
}
static void kbd_write_command_w(int data)
{
if(kb_wait())
PRINTF("timeout in kbd_write_command_w\n");
kbd_write_command(data);
}
static void kbd_write_output_w(int data)
{
if(kb_wait())
PRINTF("timeout in kbd_write_output_w\n");
kbd_write_output(data);
}
static void kbd_send_data(unsigned char data)
{
kbd_write_output_w(data);
kbd_wait_for_input();
}
static char * kbd_initialize(void)
{
int status;
/*
* Test the keyboard interface.
* This seems to be the only way to get it going.
* If the test is successful a x55 is placed in the input buffer.
*/
kbd_write_command_w(KBD_CCMD_SELF_TEST);
if (kbd_wait_for_input() != 0x55)
return "Kbd: failed self test";
/*
* Perform a keyboard interface test. This causes the controller
* to test the keyboard clock and data lines. The results of the
* test are placed in the input buffer.
*/
kbd_write_command_w(KBD_CCMD_KBD_TEST);
if (kbd_wait_for_input() != 0x00)
return "Kbd: interface failed self test";
/*
* Enable the keyboard by allowing the keyboard clock to run.
*/
kbd_write_command_w(KBD_CCMD_KBD_ENABLE);
/*
* Reset keyboard. If the read times out
* then the assumption is that no keyboard is
* plugged into the machine.
* This defaults the keyboard to scan-code set 2.
*
* Set up to try again if the keyboard asks for RESEND.
*/
do {
kbd_write_output_w(KBD_CMD_RESET);
status = kbd_wait_for_input();
if (status == KBD_REPLY_ACK)
break;
if (status != KBD_REPLY_RESEND) {
PRINTF("status: %X\n",status);
return "Kbd: reset failed, no ACK";
}
} while (1);
if (kbd_wait_for_input() != KBD_REPLY_POR)
return "Kbd: reset failed, no POR";
/*
* Set keyboard controller mode. During this, the keyboard should be
* in the disabled state.
*
* Set up to try again if the keyboard asks for RESEND.
*/
do {
kbd_write_output_w(KBD_CMD_DISABLE);
status = kbd_wait_for_input();
if (status == KBD_REPLY_ACK)
break;
if (status != KBD_REPLY_RESEND)
return "Kbd: disable keyboard: no ACK";
} while (1);
kbd_write_command_w(KBD_CCMD_WRITE_MODE);
kbd_write_output_w(KBD_MODE_KBD_INT
| KBD_MODE_SYS
| KBD_MODE_DISABLE_MOUSE
| KBD_MODE_KCC);
/* AMCC powerpc portables need this to use scan-code set 1 -- Cort */
kbd_write_command_w(KBD_CCMD_READ_MODE);
if (!(kbd_wait_for_input() & KBD_MODE_KCC)) {
/*
* If the controller does not support conversion,
* Set the keyboard to scan-code set 1.
*/
kbd_write_output_w(0xF0);
kbd_wait_for_input();
kbd_write_output_w(0x01);
kbd_wait_for_input();
}
kbd_write_output_w(KBD_CMD_ENABLE);
if (kbd_wait_for_input() != KBD_REPLY_ACK)
return "Kbd: enable keyboard: no ACK";
/*
* Finally, set the typematic rate to maximum.
*/
kbd_write_output_w(KBD_CMD_SET_RATE);
if (kbd_wait_for_input() != KBD_REPLY_ACK)
return "Kbd: Set rate: no ACK";
kbd_write_output_w(0x00);
if (kbd_wait_for_input() != KBD_REPLY_ACK)
return "Kbd: Set rate: no ACK";
return NULL;
}
static void kbd_interrupt(void *dev_id)
{
handle_kbd_event();
}
/******************************************************************
* Init
******************************************************************/
int kbd_init_hw(void)
{
char* result;
kbd_request_region();
result=kbd_initialize();
if (result==NULL) {
PRINTF("AT Keyboard initialized\n");
kbd_request_irq(kbd_interrupt);
return (1);
} else {
printf("%s\n",result);
return (-1);
}
}
void pckbd_leds(unsigned char leds)
{
kbd_send_data(KBD_CMD_SET_LEDS);
kbd_send_data(leds);
}

View file

@ -0,0 +1,461 @@
/***********************************************************************
*
* (C) Copyright 2004
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* PS/2 multiplexer driver
*
* Originally from linux source (drivers/char/ps2mult.c)
*
* Uses simple serial driver (ps2ser.c) to access the multiplexer
* Used by PS/2 keyboard driver (pc_keyb.c)
*
***********************************************************************/
#include <common.h>
#include <pc_keyb.h>
#include <asm/atomic.h>
#include <ps2mult.h>
/* #define DEBUG_MULT */
/* #define DEBUG_KEYB */
#define KBD_STAT_DEFAULT (KBD_STAT_SELFTEST | KBD_STAT_UNLOCKED)
#define PRINTF(format, args...) printf("ps2mult.c: " format, ## args)
#ifdef DEBUG_MULT
#define PRINTF_MULT(format, args...) printf("PS2MULT: " format, ## args)
#else
#define PRINTF_MULT(format, args...)
#endif
#ifdef DEBUG_KEYB
#define PRINTF_KEYB(format, args...) printf("KEYB: " format, ## args)
#else
#define PRINTF_KEYB(format, args...)
#endif
static ulong start_time;
static int init_done = 0;
static int received_escape = 0;
static int received_bsync = 0;
static int received_selector = 0;
static int kbd_command_active = 0;
static int mouse_command_active = 0;
static int ctl_command_active = 0;
static u_char command_byte = 0;
static void (*keyb_handler)(void *dev_id);
static u_char ps2mult_buf [PS2BUF_SIZE];
static atomic_t ps2mult_buf_cnt;
static int ps2mult_buf_in_idx;
static int ps2mult_buf_out_idx;
static u_char ps2mult_buf_status [PS2BUF_SIZE];
#ifndef CONFIG_BOARD_EARLY_INIT_R
#error #define CONFIG_BOARD_EARLY_INIT_R and call ps2mult_early_init() in board_early_init_r()
#endif
void ps2mult_early_init (void)
{
start_time = get_timer(0);
}
static void ps2mult_send_byte(u_char byte, u_char sel)
{
ps2ser_putc(sel);
if (sel == PS2MULT_KB_SELECTOR) {
PRINTF_MULT("0x%02x send KEYBOARD\n", byte);
kbd_command_active = 1;
} else {
PRINTF_MULT("0x%02x send MOUSE\n", byte);
mouse_command_active = 1;
}
switch (byte) {
case PS2MULT_ESCAPE:
case PS2MULT_BSYNC:
case PS2MULT_KB_SELECTOR:
case PS2MULT_MS_SELECTOR:
case PS2MULT_SESSION_START:
case PS2MULT_SESSION_END:
ps2ser_putc(PS2MULT_ESCAPE);
break;
default:
break;
}
ps2ser_putc(byte);
}
static void ps2mult_receive_byte(u_char byte, u_char sel)
{
u_char status = KBD_STAT_DEFAULT;
#if 1 /* Ignore mouse in U-Boot */
if (sel == PS2MULT_MS_SELECTOR) return;
#endif
if (sel == PS2MULT_KB_SELECTOR) {
if (kbd_command_active) {
if (!received_bsync) {
PRINTF_MULT("0x%02x lost KEYBOARD !!!\n", byte);
return;
} else {
kbd_command_active = 0;
received_bsync = 0;
}
}
PRINTF_MULT("0x%02x receive KEYBOARD\n", byte);
status |= KBD_STAT_IBF | KBD_STAT_OBF;
} else {
if (mouse_command_active) {
if (!received_bsync) {
PRINTF_MULT("0x%02x lost MOUSE !!!\n", byte);
return;
} else {
mouse_command_active = 0;
received_bsync = 0;
}
}
PRINTF_MULT("0x%02x receive MOUSE\n", byte);
status |= KBD_STAT_IBF | KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
}
if (atomic_read(&ps2mult_buf_cnt) < PS2BUF_SIZE) {
ps2mult_buf_status[ps2mult_buf_in_idx] = status;
ps2mult_buf[ps2mult_buf_in_idx++] = byte;
ps2mult_buf_in_idx &= (PS2BUF_SIZE - 1);
atomic_inc(&ps2mult_buf_cnt);
} else {
PRINTF("buffer overflow\n");
}
if (received_bsync) {
PRINTF("unexpected BSYNC\n");
received_bsync = 0;
}
}
void ps2mult_callback (int in_cnt)
{
int i;
u_char byte;
static int keyb_handler_active = 0;
if (!init_done) {
return;
}
for (i = 0; i < in_cnt; i ++) {
byte = ps2ser_getc();
if (received_escape) {
ps2mult_receive_byte(byte, received_selector);
received_escape = 0;
} else switch (byte) {
case PS2MULT_ESCAPE:
PRINTF_MULT("ESCAPE receive\n");
received_escape = 1;
break;
case PS2MULT_BSYNC:
PRINTF_MULT("BSYNC receive\n");
received_bsync = 1;
break;
case PS2MULT_KB_SELECTOR:
case PS2MULT_MS_SELECTOR:
PRINTF_MULT("%s receive\n",
byte == PS2MULT_KB_SELECTOR ? "KB_SEL" : "MS_SEL");
received_selector = byte;
break;
case PS2MULT_SESSION_START:
case PS2MULT_SESSION_END:
PRINTF_MULT("%s receive\n",
byte == PS2MULT_SESSION_START ?
"SESSION_START" : "SESSION_END");
break;
default:
ps2mult_receive_byte(byte, received_selector);
}
}
if (keyb_handler && !keyb_handler_active &&
atomic_read(&ps2mult_buf_cnt)) {
keyb_handler_active = 1;
keyb_handler(NULL);
keyb_handler_active = 0;
}
}
u_char ps2mult_read_status(void)
{
u_char byte;
if (atomic_read(&ps2mult_buf_cnt) == 0) {
ps2ser_check();
}
if (atomic_read(&ps2mult_buf_cnt)) {
byte = ps2mult_buf_status[ps2mult_buf_out_idx];
} else {
byte = KBD_STAT_DEFAULT;
}
PRINTF_KEYB("read_status()=0x%02x\n", byte);
return byte;
}
u_char ps2mult_read_input(void)
{
u_char byte = 0;
if (atomic_read(&ps2mult_buf_cnt) == 0) {
ps2ser_check();
}
if (atomic_read(&ps2mult_buf_cnt)) {
byte = ps2mult_buf[ps2mult_buf_out_idx++];
ps2mult_buf_out_idx &= (PS2BUF_SIZE - 1);
atomic_dec(&ps2mult_buf_cnt);
}
PRINTF_KEYB("read_input()=0x%02x\n", byte);
return byte;
}
void ps2mult_write_output(u_char val)
{
int i;
PRINTF_KEYB("write_output(0x%02x)\n", val);
for (i = 0; i < KBD_TIMEOUT; i++) {
if (!kbd_command_active && !mouse_command_active) {
break;
}
udelay(1000);
ps2ser_check();
}
if (kbd_command_active) {
PRINTF("keyboard command not acknoledged\n");
kbd_command_active = 0;
}
if (mouse_command_active) {
PRINTF("mouse command not acknoledged\n");
mouse_command_active = 0;
}
if (ctl_command_active) {
switch (ctl_command_active) {
case KBD_CCMD_WRITE_MODE:
/* Scan code conversion not supported */
command_byte = val & ~KBD_MODE_KCC;
break;
case KBD_CCMD_WRITE_AUX_OBUF:
ps2mult_receive_byte(val, PS2MULT_MS_SELECTOR);
break;
case KBD_CCMD_WRITE_MOUSE:
ps2mult_send_byte(val, PS2MULT_MS_SELECTOR);
break;
default:
PRINTF("invalid controller command\n");
break;
}
ctl_command_active = 0;
return;
}
ps2mult_send_byte(val, PS2MULT_KB_SELECTOR);
}
void ps2mult_write_command(u_char val)
{
ctl_command_active = 0;
PRINTF_KEYB("write_command(0x%02x)\n", val);
switch (val) {
case KBD_CCMD_READ_MODE:
ps2mult_receive_byte(command_byte, PS2MULT_KB_SELECTOR);
break;
case KBD_CCMD_WRITE_MODE:
ctl_command_active = val;
break;
case KBD_CCMD_MOUSE_DISABLE:
break;
case KBD_CCMD_MOUSE_ENABLE:
break;
case KBD_CCMD_SELF_TEST:
ps2mult_receive_byte(0x55, PS2MULT_KB_SELECTOR);
break;
case KBD_CCMD_KBD_TEST:
ps2mult_receive_byte(0x00, PS2MULT_KB_SELECTOR);
break;
case KBD_CCMD_KBD_DISABLE:
break;
case KBD_CCMD_KBD_ENABLE:
break;
case KBD_CCMD_WRITE_AUX_OBUF:
ctl_command_active = val;
break;
case KBD_CCMD_WRITE_MOUSE:
ctl_command_active = val;
break;
default:
PRINTF("invalid controller command\n");
break;
}
}
static int ps2mult_getc_w (void)
{
int res = -1;
int i;
for (i = 0; i < KBD_TIMEOUT; i++) {
if (ps2ser_check()) {
res = ps2ser_getc();
break;
}
udelay(1000);
}
switch (res) {
case PS2MULT_KB_SELECTOR:
case PS2MULT_MS_SELECTOR:
received_selector = res;
break;
default:
break;
}
return res;
}
int ps2mult_init (void)
{
int byte;
int kbd_found = 0;
int mouse_found = 0;
while (get_timer(start_time) < CONFIG_PS2MULT_DELAY);
ps2ser_init();
ps2ser_putc(PS2MULT_SESSION_START);
ps2ser_putc(PS2MULT_KB_SELECTOR);
ps2ser_putc(KBD_CMD_RESET);
do {
byte = ps2mult_getc_w();
} while (byte >= 0 && byte != KBD_REPLY_ACK);
if (byte == KBD_REPLY_ACK) {
byte = ps2mult_getc_w();
if (byte == 0xaa) {
kbd_found = 1;
puts("keyboard");
}
}
if (!kbd_found) {
while (byte >= 0) {
byte = ps2mult_getc_w();
}
}
#if 1 /* detect mouse */
ps2ser_putc(PS2MULT_MS_SELECTOR);
ps2ser_putc(AUX_RESET);
do {
byte = ps2mult_getc_w();
} while (byte >= 0 && byte != AUX_ACK);
if (byte == AUX_ACK) {
byte = ps2mult_getc_w();
if (byte == 0xaa) {
byte = ps2mult_getc_w();
if (byte == 0x00) {
mouse_found = 1;
puts(", mouse");
}
}
}
if (!mouse_found) {
while (byte >= 0) {
byte = ps2mult_getc_w();
}
}
#endif
if (mouse_found || kbd_found) {
if (!received_selector) {
if (mouse_found) {
received_selector = PS2MULT_MS_SELECTOR;
} else {
received_selector = PS2MULT_KB_SELECTOR;
}
}
init_done = 1;
} else {
puts("No device found");
}
puts("\n");
#if 0 /* for testing */
{
int i;
u_char key[] = {
0x1f, 0x12, 0x14, 0x12, 0x31, 0x2f, 0x39, /* setenv */
0x1f, 0x14, 0x20, 0x17, 0x31, 0x39, /* stdin */
0x1f, 0x12, 0x13, 0x17, 0x1e, 0x26, 0x1c, /* serial */
};
for (i = 0; i < sizeof (key); i++) {
ps2mult_receive_byte (key[i], PS2MULT_KB_SELECTOR);
ps2mult_receive_byte (key[i] | 0x80, PS2MULT_KB_SELECTOR);
}
}
#endif
return init_done ? 0 : -1;
}
int ps2mult_request_irq(void (*handler)(void *))
{
keyb_handler = handler;
return 0;
}

View file

@ -0,0 +1,241 @@
/***********************************************************************
*
* (C) Copyright 2004-2009
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* Simple 16550A serial driver
*
* Originally from linux source (drivers/char/ps2ser.c)
*
* Used by the PS/2 multiplexer driver (ps2mult.c)
*
***********************************************************************/
#include <common.h>
#include <asm/io.h>
#include <asm/atomic.h>
#include <ps2mult.h>
/* This is needed for ns16550.h */
#ifndef CONFIG_SYS_NS16550_REG_SIZE
#define CONFIG_SYS_NS16550_REG_SIZE 1
#endif
#include <ns16550.h>
DECLARE_GLOBAL_DATA_PTR;
/* #define DEBUG */
#define PS2SER_BAUD 57600
#ifdef CONFIG_MPC5xxx
#if CONFIG_PS2SERIAL == 1
#define PSC_BASE MPC5XXX_PSC1
#elif CONFIG_PS2SERIAL == 2
#define PSC_BASE MPC5XXX_PSC2
#elif CONFIG_PS2SERIAL == 3
#define PSC_BASE MPC5XXX_PSC3
#elif CONFIG_PS2SERIAL == 4
#define PSC_BASE MPC5XXX_PSC4
#elif CONFIG_PS2SERIAL == 5
#define PSC_BASE MPC5XXX_PSC5
#elif CONFIG_PS2SERIAL == 6
#define PSC_BASE MPC5XXX_PSC6
#else
#error CONFIG_PS2SERIAL must be in 1 ... 6
#endif
#else
#if CONFIG_PS2SERIAL == 1
#define COM_BASE (CONFIG_SYS_CCSRBAR+0x4500)
#elif CONFIG_PS2SERIAL == 2
#define COM_BASE (CONFIG_SYS_CCSRBAR+0x4600)
#else
#error CONFIG_PS2SERIAL must be in 1 ... 2
#endif
#endif /* CONFIG_MPC5xxx / other */
static int ps2ser_getc_hw(void);
static void ps2ser_interrupt(void *dev_id);
extern struct serial_state rs_table[]; /* in serial.c */
static u_char ps2buf[PS2BUF_SIZE];
static atomic_t ps2buf_cnt;
static int ps2buf_in_idx;
static int ps2buf_out_idx;
#ifdef CONFIG_MPC5xxx
int ps2ser_init(void)
{
volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE;
unsigned long baseclk;
int div;
/* reset PSC */
psc->command = PSC_SEL_MODE_REG_1;
/* select clock sources */
psc->psc_clock_select = 0;
baseclk = (gd->ipb_clk + 16) / 32;
/* switch to UART mode */
psc->sicr = 0;
/* configure parity, bit length and so on */
psc->mode = PSC_MODE_8_BITS | PSC_MODE_PARNONE;
psc->mode = PSC_MODE_ONE_STOP;
/* set up UART divisor */
div = (baseclk + (PS2SER_BAUD/2)) / PS2SER_BAUD;
psc->ctur = (div >> 8) & 0xff;
psc->ctlr = div & 0xff;
/* disable all interrupts */
psc->psc_imr = 0;
/* reset and enable Rx/Tx */
psc->command = PSC_RST_RX;
psc->command = PSC_RST_TX;
psc->command = PSC_RX_ENABLE | PSC_TX_ENABLE;
return (0);
}
#else
int ps2ser_init(void)
{
NS16550_t com_port = (NS16550_t)COM_BASE;
com_port->ier = 0x00;
com_port->lcr = UART_LCR_BKSE | UART_LCR_8N1;
com_port->dll = (CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) & 0xff;
com_port->dlm = ((CONFIG_SYS_NS16550_CLK / 16 / PS2SER_BAUD) >> 8) & 0xff;
com_port->lcr = UART_LCR_8N1;
com_port->mcr = (UART_MCR_DTR | UART_MCR_RTS);
com_port->fcr = (UART_FCR_FIFO_EN | UART_FCR_RXSR | UART_FCR_TXSR);
return (0);
}
#endif /* CONFIG_MPC5xxx / other */
void ps2ser_putc(int chr)
{
#ifdef CONFIG_MPC5xxx
volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE;
#else
NS16550_t com_port = (NS16550_t)COM_BASE;
#endif
debug(">>>> 0x%02x\n", chr);
#ifdef CONFIG_MPC5xxx
while (!(psc->psc_status & PSC_SR_TXRDY));
psc->psc_buffer_8 = chr;
#else
while ((com_port->lsr & UART_LSR_THRE) == 0);
com_port->thr = chr;
#endif
}
static int ps2ser_getc_hw(void)
{
#ifdef CONFIG_MPC5xxx
volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE;
#else
NS16550_t com_port = (NS16550_t)COM_BASE;
#endif
int res = -1;
#ifdef CONFIG_MPC5xxx
if (psc->psc_status & PSC_SR_RXRDY) {
res = (psc->psc_buffer_8);
}
#else
if (com_port->lsr & UART_LSR_DR) {
res = com_port->rbr;
}
#endif
return res;
}
int ps2ser_getc(void)
{
volatile int chr;
int flags;
debug("<< ");
flags = disable_interrupts();
do {
if (atomic_read(&ps2buf_cnt) != 0) {
chr = ps2buf[ps2buf_out_idx++];
ps2buf_out_idx &= (PS2BUF_SIZE - 1);
atomic_dec(&ps2buf_cnt);
} else {
chr = ps2ser_getc_hw();
}
}
while (chr < 0);
if (flags)
enable_interrupts();
debug("0x%02x\n", chr);
return chr;
}
int ps2ser_check(void)
{
int flags;
flags = disable_interrupts();
ps2ser_interrupt(NULL);
if (flags) enable_interrupts();
return atomic_read(&ps2buf_cnt);
}
static void ps2ser_interrupt(void *dev_id)
{
#ifdef CONFIG_MPC5xxx
volatile struct mpc5xxx_psc *psc = (struct mpc5xxx_psc *)PSC_BASE;
#else
NS16550_t com_port = (NS16550_t)COM_BASE;
#endif
int chr;
int status;
do {
chr = ps2ser_getc_hw();
#ifdef CONFIG_MPC5xxx
status = psc->psc_status;
#else
status = com_port->lsr;
#endif
if (chr < 0) continue;
if (atomic_read(&ps2buf_cnt) < PS2BUF_SIZE) {
ps2buf[ps2buf_in_idx++] = chr;
ps2buf_in_idx &= (PS2BUF_SIZE - 1);
atomic_inc(&ps2buf_cnt);
} else {
printf ("ps2ser.c: buffer overflow\n");
}
#ifdef CONFIG_MPC5xxx
} while (status & PSC_SR_RXRDY);
#else
} while (status & UART_LSR_DR);
#endif
if (atomic_read(&ps2buf_cnt)) {
ps2mult_callback(atomic_read(&ps2buf_cnt));
}
}

View file

@ -0,0 +1,375 @@
/*
* (C) Copyright 2011
* NVIDIA Corporation <www.nvidia.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <fdtdec.h>
#include <input.h>
#include <key_matrix.h>
#include <stdio_dev.h>
#include <tegra-kbc.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/funcmux.h>
#include <asm/arch/timer.h>
#include <linux/input.h>
DECLARE_GLOBAL_DATA_PTR;
enum {
KBC_MAX_GPIO = 24,
KBC_MAX_KPENT = 8, /* size of keypress entry queue */
};
#define KBC_FIFO_TH_CNT_SHIFT 14
#define KBC_DEBOUNCE_CNT_SHIFT 4
#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3)
#define KBC_CONTROL_KBC_EN (1 << 0)
#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2)
#define KBC_KPENT_VALID (1 << 7)
#define KBC_ST_STATUS (1 << 3)
enum {
KBC_DEBOUNCE_COUNT = 2,
KBC_REPEAT_RATE_MS = 30,
KBC_REPEAT_DELAY_MS = 240,
KBC_CLOCK_KHZ = 32, /* Keyboard uses a 32KHz clock */
};
/* keyboard controller config and state */
static struct keyb {
struct input_config input; /* The input layer */
struct key_matrix matrix; /* The key matrix layer */
struct kbc_tegra *kbc; /* tegra keyboard controller */
unsigned char inited; /* 1 if keyboard has been inited */
unsigned char first_scan; /* 1 if this is our first key scan */
/*
* After init we must wait a short time before polling the keyboard.
* This gives the tegra keyboard controller time to react after reset
* and lets us grab keys pressed during reset.
*/
unsigned int init_dly_ms; /* Delay before we can read keyboard */
unsigned int start_time_ms; /* Time that we inited (in ms) */
unsigned int last_poll_ms; /* Time we should last polled */
unsigned int next_repeat_ms; /* Next time we repeat a key */
} config;
/**
* reads the keyboard fifo for current keypresses
*
* @param config Keyboard config
* @param fifo Place to put fifo results
* @param max_keycodes Maximum number of key codes to put in the fifo
* @return number of items put into fifo
*/
static int tegra_kbc_find_keys(struct keyb *config, int *fifo,
int max_keycodes)
{
struct key_matrix_key keys[KBC_MAX_KPENT], *key;
u32 kp_ent = 0;
int i;
for (key = keys, i = 0; i < KBC_MAX_KPENT; i++, key++) {
/* Get next word */
if (!(i & 3))
kp_ent = readl(&config->kbc->kp_ent[i / 4]);
key->valid = (kp_ent & KBC_KPENT_VALID) != 0;
key->row = (kp_ent >> 3) & 0xf;
key->col = kp_ent & 0x7;
/* Shift to get next entry */
kp_ent >>= 8;
}
return key_matrix_decode(&config->matrix, keys, KBC_MAX_KPENT, fifo,
max_keycodes);
}
/**
* Process all the keypress sequences in fifo and send key codes
*
* The fifo contains zero or more keypress sets. Each set
* consists of from 1-8 keycodes, representing the keycodes which
* were simultaneously pressed during that scan.
*
* This function works through each set and generates ASCII characters
* for each. Not that one set may produce more than one ASCII characters -
* for example holding down 'd' and 'f' at the same time will generate
* two ASCII characters.
*
* Note: if fifo_cnt is 0, we will tell the input layer that no keys are
* pressed.
*
* @param config Keyboard config
* @param fifo_cnt Number of entries in the keyboard fifo
*/
static void process_fifo(struct keyb *config, int fifo_cnt)
{
int fifo[KBC_MAX_KPENT];
int cnt = 0;
/* Always call input_send_keycodes() at least once */
do {
if (fifo_cnt)
cnt = tegra_kbc_find_keys(config, fifo, KBC_MAX_KPENT);
input_send_keycodes(&config->input, fifo, cnt);
} while (--fifo_cnt > 0);
}
/**
* Check the keyboard controller and emit ASCII characters for any keys that
* are pressed.
*
* @param config Keyboard config
*/
static void check_for_keys(struct keyb *config)
{
int fifo_cnt;
if (!config->first_scan &&
get_timer(config->last_poll_ms) < KBC_REPEAT_RATE_MS)
return;
config->last_poll_ms = get_timer(0);
config->first_scan = 0;
/*
* Once we get here we know the keyboard has been scanned. So if there
* scan waiting for us, we know that nothing is held down.
*/
fifo_cnt = (readl(&config->kbc->interrupt) >> 4) & 0xf;
process_fifo(config, fifo_cnt);
}
/**
* In order to detect keys pressed on boot, wait for the hardware to
* complete scanning the keys. This includes time to transition from
* Wkup mode to Continous polling mode and the repoll time. We can
* deduct the time that's already elapsed.
*
* @param config Keyboard config
*/
static void kbd_wait_for_fifo_init(struct keyb *config)
{
if (!config->inited) {
unsigned long elapsed_time;
long delay_ms;
elapsed_time = get_timer(config->start_time_ms);
delay_ms = config->init_dly_ms - elapsed_time;
if (delay_ms > 0) {
udelay(delay_ms * 1000);
debug("%s: delay %ldms\n", __func__, delay_ms);
}
config->inited = 1;
}
}
/**
* Check the tegra keyboard, and send any keys that are pressed.
*
* This is called by input_tstc() and input_getc() when they need more
* characters
*
* @param input Input configuration
* @return 1, to indicate that we have something to look at
*/
int tegra_kbc_check(struct input_config *input)
{
kbd_wait_for_fifo_init(&config);
check_for_keys(&config);
return 1;
}
/**
* Test if keys are available to be read
*
* @return 0 if no keys available, 1 if keys are available
*/
static int kbd_tstc(void)
{
/* Just get input to do this for us */
return input_tstc(&config.input);
}
/**
* Read a key
*
* TODO: U-Boot wants 0 for no key, but Ctrl-@ is a valid key...
*
* @return ASCII key code, or 0 if no key, or -1 if error
*/
static int kbd_getc(void)
{
/* Just get input to do this for us */
return input_getc(&config.input);
}
/* configures keyboard GPIO registers to use the rows and columns */
static void config_kbc_gpio(struct kbc_tegra *kbc)
{
int i;
for (i = 0; i < KBC_MAX_GPIO; i++) {
u32 row_cfg, col_cfg;
u32 r_shift = 5 * (i % 6);
u32 c_shift = 4 * (i % 8);
u32 r_mask = 0x1f << r_shift;
u32 c_mask = 0xf << c_shift;
u32 r_offs = i / 6;
u32 c_offs = i / 8;
row_cfg = readl(&kbc->row_cfg[r_offs]);
col_cfg = readl(&kbc->col_cfg[c_offs]);
row_cfg &= ~r_mask;
col_cfg &= ~c_mask;
if (i < config.matrix.num_rows) {
row_cfg |= ((i << 1) | 1) << r_shift;
} else {
col_cfg |= (((i - config.matrix.num_rows) << 1) | 1)
<< c_shift;
}
writel(row_cfg, &kbc->row_cfg[r_offs]);
writel(col_cfg, &kbc->col_cfg[c_offs]);
}
}
/**
* Start up the keyboard device
*/
static void tegra_kbc_open(void)
{
struct kbc_tegra *kbc = config.kbc;
unsigned int scan_period;
u32 val;
/*
* We will scan at twice the keyboard repeat rate, so that there is
* always a scan ready when we check it in check_for_keys().
*/
scan_period = KBC_REPEAT_RATE_MS / 2;
writel(scan_period * KBC_CLOCK_KHZ, &kbc->rpt_dly);
writel(scan_period * KBC_CLOCK_KHZ, &kbc->init_dly);
/*
* Before reading from the keyboard we must wait for the init_dly
* plus the rpt_delay, plus 2ms for the row scan time.
*/
config.init_dly_ms = scan_period * 2 + 2;
val = KBC_DEBOUNCE_COUNT << KBC_DEBOUNCE_CNT_SHIFT;
val |= 1 << KBC_FIFO_TH_CNT_SHIFT; /* fifo interrupt threshold */
val |= KBC_CONTROL_KBC_EN; /* enable */
writel(val, &kbc->control);
config.start_time_ms = get_timer(0);
config.last_poll_ms = config.next_repeat_ms = get_timer(0);
config.first_scan = 1;
}
/**
* Set up the tegra keyboard. This is called by the stdio device handler
*
* We want to do this init when the keyboard is actually used rather than
* at start-up, since keyboard input may not currently be selected.
*
* Once the keyboard starts there will be a period during which we must
* wait for the keyboard to init. We do this only when a key is first
* read - see kbd_wait_for_fifo_init().
*
* @return 0 if ok, -ve on error
*/
static int init_tegra_keyboard(void)
{
#ifdef CONFIG_OF_CONTROL
int node;
node = fdtdec_next_compatible(gd->fdt_blob, 0,
COMPAT_NVIDIA_TEGRA20_KBC);
if (node < 0) {
debug("%s: cannot locate keyboard node\n", __func__);
return node;
}
config.kbc = (struct kbc_tegra *)fdtdec_get_addr(gd->fdt_blob,
node, "reg");
if ((fdt_addr_t)config.kbc == FDT_ADDR_T_NONE) {
debug("%s: No keyboard register found\n", __func__);
return -1;
}
/* Decode the keyboard matrix information (16 rows, 8 columns) */
if (key_matrix_init(&config.matrix, 16, 8)) {
debug("%s: Could not init key matrix\n", __func__);
return -1;
}
if (key_matrix_decode_fdt(&config.matrix, gd->fdt_blob, node)) {
debug("%s: Could not decode key matrix from fdt\n", __func__);
return -1;
}
if (config.matrix.fn_keycode) {
if (input_add_table(&config.input, KEY_FN, -1,
config.matrix.fn_keycode,
config.matrix.key_count))
return -1;
}
#else
#error "Tegra keyboard driver requires FDT definitions"
#endif
/* Set up pin mux and enable the clock */
funcmux_select(PERIPH_ID_KBC, FUNCMUX_DEFAULT);
clock_enable(PERIPH_ID_KBC);
config_kbc_gpio(config.kbc);
tegra_kbc_open();
debug("%s: Tegra keyboard ready\n", __func__);
return 0;
}
int drv_keyboard_init(void)
{
struct stdio_dev dev;
if (input_init(&config.input, 0, KBC_REPEAT_DELAY_MS,
KBC_REPEAT_RATE_MS)) {
debug("%s: Cannot set up input\n", __func__);
return -1;
}
config.input.read_keys = tegra_kbc_check;
memset(&dev, '\0', sizeof(dev));
strcpy(dev.name, "tegra-kbc");
dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
dev.getc = kbd_getc;
dev.tstc = kbd_tstc;
dev.start = init_tegra_keyboard;
/* Register the device. init_tegra_keyboard() will be called soon */
return input_stdio_register(&dev);
}