* [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
@ 2011-01-12 21:52 riyer
2011-01-12 21:59 ` Rakesh Iyer
0 siblings, 1 reply; 9+ messages in thread
From: riyer @ 2011-01-12 21:52 UTC (permalink / raw)
To: tsoni, dmitry.torokhov, pavel, shubhrajyoti, ccross, konkers
Cc: olof, achew, linux-tegra, linux-kernel, linux-input, Rakesh Iyer
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>
---
Incorporated changes suggested by Trilok Soni, Dmitry Torokhov and Pavel Machek
arch/arm/mach-tegra/include/mach/kbc.h | 61 +++
drivers/input/keyboard/Kconfig | 10 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/tegra-kbc.c | 634 ++++++++++++++++++++++++++++++++
4 files changed, 706 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-tegra/include/mach/kbc.h
create mode 100644 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..029a468
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/kbc.h
@@ -0,0 +1,61 @@
+/*
+ * 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 *keycode;
+ bool wakeup;
+ 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..8be47da 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -327,6 +327,16 @@ config KEYBOARD_NEWTON
To compile this driver as a module, choose M here: the
module will be called newtonkbd.
+config KEYBOARD_TEGRA
+ tristate "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.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tegra-kbc.
+
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 100644
index 0000000..2a4114e
--- /dev/null
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -0,0 +1,634 @@
+/*
+ * 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/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_MAX_DEBOUNCE_CNT 0x3fful
+
+/* KBC row scan time and delay for beginning the row scan. */
+#define KBC_ROW_SCAN_TIME 16
+#define KBC_ROW_SCAN_DLY 5
+
+/* KBC uses a 32KHz clock so a cycle = 1/32Khz */
+#define KBC_CYCLE_USEC 32
+
+/* KBC Registers */
+
+/* KBC Control Register */
+#define KBC_CONTROL_0 0x0
+#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14)
+#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4)
+#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3)
+#define KBC_CONTROL_KBC_EN (1 << 0)
+
+/* KBC Interrupt Register */
+#define KBC_INT_0 0x4
+#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2)
+
+#define KBC_ROW_CFG0_0 0x8
+#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 KBC_MAX_KEYS (KBC_MAX_ROW * KBC_MAX_COL)
+
+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_dly;
+ unsigned long cp_dly_jiffies;
+ int fifo[KBC_MAX_KPENT];
+ const struct tegra_kbc_platform_data *pdata;
+ int keycode[KBC_MAX_KEYS];
+ struct timer_list timer;
+ struct clk *clk;
+};
+
+static int tegra_kbd_keycode[KBC_MAX_KEYS] = {
+ 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 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;
+
+ spin_lock_irqsave(&kbc->lock, flags);
+ for (i = 0; i < ARRAY_SIZE(kp_ent_val); i++)
+ kp_ent_val[i] = readl(kbc->mmio + KBC_KP_ENT0_0 + (i*4));
+ spin_unlock_irqrestore(&kbc->lock, 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;
+ }
+
+ j = 0;
+ for (i = 0; i < valid; i++) {
+ int k = kbc->keycode[(rows_val[i] * KBC_MAX_COL) + cols_val[i]];
+ 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 void tegra_kbc_keypress_timer(unsigned long data)
+{
+ struct tegra_kbc *kbc = (struct tegra_kbc *)data;
+ unsigned long flags;
+ u32 val;
+ int i;
+
+ val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf;
+ if (val) {
+ unsigned long dly;
+
+ tegra_kbc_report_keys(kbc, kbc->fifo);
+
+ /* If more than one keys are pressed we need not wait
+ * for the repoll delay. */
+ dly = (val == 1) ? kbc->repoll_dly : 1;
+ mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly));
+ } 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 |= KBC_CONTROL_FIFO_CNT_INT_EN;
+ writel(val, kbc->mmio + KBC_CONTROL_0);
+ spin_unlock_irqrestore(&kbc->lock, flags);
+ }
+}
+
+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;
+ unsigned int debounce_cnt;
+ 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);
+
+ /* Keyboard debounce count is maximum of 12 bits. */
+ debounce_cnt = kbc->pdata->debounce_cnt;
+ debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
+ val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
+ val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
+ val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */
+ val |= KBC_CONTROL_KBC_EN; /* 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_jiffies = usecs_to_jiffies((val & 0xfffff) * 32);
+
+ /* 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);
+ del_timer_sync(&kbc->timer);
+ clk_disable(kbc->clk);
+ clk_put(kbc->clk);
+
+ input_unregister_device(kbc->idev);
+ iounmap(kbc->mmio);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_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_keypress_timer */
+ ctl = readl(kbc->mmio + KBC_CONTROL_0);
+ ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN;
+ writel(ctl, kbc->mmio + KBC_CONTROL_0);
+
+ /* quickly bail out & reenable interrupts if the fifo threshold count
+ * interrupt wasn't the interrupt source */
+ val = readl(kbc->mmio + KBC_INT_0);
+ writel(val, kbc->mmio + KBC_INT_0);
+
+ if (!(val & KBC_INT_FIFO_CNT_INT_STATUS)) {
+ ctl |= KBC_CONTROL_FIFO_CNT_INT_EN;
+ writel(ctl, kbc->mmio + KBC_CONTROL_0);
+ return IRQ_HANDLED;
+ }
+
+ /* Schedule timer to run when hardware is in continuous polling mode. */
+ mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies);
+ return IRQ_HANDLED;
+}
+
+static int __devinit tegra_kbc_probe(struct platform_device *pdev)
+{
+ struct tegra_kbc *kbc;
+ const 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 num_rows = 0;
+ unsigned int debounce_cnt;
+ unsigned int scan_time_rows;
+
+ 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_allocateinput;
+ }
+
+ 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_memoryresource;
+ }
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ err = -EBUSY;
+ goto fail_memoryresource;
+ }
+ kbc->mmio = ioremap(res->start, resource_size(res));
+ if (!kbc->mmio) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ err = -ENXIO;
+ goto fail_memoryresource;
+ }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
+ err = -ENXIO;
+ goto fail_keyboardresource;
+ }
+ kbc->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR_OR_NULL(kbc->clk)) {
+ dev_err(&pdev->dev, "failed to get keyboard clock\n");
+ err = (kbc->clk) ? PTR_ERR(kbc->clk) : -ENODEV;
+ kbc->clk = NULL;
+ goto fail_keyboardresource;
+ }
+
+ 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_configurekeyboard;
+ }
+
+ 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_configurekeyboard;
+ }
+ rows[pdata->pin_cfg[i].num] = 1;
+ num_rows++;
+ } 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_configurekeyboard;
+ }
+ 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);
+ }
+
+ debounce_cnt = pdata->debounce_cnt;
+ debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
+
+ /* The time delay between two consecutive reads of the FIFO is the sum
+ * of the repeat time and the time taken for scanning the rows.
+ * There is an additional delay before the row scanning starts.
+ * The repoll delay is computed in milliseconds. */
+ scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
+ kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
+ kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000;
+
+ kbc->idev->evbit[0] = BIT_MASK(EV_KEY);
+
+ /* Override the default keycodes with the board supplied ones. */
+ if (pdata->keycode)
+ memcpy(kbc->keycode, pdata->keycode, sizeof(kbc->keycode));
+ else
+ memcpy(kbc->keycode, tegra_kbd_keycode, sizeof(kbc->keycode));
+
+ kbc->idev->keycode = kbc->keycode;
+ kbc->idev->keycodesize = sizeof(kbc->keycode[0]);
+ kbc->idev->keycodemax = ARRAY_SIZE(kbc->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 = kbc->keycode[(j * KBC_MAX_COL) + i];
+ if (keycode != -1)
+ set_bit(keycode, kbc->idev->keybit);
+
+ }
+ }
+
+ setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
+ /* 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 keyboard IRQ\n");
+ goto fail_configurekeyboard;
+ }
+ kbc->irq = irq;
+
+ err = input_register_device(kbc->idev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto fail_registerinput;
+ }
+
+ device_init_wakeup(&pdev->dev, pdata->wakeup);
+ return 0;
+
+fail_registerinput:
+ free_irq(kbc->irq, pdev);
+fail_configurekeyboard:
+ clk_put(kbc->clk);
+fail_keyboardresource:
+ iounmap(kbc->mmio);
+fail_memoryresource:
+ input_free_device(kbc->idev);
+fail_allocateinput:
+ kfree(kbc);
+ return err;
+}
+
+#ifdef CONFIG_PM
+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);
+ } else if (kbc->idev->users) {
+ mutex_lock(&kbc->idev->mutex);
+ tegra_kbc_close(kbc->idev);
+ mutex_unlock(&kbc->idev->mutex);
+ }
+
+ return 0;
+}
+
+static int tegra_kbc_resume(struct platform_device *pdev)
+{
+ struct tegra_kbc *kbc = platform_get_drvdata(pdev);
+ int err = 0;
+
+ if (device_may_wakeup(&pdev->dev)) {
+ disable_irq_wake(kbc->irq);
+ tegra_kbc_setup_wakekeys(kbc, false);
+ } else if (kbc->idev->users) {
+ mutex_lock(&kbc->idev->mutex);
+ err = tegra_kbc_open(kbc->idev);
+ mutex_unlock(&kbc->idev->mutex);
+ }
+
+ return err;
+}
+#endif
+
+static struct platform_driver tegra_kbc_driver = {
+ .probe = tegra_kbc_probe,
+ .remove = __devexit_p(tegra_kbc_remove),
+#ifdef CONFIG_PM
+ .suspend = tegra_kbc_suspend,
+ .resume = tegra_kbc_resume,
+#endif
+ .driver = {
+ .name = "tegra-kbc",
+ .owner = THIS_MODULE,
+ }
+};
+
+static void __exit tegra_kbc_exit(void)
+{
+ platform_driver_unregister(&tegra_kbc_driver);
+}
+module_exit(tegra_kbc_exit);
+
+static int __init tegra_kbc_init(void)
+{
+ return platform_driver_register(&tegra_kbc_driver);
+}
+module_init(tegra_kbc_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>");
+MODULE_DESCRIPTION("Tegra matrix keyboard controller driver");
+MODULE_ALIAS("platform:tegra-kbc");
--
1.7.0.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* RE: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
2011-01-12 21:52 [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver riyer
@ 2011-01-12 21:59 ` Rakesh Iyer
2011-01-12 22:02 ` Pavel Machek
0 siblings, 1 reply; 9+ messages in thread
From: Rakesh Iyer @ 2011-01-12 21:59 UTC (permalink / raw)
To: tsoni@codeaurora.org, dmitry.torokhov@gmail.com, pavel@ucw.cz,
shubhrajyoti@ti.com
Cc: olof@lixom.net, Andrew Chew, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
Hi all.
I apologize for not specifying the PATCH indicator as "PATCH v4".
Sorry for the inconvenience caused.
Thanks and Regards
Rakesh
> -----Original Message-----
> From: Rakesh Iyer
> Sent: Wednesday, January 12, 2011 1:53 PM
> To: tsoni@codeaurora.org; dmitry.torokhov@gmail.com; pavel@ucw.cz;
> shubhrajyoti@ti.com; ccross@android.com; konkers@android.com
> Cc: olof@lixom.net; Andrew Chew; linux-tegra@vger.kernel.org; linux-
> kernel@vger.kernel.org; linux-input@vger.kernel.org; Rakesh Iyer
> Subject: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
>
> 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>
> ---
> Incorporated changes suggested by Trilok Soni, Dmitry Torokhov and Pavel Machek
>
> arch/arm/mach-tegra/include/mach/kbc.h | 61 +++
> drivers/input/keyboard/Kconfig | 10 +
> drivers/input/keyboard/Makefile | 1 +
> drivers/input/keyboard/tegra-kbc.c | 634 ++++++++++++++++++++++++++++++++
> 4 files changed, 706 insertions(+), 0 deletions(-)
> create mode 100644 arch/arm/mach-tegra/include/mach/kbc.h
> create mode 100644 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..029a468
> --- /dev/null
> +++ b/arch/arm/mach-tegra/include/mach/kbc.h
> @@ -0,0 +1,61 @@
> +/*
> + * 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 *keycode;
> + bool wakeup;
> + 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..8be47da 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -327,6 +327,16 @@ config KEYBOARD_NEWTON
> To compile this driver as a module, choose M here: the
> module will be called newtonkbd.
>
> +config KEYBOARD_TEGRA
> + tristate "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.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called tegra-kbc.
> +
> 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 100644
> index 0000000..2a4114e
> --- /dev/null
> +++ b/drivers/input/keyboard/tegra-kbc.c
> @@ -0,0 +1,634 @@
> +/*
> + * 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/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_MAX_DEBOUNCE_CNT 0x3fful
> +
> +/* KBC row scan time and delay for beginning the row scan. */
> +#define KBC_ROW_SCAN_TIME 16
> +#define KBC_ROW_SCAN_DLY 5
> +
> +/* KBC uses a 32KHz clock so a cycle = 1/32Khz */
> +#define KBC_CYCLE_USEC 32
> +
> +/* KBC Registers */
> +
> +/* KBC Control Register */
> +#define KBC_CONTROL_0 0x0
> +#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14)
> +#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4)
> +#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3)
> +#define KBC_CONTROL_KBC_EN (1 << 0)
> +
> +/* KBC Interrupt Register */
> +#define KBC_INT_0 0x4
> +#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2)
> +
> +#define KBC_ROW_CFG0_0 0x8
> +#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 KBC_MAX_KEYS (KBC_MAX_ROW * KBC_MAX_COL)
> +
> +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_dly;
> + unsigned long cp_dly_jiffies;
> + int fifo[KBC_MAX_KPENT];
> + const struct tegra_kbc_platform_data *pdata;
> + int keycode[KBC_MAX_KEYS];
> + struct timer_list timer;
> + struct clk *clk;
> +};
> +
> +static int tegra_kbd_keycode[KBC_MAX_KEYS] = {
> + 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 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;
> +
> + spin_lock_irqsave(&kbc->lock, flags);
> + for (i = 0; i < ARRAY_SIZE(kp_ent_val); i++)
> + kp_ent_val[i] = readl(kbc->mmio + KBC_KP_ENT0_0 + (i*4));
> + spin_unlock_irqrestore(&kbc->lock, 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;
> + }
> +
> + j = 0;
> + for (i = 0; i < valid; i++) {
> + int k = kbc->keycode[(rows_val[i] * KBC_MAX_COL) + cols_val[i]];
> + 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 void tegra_kbc_keypress_timer(unsigned long data)
> +{
> + struct tegra_kbc *kbc = (struct tegra_kbc *)data;
> + unsigned long flags;
> + u32 val;
> + int i;
> +
> + val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf;
> + if (val) {
> + unsigned long dly;
> +
> + tegra_kbc_report_keys(kbc, kbc->fifo);
> +
> + /* If more than one keys are pressed we need not wait
> + * for the repoll delay. */
> + dly = (val == 1) ? kbc->repoll_dly : 1;
> + mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly));
> + } 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 |= KBC_CONTROL_FIFO_CNT_INT_EN;
> + writel(val, kbc->mmio + KBC_CONTROL_0);
> + spin_unlock_irqrestore(&kbc->lock, flags);
> + }
> +}
> +
> +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;
> + unsigned int debounce_cnt;
> + 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);
> +
> + /* Keyboard debounce count is maximum of 12 bits. */
> + debounce_cnt = kbc->pdata->debounce_cnt;
> + debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
> + val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
> + val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
> + val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */
> + val |= KBC_CONTROL_KBC_EN; /* 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_jiffies = usecs_to_jiffies((val & 0xfffff) * 32);
> +
> + /* 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);
> + del_timer_sync(&kbc->timer);
> + clk_disable(kbc->clk);
> + clk_put(kbc->clk);
> +
> + input_unregister_device(kbc->idev);
> + iounmap(kbc->mmio);
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + release_mem_region(res->start, resource_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_keypress_timer */
> + ctl = readl(kbc->mmio + KBC_CONTROL_0);
> + ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN;
> + writel(ctl, kbc->mmio + KBC_CONTROL_0);
> +
> + /* quickly bail out & reenable interrupts if the fifo threshold count
> + * interrupt wasn't the interrupt source */
> + val = readl(kbc->mmio + KBC_INT_0);
> + writel(val, kbc->mmio + KBC_INT_0);
> +
> + if (!(val & KBC_INT_FIFO_CNT_INT_STATUS)) {
> + ctl |= KBC_CONTROL_FIFO_CNT_INT_EN;
> + writel(ctl, kbc->mmio + KBC_CONTROL_0);
> + return IRQ_HANDLED;
> + }
> +
> + /* Schedule timer to run when hardware is in continuous polling mode. */
> + mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies);
> + return IRQ_HANDLED;
> +}
> +
> +static int __devinit tegra_kbc_probe(struct platform_device *pdev)
> +{
> + struct tegra_kbc *kbc;
> + const 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 num_rows = 0;
> + unsigned int debounce_cnt;
> + unsigned int scan_time_rows;
> +
> + 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_allocateinput;
> + }
> +
> + 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_memoryresource;
> + }
> + res = request_mem_region(res->start, resource_size(res), pdev->name);
> + if (!res) {
> + dev_err(&pdev->dev, "failed to request I/O memory\n");
> + err = -EBUSY;
> + goto fail_memoryresource;
> + }
> + kbc->mmio = ioremap(res->start, resource_size(res));
> + if (!kbc->mmio) {
> + dev_err(&pdev->dev, "failed to remap I/O memory\n");
> + err = -ENXIO;
> + goto fail_memoryresource;
> + }
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
> + err = -ENXIO;
> + goto fail_keyboardresource;
> + }
> + kbc->clk = clk_get(&pdev->dev, NULL);
> + if (IS_ERR_OR_NULL(kbc->clk)) {
> + dev_err(&pdev->dev, "failed to get keyboard clock\n");
> + err = (kbc->clk) ? PTR_ERR(kbc->clk) : -ENODEV;
> + kbc->clk = NULL;
> + goto fail_keyboardresource;
> + }
> +
> + 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_configurekeyboard;
> + }
> +
> + 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_configurekeyboard;
> + }
> + rows[pdata->pin_cfg[i].num] = 1;
> + num_rows++;
> + } 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_configurekeyboard;
> + }
> + 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);
> + }
> +
> + debounce_cnt = pdata->debounce_cnt;
> + debounce_cnt = min_t(unsigned int, debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
> +
> + /* The time delay between two consecutive reads of the FIFO is the sum
> + * of the repeat time and the time taken for scanning the rows.
> + * There is an additional delay before the row scanning starts.
> + * The repoll delay is computed in milliseconds. */
> + scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
> + kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
> + kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000;
> +
> + kbc->idev->evbit[0] = BIT_MASK(EV_KEY);
> +
> + /* Override the default keycodes with the board supplied ones. */
> + if (pdata->keycode)
> + memcpy(kbc->keycode, pdata->keycode, sizeof(kbc->keycode));
> + else
> + memcpy(kbc->keycode, tegra_kbd_keycode, sizeof(kbc->keycode));
> +
> + kbc->idev->keycode = kbc->keycode;
> + kbc->idev->keycodesize = sizeof(kbc->keycode[0]);
> + kbc->idev->keycodemax = ARRAY_SIZE(kbc->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 = kbc->keycode[(j * KBC_MAX_COL) + i];
> + if (keycode != -1)
> + set_bit(keycode, kbc->idev->keybit);
> +
> + }
> + }
> +
> + setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
> + /* 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 keyboard IRQ\n");
> + goto fail_configurekeyboard;
> + }
> + kbc->irq = irq;
> +
> + err = input_register_device(kbc->idev);
> + if (err) {
> + dev_err(&pdev->dev, "failed to register input device\n");
> + goto fail_registerinput;
> + }
> +
> + device_init_wakeup(&pdev->dev, pdata->wakeup);
> + return 0;
> +
> +fail_registerinput:
> + free_irq(kbc->irq, pdev);
> +fail_configurekeyboard:
> + clk_put(kbc->clk);
> +fail_keyboardresource:
> + iounmap(kbc->mmio);
> +fail_memoryresource:
> + input_free_device(kbc->idev);
> +fail_allocateinput:
> + kfree(kbc);
> + return err;
> +}
> +
> +#ifdef CONFIG_PM
> +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);
> + } else if (kbc->idev->users) {
> + mutex_lock(&kbc->idev->mutex);
> + tegra_kbc_close(kbc->idev);
> + mutex_unlock(&kbc->idev->mutex);
> + }
> +
> + return 0;
> +}
> +
> +static int tegra_kbc_resume(struct platform_device *pdev)
> +{
> + struct tegra_kbc *kbc = platform_get_drvdata(pdev);
> + int err = 0;
> +
> + if (device_may_wakeup(&pdev->dev)) {
> + disable_irq_wake(kbc->irq);
> + tegra_kbc_setup_wakekeys(kbc, false);
> + } else if (kbc->idev->users) {
> + mutex_lock(&kbc->idev->mutex);
> + err = tegra_kbc_open(kbc->idev);
> + mutex_unlock(&kbc->idev->mutex);
> + }
> +
> + return err;
> +}
> +#endif
> +
> +static struct platform_driver tegra_kbc_driver = {
> + .probe = tegra_kbc_probe,
> + .remove = __devexit_p(tegra_kbc_remove),
> +#ifdef CONFIG_PM
> + .suspend = tegra_kbc_suspend,
> + .resume = tegra_kbc_resume,
> +#endif
> + .driver = {
> + .name = "tegra-kbc",
> + .owner = THIS_MODULE,
> + }
> +};
> +
> +static void __exit tegra_kbc_exit(void)
> +{
> + platform_driver_unregister(&tegra_kbc_driver);
> +}
> +module_exit(tegra_kbc_exit);
> +
> +static int __init tegra_kbc_init(void)
> +{
> + return platform_driver_register(&tegra_kbc_driver);
> +}
> +module_init(tegra_kbc_init);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>");
> +MODULE_DESCRIPTION("Tegra matrix keyboard controller driver");
> +MODULE_ALIAS("platform:tegra-kbc");
> --
> 1.7.0.4
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
2011-01-12 21:59 ` Rakesh Iyer
@ 2011-01-12 22:02 ` Pavel Machek
2011-01-12 22:13 ` Rakesh Iyer
0 siblings, 1 reply; 9+ messages in thread
From: Pavel Machek @ 2011-01-12 22:02 UTC (permalink / raw)
To: Rakesh Iyer
Cc: tsoni@codeaurora.org, dmitry.torokhov@gmail.com,
shubhrajyoti@ti.com, ccross@android.com, konkers@android.com,
olof@lixom.net, Andrew Chew, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
On Wed 2011-01-12 13:59:45, Rakesh Iyer wrote:
> Hi all.
>
> I apologize for not specifying the PATCH indicator as "PATCH v4".
>
> Sorry for the inconvenience caused.
Well, that is not huge problem, but short note of changes from last
version would be welcome.
AFAICT, FN handling was reworked...?
> > + KEY_F10, KEY_F9, KEY_BACKSPACE, KEY_3,
> > + KEY_2, KEY_UP, KEY_PRINT, KEY_PAUSE,
But now I'm confused; does hardware have Fx keys (F10 above) or not?
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
2011-01-12 22:02 ` Pavel Machek
@ 2011-01-12 22:13 ` Rakesh Iyer
2011-01-12 22:26 ` Pavel Machek
0 siblings, 1 reply; 9+ messages in thread
From: Rakesh Iyer @ 2011-01-12 22:13 UTC (permalink / raw)
To: 'Pavel Machek'
Cc: tsoni@codeaurora.org, dmitry.torokhov@gmail.com,
shubhrajyoti@ti.com, ccross@android.com, konkers@android.com,
olof@lixom.net, Andrew Chew, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
Here are the list of changes. I was not sure of the protocol of adding this into the patch email.
a) Addressed all of Trilok Soni's comments excluding the tegra clk programming which is necessary.
Use correct labels in tegra_kbc_probe error paths.
Identify and if necessary document all register accesses without using magic numbers.
Modify suspend to check for valid users before proceeding to close.
b) Removed the Fn keymap and its references. This obviated the need for some helper functions.
c) Setup the input device's keycodexx parameters.
Pavel's query
> But now I'm confused; does hardware have Fx keys (F10 above) or not?
> Pavel
The function key for this keyboard is separate from the F1 to F12 keys, where the latter are always available.
The function key serves just like a SHIFT key to overload the use of certain keys e.g. Fn+PgUP-> "Home", Fn+PgDn -> "End"
Regards
Rakesh
> -----Original Message-----
> From: Pavel Machek [mailto:pavel@ucw.cz]
> Sent: Wednesday, January 12, 2011 2:03 PM
> To: Rakesh Iyer
> Cc: tsoni@codeaurora.org; dmitry.torokhov@gmail.com; shubhrajyoti@ti.com;
> ccross@android.com; konkers@android.com; olof@lixom.net; Andrew Chew; 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
>
> On Wed 2011-01-12 13:59:45, Rakesh Iyer wrote:
> > Hi all.
> >
> > I apologize for not specifying the PATCH indicator as "PATCH v4".
> >
> > Sorry for the inconvenience caused.
>
> Well, that is not huge problem, but short note of changes from last
> version would be welcome.
>
> AFAICT, FN handling was reworked...?
>
> > > + KEY_F10, KEY_F9, KEY_BACKSPACE, KEY_3,
> > > + KEY_2, KEY_UP, KEY_PRINT, KEY_PAUSE,
>
> But now I'm confused; does hardware have Fx keys (F10 above) or not?
> Pavel
>
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
2011-01-12 22:13 ` Rakesh Iyer
@ 2011-01-12 22:26 ` Pavel Machek
0 siblings, 0 replies; 9+ messages in thread
From: Pavel Machek @ 2011-01-12 22:26 UTC (permalink / raw)
To: Rakesh Iyer
Cc: tsoni@codeaurora.org, dmitry.torokhov@gmail.com,
shubhrajyoti@ti.com, ccross@android.com, konkers@android.com,
olof@lixom.net, Andrew Chew, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
Hi!
> Here are the list of changes. I was not sure of the protocol of adding this into the patch email.
>
Thanks for detailed list of changes. Usually, these go under "---".
> b) Removed the Fn keymap and its references. This obviated the need for some helper functions.
>
Thanks! Now if you could wrap emails at column 80, and you'll have
perfect patches :-).
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
@ 2011-01-05 1:03 riyer
2011-01-05 6:01 ` Shubhrajyoti
2011-01-05 8:36 ` Trilok Soni
0 siblings, 2 replies; 9+ messages in thread
From: riyer @ 2011-01-05 1:03 UTC (permalink / raw)
To: ccross, konkers
Cc: olof, riyer, achew, linux-tegra, linux-kernel, linux-input
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>
+#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);
+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);
+ } 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");
--
1.7.2.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
2011-01-05 1:03 riyer
@ 2011-01-05 6:01 ` Shubhrajyoti
2011-01-05 8:36 ` Trilok Soni
1 sibling, 0 replies; 9+ messages in thread
From: Shubhrajyoti @ 2011-01-05 6:01 UTC (permalink / raw)
To: riyer; +Cc: ccross, konkers, olof, achew, linux-tegra, linux-kernel,
linux-input
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");
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
2011-01-05 1:03 riyer
2011-01-05 6:01 ` Shubhrajyoti
@ 2011-01-05 8:36 ` Trilok Soni
2011-01-05 22:02 ` Rakesh Iyer
1 sibling, 1 reply; 9+ messages in thread
From: Trilok Soni @ 2011-01-05 8:36 UTC (permalink / raw)
To: riyer; +Cc: ccross, konkers, olof, achew, linux-tegra, linux-kernel,
linux-input
Hi Rakesh,
On 1/5/2011 6: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>
Thanks for the patch. Few comments.
> ---
> 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
Wrong mode of file.
>
> 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
no paths please
> + *
> + * 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"
This needs to be tristate.
> + 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.
> +
once you are adding tristate above, you need to put text like when this selected
as module etc; please refer other Kconfig options text.
> 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
no paths please
> +
> +#include <linux/module.h>
> +#include <linux/input.h>
> +#include <linux/platform_device.h>
> +#include <linux/kthread.h>
are you using anything related to thread? please audit this list of header
files includes and remove the one which are not used.
> +#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)
Use resource_size
> +
> +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;
const
> + 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);
> +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter);
> +
I don't understand why these are here, why can't we re-arrange them in such
a way that above is not required.
> +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);
> + } else {
> + tegra_kbc_close(kbc->idev);
May be you want to use input_dev->mutex here before making this decision and avoid
race between open and close with suspend/resume.
> +
> +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);
could you please explain why this disabling required? why can't we use spinlock?
> +
> + 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);
> + }
> + }
> +}
could you please explain this functionality in detail? Are you making only selectable
keys to be able to wakeup system and not all?
> +
> +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);
input_free_device is not required after input_unregister_device.
> + 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);
why can't we use the setup_timer and friends? do you really need hrtimers?
> + 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;
const
> + 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);
why can't this be configured as pdata? We may not need this to be always wakeup.
> + 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,
__devexit_p
> +#ifdef CONFIG_PM
> + .suspend = tegra_kbc_suspend,
> + .resume = tegra_kbc_resume,
> +#endif
> + .driver = {
> + .name = "tegra-kbc"
.owner
> + }
> +};
> +
> +static void __exit tegra_kbc_exit(void)
> +{
> + platform_driver_unregister(&tegra_kbc_driver);
> +}
> +
> +static int __devinit tegra_kbc_init(void)
__init should do
> +{
> + return platform_driver_register(&tegra_kbc_driver);
> +}
> +
> +module_exit(tegra_kbc_exit);
> +module_init(tegra_kbc_init);
I prefer to put init and exit with relevant functions.
> +
> +MODULE_DESCRIPTION("Tegra matrix keyboard controller driver");
MODULE_LICENSE, MODULE_AUTHOR, MODULE_ALIAS please.
---Trilok Soni
--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver
2011-01-05 8:36 ` Trilok Soni
@ 2011-01-05 22:02 ` Rakesh Iyer
0 siblings, 0 replies; 9+ messages in thread
From: Rakesh Iyer @ 2011-01-05 22:02 UTC (permalink / raw)
To: 'Trilok Soni'
Cc: ccross@android.com, konkers@android.com, olof@lixom.net,
Andrew Chew, linux-tegra@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-input@vger.kernel.org
Hello Trilok.
Thanks for the review.
Most of your comments are being worked on right now.
I have responses to a couple of your comments.
Comment 1
>
> > + 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);
> > +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter);
> > +
>
> I don't understand why these are here, why can't we re-arrange them in such
> a way that above is not required.
>
I am assuming the comment is to question the need for 'tegra_kbc_keycode'.
The keyboard driver can only determine the row and column of the keypress from the hardware and needs the translation to a value.
So we cannot get rid of the translation mechanism. This routine also allows a simpler processing of the function keymaps, so that the multiple callers are kept simple.
Comment 2
> > +
> > + 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);
> > + }
> > + }
> > +}
>
> could you please explain this functionality in detail? Are you making only selectable
> keys to be able to wakeup system and not all?
>
Yes, we are making only selectable keys wakeup the system. A 1 bit for a particular 'row,column' indicates that 'row,column' is disabled during suspend.
The keys that are allowed to wake the system are decided per board and are specified in pdata->wake_cfg.
Please let me know if this is okay. I will incorporate the remaining comments and send out a new patch after successful testing.
Thanks and Regards
Rakesh
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2011-01-12 22:26 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-01-12 21:52 [PATCH 1/1] input: tegra-kbc - Add tegra keyboard driver riyer
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
-- strict thread matches above, loose matches on Subject: below --
2011-01-05 1:03 riyer
2011-01-05 6:01 ` Shubhrajyoti
2011-01-05 8:36 ` Trilok Soni
2011-01-05 22:02 ` Rakesh Iyer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).