diff options
Diffstat (limited to 'drivers/input')
| -rw-r--r-- | drivers/input/Makefile | 3 | ||||
| -rw-r--r-- | drivers/input/input.c | 430 | ||||
| -rw-r--r-- | drivers/input/key_matrix.c | 208 | ||||
| -rw-r--r-- | drivers/input/tegra-kbc.c | 375 | 
4 files changed, 1016 insertions, 0 deletions
diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 1f4dad35b..5c831b261 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -26,10 +26,13 @@ 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) diff --git a/drivers/input/input.c b/drivers/input/input.c new file mode 100644 index 000000000..4eadd773b --- /dev/null +++ b/drivers/input/input.c @@ -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; +} diff --git a/drivers/input/key_matrix.c b/drivers/input/key_matrix.c new file mode 100644 index 000000000..84b898ff3 --- /dev/null +++ b/drivers/input/key_matrix.c @@ -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; +} diff --git a/drivers/input/tegra-kbc.c b/drivers/input/tegra-kbc.c new file mode 100644 index 000000000..f164791be --- /dev/null +++ b/drivers/input/tegra-kbc.c @@ -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); +}  |