All of lore.kernel.org
 help / color / mirror / Atom feed
From: Shubhrajyoti <shubhrajyoti@ti.com>
To: riyer@nvidia.com
Cc: ccross@android.com, konkers@android.com, olof@lixom.net,
	achew@nvidia.com, linux-tegra@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
Subject: Re: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
Date: Wed, 05 Jan 2011 11:31:37 +0530	[thread overview]
Message-ID: <4D240941.3030900@ti.com> (raw)
In-Reply-To: <1294189382-13657-1-git-send-email-riyer@nvidia.com>

On Wednesday 05 January 2011 06:33 AM, riyer@nvidia.com wrote:
> From: Rakesh Iyer<riyer@nvidia.com>
>
> This patch adds support for the internal matrix keyboard controller for
> Nvidia Tegra platforms.
>
> Signed-off-by: Rakesh Iyer<riyer@nvidia.com>
> ---
>   arch/arm/mach-tegra/include/mach/kbc.h |   61 +++
>   drivers/input/keyboard/Kconfig         |    9 +
>   drivers/input/keyboard/Makefile        |    1 +
>   drivers/input/keyboard/tegra-kbc.c     |  691 ++++++++++++++++++++++++++++++++
>   4 files changed, 762 insertions(+), 0 deletions(-)
>   create mode 100644 arch/arm/mach-tegra/include/mach/kbc.h
>   create mode 100755 drivers/input/keyboard/tegra-kbc.c
>
> diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach-tegra/include/mach/kbc.h
> new file mode 100644
> index 0000000..d1ef7fc
> --- /dev/null
> +++ b/arch/arm/mach-tegra/include/mach/kbc.h
> @@ -0,0 +1,61 @@
> +/*
> + * arch/arm/mach-tegra/include/mach/kbc.h
> + *
> + * Platform definitions for tegra-kbc keyboard input driver
> + *
> + * Copyright (c) 2010, NVIDIA Corporation.
> + *
> + * 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.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
> + */
> +
> +#ifndef ASMARM_ARCH_TEGRA_KBC_H
> +#define ASMARM_ARCH_TEGRA_KBC_H
> +
> +#include<linux/types.h>
> +
> +#ifdef CONFIG_ARCH_TEGRA_2x_SOC
> +#define KBC_MAX_GPIO 24
> +#define KBC_MAX_KPENT 8
> +#else
> +#define KBC_MAX_GPIO 20
> +#define KBC_MAX_KPENT 7
> +#endif
> +
> +#define KBC_MAX_ROW 16
> +#define KBC_MAX_COL 8
> +
> +#define KBC_MAX_KEY (KBC_MAX_ROW*KBC_MAX_COL)
> +
> +struct tegra_kbc_pin_cfg {
> +	bool is_row;
> +	bool is_col;
> +	unsigned char num;
> +};
> +
> +struct tegra_kbc_wake_key {
> +	u8 row:4;
> +	u8 col:4;
> +};
> +
> +struct tegra_kbc_platform_data {
> +	unsigned int debounce_cnt;
> +	unsigned int repeat_cnt;
> +	int wake_cnt; /* 0:wake on any key>1:wake on wake_cfg */
> +	int *plain_keycode;
> +	int *fn_keycode;
> +	struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
> +	struct tegra_kbc_wake_key *wake_cfg;
> +};
> +#endif
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index 9cc488d..d582159 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -327,6 +327,15 @@ config KEYBOARD_NEWTON
>   	  To compile this driver as a module, choose M here: the
>   	  module will be called newtonkbd.
>
> +config KEYBOARD_TEGRA
> +	boolean "NVIDIA Tegra internal matrix keyboard controller support"
> +	depends on ARCH_TEGRA
> +	help
> +	  Say Y here if you want to use a matrix keyboard connected directly
> +	  to the internal keyboard controller on Tegra SoCs
> +
> +	  If unsure, say Y.
> +
>   config KEYBOARD_OPENCORES
>   	tristate "OpenCores Keyboard Controller"
>   	help
> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> index 504b591..ac0dcb9 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
>   obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
>   obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
>   obj-$(CONFIG_KEYBOARD_SUNKBD)		+= sunkbd.o
> +obj-$(CONFIG_KEYBOARD_TEGRA)		+= tegra-kbc.o
>   obj-$(CONFIG_KEYBOARD_TWL4030)		+= twl4030_keypad.o
>   obj-$(CONFIG_KEYBOARD_XTKBD)		+= xtkbd.o
>   obj-$(CONFIG_KEYBOARD_W90P910)		+= w90p910_keypad.o
> diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
> new file mode 100755
> index 0000000..0924d60
> --- /dev/null
> +++ b/drivers/input/keyboard/tegra-kbc.c
> @@ -0,0 +1,691 @@
> +/*
> + * drivers/input/keyboard/tegra-kbc.c
> + *
> + * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix
> + * keyboard controller
> + *
> + * Copyright (c) 2009-2010, NVIDIA Corporation.
> + *
> + * 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.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
> + */
> +
> +#include<linux/module.h>
> +#include<linux/input.h>
> +#include<linux/platform_device.h>
> +#include<linux/kthread.h>
Do we need all these header files?
> +#include<linux/delay.h>
> +#include<linux/io.h>
> +#include<linux/interrupt.h>
> +#include<linux/clk.h>
> +#include<linux/slab.h>
> +#include<mach/clk.h>
> +#include<mach/kbc.h>
> +
> +#define KBC_CONTROL_0	0
> +#define KBC_INT_0	4
> +#define KBC_ROW_CFG0_0	8
> +#define KBC_COL_CFG0_0	0x18
> +#define KBC_INIT_DLY_0	0x28
> +#define KBC_RPT_DLY_0	0x2c
> +#define KBC_KP_ENT0_0	0x30
> +#define KBC_KP_ENT1_0	0x34
> +#define KBC_ROW0_MASK_0	0x38
> +
> +#define res_size(res)	((res)->end - (res)->start + 1)
> +
> +struct tegra_kbc {
> +	void __iomem *mmio;
> +	struct input_dev *idev;
> +	int irq;
> +	unsigned int wake_enable_rows;
> +	unsigned int wake_enable_cols;
> +	spinlock_t lock;
> +	unsigned int repoll_time;
> +	unsigned long cp_dly;
> +	int fifo[KBC_MAX_KPENT];
> +	struct tegra_kbc_platform_data *pdata;
> +	int *plain_keycode;
> +	int *fn_keycode;
> +	struct hrtimer timer;
> +	struct clk *clk;
> +};
> +
> +static int plain_kbd_keycode[] = {
> +	/*
> +	 * Row 0 Unused, Unused, 'W', 'S', 'A', 'Z', Unused, Function,
> +	 * Row 1 Unused, Unused, Unused, Unused, Unused, Unused, Unused, Menu
> +	 * Row 2 Unused, Unused, Unused, Unused, Unused, Unused, Alt, Alt2
> +	 * Row 3 '5', '4', 'R', 'E', 'F', 'D', 'X', Unused,
> +	 * Row 4 '7', '6', 'T', 'H', 'G', 'V', 'C', SPACEBAR,
> +	 * Row 5 '9', '8', 'U', 'Y', 'J', 'N', 'B', '|\',
> +	 * Row 6 Minus, '0', 'O', 'I', 'L', 'K', '<', M,
> +	 * Row 7 Unused, '+', '}]', '#', Unused, Unused, Unused, Menu,
> +	 * Row 8 Unused, Unused, Unused, Unused, SHIFT, SHIFT, Unused, Unused,
> +	 * Row 9 Unused, Unused, Unused, Unused, Unused, Ctrl, Unused, Ctrl,
> +	 * Row A Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused,
> +	 * Row B '{[', 'P', '"', ':;', '/?, '>', Unused, Unused,
> +	 * Row C 'F10', 'F9', 'BckSpc', '3', '2', Up, Prntscr, Pause
> +	 * Row D INS, DEL, Unused, Pgup, PgDn, right, Down, Left,
> +	 * Row E F11, F12, F8, 'Q', F4, F3, '1', F7,
> +	 * Row F ESC, '~', F5, TAB, F1, F2, CAPLOCK, F6,
> +	 */
> +	KEY_RESERVED, KEY_RESERVED, KEY_W, KEY_S,
> +		KEY_A, KEY_Z, KEY_RESERVED, KEY_FN,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_MENU,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RIGHTALT, KEY_LEFTALT,
> +	KEY_5, KEY_4, KEY_R, KEY_E,
> +		KEY_F, KEY_D, KEY_X, KEY_RESERVED,
> +	KEY_7, KEY_6, KEY_T, KEY_H,
> +		KEY_G, KEY_V, KEY_C, KEY_SPACE,
> +	KEY_9, KEY_8, KEY_U, KEY_Y,
> +		KEY_J, KEY_N, KEY_B, KEY_BACKSLASH,
> +	KEY_MINUS, KEY_0, KEY_O, KEY_I,
> +		KEY_L, KEY_K, KEY_COMMA, KEY_M,
> +	KEY_RESERVED, KEY_EQUAL, KEY_RIGHTBRACE, KEY_ENTER,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_MENU,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_LEFTCTRL,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_LEFTBRACE, KEY_P, KEY_APOSTROPHE, KEY_SEMICOLON,
> +		KEY_SLASH, KEY_DOT, KEY_RESERVED, KEY_RESERVED,
> +	KEY_F10, KEY_F9, KEY_BACKSPACE, KEY_3,
> +		KEY_2, KEY_UP, KEY_PRINT, KEY_PAUSE,
> +	KEY_INSERT, KEY_DELETE, KEY_RESERVED, KEY_PAGEUP,
> +		KEY_PAGEDOWN, KEY_RIGHT, KEY_DOWN, KEY_LEFT,
> +	KEY_F11, KEY_F12, KEY_F8, KEY_Q,
> +		KEY_F4, KEY_F3, KEY_1, KEY_F7,
> +	KEY_ESC, KEY_GRAVE, KEY_F5, KEY_TAB,
> +		KEY_F1, KEY_F2, KEY_CAPSLOCK, KEY_F6
> +};
> +
> +static int fn_kbd_keycode[] = {
> +	/*
> +	 * Row 0 Unused, Unused, 'W', 'S', 'A', 'Z', Unused, Function,
> +	 * Row 1 Special, Unused, Unused, Unused, Unused, Unused, Unused, Menu
> +	 * Row 2 Unused, Unused, Unused, Unused, Unused, Unused, Alt, Alt2
> +	 * Row 3 '5', '4', 'R', 'E', 'F', 'D', 'X', Unused,
> +	 * Row 4 '7', '6', 'T', 'H', 'G', 'V', 'C', SPACEBAR,
> +	 * Row 5 '9', '8', 'U', 'Y', 'J', 'N', 'B', '|\',
> +	 * Row 6 Minus, '0', 'O', 'I', 'L', 'K', '<', M,
> +	 * Row 7 Unused, '+', '}]', '#', Unused, Unused, Unused, Menu,
> +	 * Row 8 Unused, Unused, Unused, Unused, SHIFT, SHIFT, Unused, Unused,
> +	 * Row 9 Unused, Unused, Unused, Unused, Unused, Ctrl, Unused, Control,
> +	 * Row A Unused, Unused, Unused, Unused, Unused, Unused, Unused, Unused,
> +	 * Row B '{[', 'P', '"', ':;', '/?, '>', Unused, Unused,
> +	 * Row C 'F10', 'F9', 'BckSpc', '3', '2', 'Up, Prntscr, Pause
> +	 * Row D INS, DEL, Unused, Pgup, PgDn, right, Down, Left,
> +	 * Row E F11, F12, F8, 'Q', F4, F3, '1', F7,
> +	 * Row F ESC, '~', F5, TAB, F1, F2, CAPLOCK, F6,
> +	 */
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_9, KEY_8, KEY_4, KEY_RESERVED,
> +		KEY_1, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_SLASH, KEY_6, KEY_5,
> +		KEY_3, KEY_2, KEY_RESERVED, KEY_0,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_KPASTERISK, KEY_RESERVED, KEY_KPMINUS,
> +		KEY_KPPLUS, KEY_DOT, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_RESERVED, KEY_VOLUMEUP, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_HOME,
> +		KEY_END, KEY_BRIGHTNESSUP, KEY_VOLUMEDOWN, KEY_BRIGHTNESSDOWN,
> +	KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_MUTE, KEY_RESERVED,
> +		KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +	KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
> +		KEY_QUESTION, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
> +};
> +
> +static int tegra_kbc_keycode(struct tegra_kbc *kbc, int r, int c, bool fn_key)
> +{
> +	if (!fn_key)
> +		return kbc->plain_keycode[(r * KBC_MAX_COL) + c];
> +	else
> +		return kbc->fn_keycode[(r * KBC_MAX_COL) + c];
> +}
> +
> +#ifdef CONFIG_PM
> +static int tegra_kbc_open(struct input_dev *dev);
> +static void tegra_kbc_close(struct input_dev *dev);
Could we move open and close out of the CONFIG_PM flag?
> +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter);
> +
> +static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +	struct tegra_kbc *kbc = platform_get_drvdata(pdev);
> +
> +	if (device_may_wakeup(&pdev->dev)) {
> +		tegra_kbc_setup_wakekeys(kbc, true);
> +		enable_irq_wake(kbc->irq);
> +		/* Forcefully clear the interrupt status */
> +		writel(0x7, kbc->mmio + KBC_INT_0);
> +		msleep(30);
Why do we need a sleep in the suspend path?
Could we have a #define here.
> +	} else {
> +		tegra_kbc_close(kbc->idev);
> +	}
> +
> +	return 0;
> +}
> +
> +static int tegra_kbc_resume(struct platform_device *pdev)
> +{
> +	struct tegra_kbc *kbc = platform_get_drvdata(pdev);
> +
> +	if (device_may_wakeup(&pdev->dev)) {
> +		disable_irq_wake(kbc->irq);
> +		tegra_kbc_setup_wakekeys(kbc, false);
> +	} else if (kbc->idev->users)
> +		return tegra_kbc_open(kbc->idev);
> +
> +	return 0;
> +}
> +#endif
> +
> +static void tegra_kbc_report_keys(struct tegra_kbc *kbc, int *fifo)
> +{
> +	int curr_fifo[KBC_MAX_KPENT];
> +	int rows_val[KBC_MAX_KPENT], cols_val[KBC_MAX_KPENT];
> +	u32 kp_ent_val[(KBC_MAX_KPENT + 3) / 4];
> +	u32 *kp_ents = kp_ent_val;
> +	u32 kp_ent = 0;
> +	unsigned long flags;
> +	int i, j, valid = 0;
> +	bool fn = false;
> +
> +	local_irq_save(flags);
> +	for (i = 0; i<  ARRAY_SIZE(kp_ent_val); i++)
> +		kp_ent_val[i] = readl(kbc->mmio + KBC_KP_ENT0_0 + (i*4));
> +	local_irq_restore(flags);
> +
> +	valid = 0;
> +	for (i = 0; i<  KBC_MAX_KPENT; i++) {
> +		if (!(i&3))
> +			kp_ent = *kp_ents++;
> +
> +		if (kp_ent&  0x80) {
> +			cols_val[valid] = kp_ent&  0x7;
> +			rows_val[valid++] = (kp_ent>>  3)&  0xf;
> +		}
> +		kp_ent>>= 8;
> +	}
> +
> +	for (i = 0; i<  valid; i++) {
> +		int k = tegra_kbc_keycode(kbc, rows_val[i], cols_val[i], false);
> +		if (k == KEY_FN) {
> +			fn = true;
> +			break;
> +		}
> +	}
> +
> +	j = 0;
> +	for (i = 0; i<  valid; i++) {
> +		int k = tegra_kbc_keycode(kbc, rows_val[i], cols_val[i], fn);
> +		if (likely(k != -1))
> +			curr_fifo[j++] = k;
> +	}
> +	valid = j;
> +
> +	for (i = 0; i<  KBC_MAX_KPENT; i++) {
> +		if (fifo[i] == -1)
> +			continue;
> +		for (j = 0; j<  valid; j++) {
> +			if (curr_fifo[j] == fifo[i]) {
> +				curr_fifo[j] = -1;
> +				break;
> +			}
> +		}
> +		if (j == valid) {
> +			input_report_key(kbc->idev, fifo[i], 0);
> +			fifo[i] = -1;
> +		}
> +	}
> +	for (j = 0; j<  valid; j++) {
> +		if (curr_fifo[j] == -1)
> +			continue;
> +		for (i = 0; i<  KBC_MAX_KPENT; i++) {
> +			if (fifo[i] == -1)
> +				break;
> +		}
> +		if (i != KBC_MAX_KPENT) {
> +			fifo[i] = curr_fifo[j];
> +			input_report_key(kbc->idev, fifo[i], 1);
> +		} else
> +			WARN_ON(1);
> +	}
> +}
> +
> +static enum hrtimer_restart tegra_kbc_key_repeat_timer(struct hrtimer *handle)
> +{
> +	struct tegra_kbc *kbc;
> +	unsigned long flags;
> +	u32 val;
> +	int i;
> +
> +	kbc = container_of(handle, struct tegra_kbc, timer);
> +
> +	val = (readl(kbc->mmio + KBC_INT_0)>>  4)&  0xf;
> +	if (val) {
> +		unsigned long dly;
> +
> +		tegra_kbc_report_keys(kbc, kbc->fifo);
> +
> +		dly = ((val == 1) ? kbc->repoll_time : 1) * 1000000;
> +		hrtimer_start(&kbc->timer, ktime_set(0, dly), HRTIMER_MODE_REL);
> +	} else {
> +		/* release any pressed keys and exit the loop */
> +		for (i = 0; i<  ARRAY_SIZE(kbc->fifo); i++) {
> +			if (kbc->fifo[i] == -1)
> +				continue;
> +			input_report_key(kbc->idev, kbc->fifo[i], 0);
> +			kbc->fifo[i] = -1;
> +		}
> +
> +		/* All keys are released so enable the keypress interrupt */
> +		spin_lock_irqsave(&kbc->lock, flags);
> +		val = readl(kbc->mmio + KBC_CONTROL_0);
> +		val |= (1<<3);
> +		writel(val, kbc->mmio + KBC_CONTROL_0);
> +		spin_unlock_irqrestore(&kbc->lock, flags);
> +	}
> +	return HRTIMER_NORESTART;
> +}
> +
> +static void tegra_kbc_close(struct input_dev *dev)
> +{
> +	struct tegra_kbc *kbc = input_get_drvdata(dev);
> +	unsigned long flags;
> +	u32 val;
> +
> +	spin_lock_irqsave(&kbc->lock, flags);
> +	val = readl(kbc->mmio + KBC_CONTROL_0);
> +	val&= ~1;
> +	writel(val, kbc->mmio + KBC_CONTROL_0);
> +	spin_unlock_irqrestore(&kbc->lock, flags);
> +
> +	clk_disable(kbc->clk);
> +}
> +
> +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
> +{
> +	int i;
> +	unsigned int rst_val;
> +
> +	BUG_ON(kbc->pdata->wake_cnt>  KBC_MAX_KEY);
> +	rst_val = (filter&&  kbc->pdata->wake_cnt) ? ~0 : 0;
> +
> +	for (i = 0; i<  KBC_MAX_ROW; i++)
> +		writel(rst_val, kbc->mmio+KBC_ROW0_MASK_0+i*4);
> +
> +	if (filter) {
> +		for (i = 0; i<  kbc->pdata->wake_cnt; i++) {
> +			u32 val, addr;
> +			addr = kbc->pdata->wake_cfg[i].row*4 + KBC_ROW0_MASK_0;
> +			val = readl(kbc->mmio + addr);
> +			val&= ~(1<<kbc->pdata->wake_cfg[i].col);
> +			writel(val, kbc->mmio + addr);
> +		}
> +	}
> +}
> +
> +static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
> +{
> +	const struct tegra_kbc_platform_data *pdata = kbc->pdata;
> +	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) * 4 + KBC_ROW_CFG0_0;
> +		u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0;
> +
> +		row_cfg = readl(kbc->mmio + r_offs);
> +		col_cfg = readl(kbc->mmio + c_offs);
> +
> +		row_cfg&= ~r_mask;
> +		col_cfg&= ~c_mask;
> +
> +		if (pdata->pin_cfg[i].is_row)
> +			row_cfg |= ((pdata->pin_cfg[i].num<<1) | 1)<<  r_shift;
> +		else if (pdata->pin_cfg[i].is_col)
> +			col_cfg |= ((pdata->pin_cfg[i].num<<1) | 1)<<  c_shift;
> +
> +		writel(row_cfg, kbc->mmio + r_offs);
> +		writel(col_cfg, kbc->mmio + c_offs);
> +	}
> +}
> +
> +static int tegra_kbc_open(struct input_dev *dev)
> +{
> +	struct tegra_kbc *kbc = input_get_drvdata(dev);
> +	unsigned long flags;
> +	u32 val = 0;
> +
> +	clk_enable(kbc->clk);
> +
> +	/* Reset the KBC controller to clear all previous status.*/
> +	tegra_periph_reset_assert(kbc->clk);
> +	udelay(100);
> +	tegra_periph_reset_deassert(kbc->clk);
> +	udelay(100);
> +
> +	tegra_kbc_config_pins(kbc);
> +	tegra_kbc_setup_wakekeys(kbc, false);
> +
> +	writel(kbc->pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
> +
> +	val = kbc->pdata->debounce_cnt<<  4;
> +	val |= 1<<14; /* fifo interrupt threshold = 1 entry */
> +	val |= 1<<3;  /* interrupt on FIFO threshold reached */
> +	val |= 1;     /* enable */
> +	writel(val, kbc->mmio + KBC_CONTROL_0);
> +
> +	/* Compute the delay(ns) from interrupt mode to continuous polling mode
> +	 * so the timer routine is scheduled appropriately. */
> +	val = readl(kbc->mmio + KBC_INIT_DLY_0);
> +	kbc->cp_dly = (val&  0xfffff) * 32 * 1000;
> +
> +	/* atomically clear out any remaining entries in the key FIFO
> +	 * and enable keyboard interrupts */
> +	spin_lock_irqsave(&kbc->lock, flags);
> +	while (1) {
> +		val = readl(kbc->mmio + KBC_INT_0);
> +		val>>= 4;
> +		if (val) {
> +			val = readl(kbc->mmio + KBC_KP_ENT0_0);
> +			val = readl(kbc->mmio + KBC_KP_ENT1_0);
> +		} else {
> +			break;
> +		}
> +	}
> +	writel(0x7, kbc->mmio + KBC_INT_0);
> +	spin_unlock_irqrestore(&kbc->lock, flags);
> +
> +	return 0;
> +}
> +
> +
> +static int __devexit tegra_kbc_remove(struct platform_device *pdev)
> +{
> +	struct tegra_kbc *kbc = platform_get_drvdata(pdev);
> +	struct resource *res;
> +
> +	free_irq(kbc->irq, pdev);
> +	hrtimer_cancel(&kbc->timer);
> +	clk_disable(kbc->clk);
> +	clk_put(kbc->clk);
> +
> +	input_unregister_device(kbc->idev);
> +	input_free_device(kbc->idev);
> +	iounmap(kbc->mmio);
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	release_mem_region(res->start, res_size(res));
> +
> +	kfree(kbc);
> +	return 0;
> +}
> +
> +static irqreturn_t tegra_kbc_isr(int irq, void *args)
> +{
> +	struct tegra_kbc *kbc = args;
> +	u32 val, ctl;
> +
> +	/* until all keys are released, defer further processing to
> +	 * the polling loop in tegra_kbc_key_repeat */
> +	ctl = readl(kbc->mmio + KBC_CONTROL_0);
> +	ctl&= ~(1<<3);
> +	writel(ctl, kbc->mmio + KBC_CONTROL_0);
> +
> +	/* quickly bail out&  reenable interrupts if the interrupt source
> +	 * wasn't fifo count threshold */
> +	val = readl(kbc->mmio + KBC_INT_0);
> +	writel(val, kbc->mmio + KBC_INT_0);
> +
> +	if (!(val&  (1<<2))) {
> +		ctl |= 1<<3;
> +		writel(ctl, kbc->mmio + KBC_CONTROL_0);
> +		return IRQ_HANDLED;
> +	}
> +
> +	/* Schedule timer to run when hardware is in continuous polling mode. */
> +	hrtimer_start(&kbc->timer, ktime_set(0, kbc->cp_dly), HRTIMER_MODE_REL);
> +	return IRQ_HANDLED;
> +}
> +
> +static int __devinit tegra_kbc_probe(struct platform_device *pdev)
> +{
> +	struct tegra_kbc *kbc;
> +	struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data;
> +	struct resource *res;
> +	int irq;
> +	int err;
> +	int rows[KBC_MAX_ROW];
> +	int cols[KBC_MAX_COL];
> +	int i, j;
> +	int nr = 0;
> +
> +	if (!pdata)
> +		return -EINVAL;
> +
> +	kbc = kzalloc(sizeof(*kbc), GFP_KERNEL);
> +	if (!kbc)
> +		return -ENOMEM;
> +
> +	kbc->pdata = pdata;
> +	kbc->irq = -EINVAL;
> +
> +	memset(rows, 0, sizeof(rows));
> +	memset(cols, 0, sizeof(cols));
> +
> +	kbc->idev = input_allocate_device();
> +	if (!kbc->idev) {
> +		err = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "failed to get I/O memory\n");
> +		err = -ENXIO;
> +		goto fail;
> +	}
> +	res = request_mem_region(res->start, res_size(res), pdev->name);
> +	if (!res) {
> +		dev_err(&pdev->dev, "failed to request I/O memory\n");
> +		err = -EBUSY;
> +		goto fail;
> +	}
> +	kbc->mmio = ioremap(res->start, res_size(res));
> +	if (!kbc->mmio) {
> +		dev_err(&pdev->dev, "failed to remap I/O memory\n");
> +		err = -ENXIO;
> +		goto fail;
> +	}
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq<  0) {
> +		dev_err(&pdev->dev, "failed to get keypad IRQ\n");
> +		err = -ENXIO;
> +		goto fail;
> +	}
> +	kbc->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR_OR_NULL(kbc->clk)) {
> +		dev_err(&pdev->dev, "failed to get keypad clock\n");
> +		err = (kbc->clk) ? PTR_ERR(kbc->clk) : -ENODEV;
> +		kbc->clk = NULL;
> +		goto fail;
> +	}
> +
> +	platform_set_drvdata(pdev, kbc);
> +
> +	kbc->idev->name = pdev->name;
> +	input_set_drvdata(kbc->idev, kbc);
> +	kbc->idev->id.bustype = BUS_HOST;
> +	kbc->idev->open = tegra_kbc_open;
> +	kbc->idev->close = tegra_kbc_close;
> +	kbc->idev->dev.parent =&pdev->dev;
> +	spin_lock_init(&kbc->lock);
> +
> +	for (i = 0; i<  KBC_MAX_GPIO; i++) {
> +		if (pdata->pin_cfg[i].is_row&&  pdata->pin_cfg[i].is_col) {
> +			dev_err(&pdev->dev, "invalid pin configuration data\n");
> +			err = -EINVAL;
> +			goto fail;
> +		}
> +
> +		if (pdata->pin_cfg[i].is_row) {
> +			if (pdata->pin_cfg[i].num>= KBC_MAX_ROW) {
> +				dev_err(&pdev->dev, "invalid row number\n");
> +				err = -EINVAL;
> +				goto fail;
> +			}
> +			rows[pdata->pin_cfg[i].num] = 1;
> +			nr++;
> +		} else if (pdata->pin_cfg[i].is_col) {
> +			if (pdata->pin_cfg[i].num>= KBC_MAX_COL) {
> +				dev_err(&pdev->dev, "invalid column number\n");
> +				err = -EINVAL;
> +				goto fail;
> +			}
> +			cols[pdata->pin_cfg[i].num] = 1;
> +		}
> +	}
> +	kbc->wake_enable_rows = 0;
> +	kbc->wake_enable_cols = 0;
> +
> +	for (i = 0; i<  pdata->wake_cnt; i++) {
> +		kbc->wake_enable_rows |= (1<<  kbc->pdata->wake_cfg[i].row);
> +		kbc->wake_enable_cols |= (1<<  kbc->pdata->wake_cfg[i].col);
> +	}
> +
> +	pdata->debounce_cnt = min_t(unsigned int, pdata->debounce_cnt, 0x3fful);
> +	kbc->repoll_time = 5 + (16+pdata->debounce_cnt)*nr + pdata->repeat_cnt;
> +	kbc->repoll_time = (kbc->repoll_time + 31) / 32;
> +
> +	kbc->idev->evbit[0] = BIT_MASK(EV_KEY);
> +
> +	/* Override the default keycodes with the board supplied ones. */
> +	if (pdata->plain_keycode) {
> +		kbc->plain_keycode = pdata->plain_keycode;
> +		kbc->fn_keycode = pdata->fn_keycode;
> +	} else {
> +		kbc->plain_keycode = plain_kbd_keycode;
> +		kbc->fn_keycode = fn_kbd_keycode;
> +	}
> +
> +	for (i = 0; i<  KBC_MAX_COL; i++) {
> +		if (!cols[i])
> +			continue;
> +		for (j = 0; j<  KBC_MAX_ROW; j++) {
> +			int keycode;
> +
> +			if (!rows[j])
> +				continue;
> +
> +			/* enable all the mapped keys. */
> +			keycode = tegra_kbc_keycode(kbc, j, i, false);
> +			if (keycode != -1)
> +				set_bit(keycode, kbc->idev->keybit);
> +
> +			keycode = tegra_kbc_keycode(kbc, j, i, true);
> +			if (keycode != -1)
> +				set_bit(keycode, kbc->idev->keybit);
> +		}
> +	}
> +
> +	hrtimer_init(&kbc->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> +	kbc->timer.function = tegra_kbc_key_repeat_timer;
> +	/* Initialize the FIFO to invalid entries */
> +	for (i = 0; i<  ARRAY_SIZE(kbc->fifo); i++)
> +		kbc->fifo[i] = -1;
> +
> +	/* keycode FIFO needs to be read atomically; leave local
> +	 * interrupts disabled when handling KBC interrupt */
> +	err = request_irq(irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH,
> +		pdev->name, kbc);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to request keypad IRQ\n");
> +		goto fail;
> +	}
> +	kbc->irq = irq;
> +
> +	err = input_register_device(kbc->idev);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to register input device\n");
> +		goto fail;
> +	}
> +
> +	device_init_wakeup(&pdev->dev, 1);
> +	return 0;
> +
> +fail:
> +	if (kbc->irq>= 0)
> +		free_irq(kbc->irq, pdev);
> +	if (kbc->idev)
> +		input_free_device(kbc->idev);
> +	if (kbc->clk)
> +		clk_put(kbc->clk);
> +	if (kbc->mmio)
> +		iounmap(kbc->mmio);
> +	kfree(kbc);
> +	return err;
> +}
> +
> +static struct platform_driver tegra_kbc_driver = {
> +	.probe		= tegra_kbc_probe,
> +	.remove		= tegra_kbc_remove,
> +#ifdef CONFIG_PM
> +	.suspend	= tegra_kbc_suspend,
> +	.resume		= tegra_kbc_resume,
> +#endif
> +	.driver	= {
> +		.name	= "tegra-kbc"
> +	}
> +};
> +
> +static void __exit tegra_kbc_exit(void)
> +{
> +	platform_driver_unregister(&tegra_kbc_driver);
> +}
> +
> +static int __devinit tegra_kbc_init(void)
> +{
> +	return platform_driver_register(&tegra_kbc_driver);
> +}
> +
> +module_exit(tegra_kbc_exit);
> +module_init(tegra_kbc_init);
> +
> +MODULE_DESCRIPTION("Tegra matrix keyboard controller driver");

  reply	other threads:[~2011-01-05  6:01 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-01-05  1:03 [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver riyer
2011-01-05  6:01 ` Shubhrajyoti [this message]
2011-01-05  8:36 ` Trilok Soni
2011-01-05 22:02   ` Rakesh Iyer
  -- strict thread matches above, loose matches on Subject: below --
2011-01-12 21:52 riyer
2011-01-12 21:59 ` Rakesh Iyer
2011-01-12 21:59   ` Rakesh Iyer
2011-01-12 22:02   ` Pavel Machek
2011-01-12 22:13     ` Rakesh Iyer
2011-01-12 22:26       ` Pavel Machek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4D240941.3030900@ti.com \
    --to=shubhrajyoti@ti.com \
    --cc=achew@nvidia.com \
    --cc=ccross@android.com \
    --cc=konkers@android.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=olof@lixom.net \
    --cc=riyer@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.