summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorGary King <gking@nvidia.com>2010-03-22 17:29:20 -0700
committerGary King <gking@nvidia.com>2010-03-23 10:42:58 -0800
commitb488d3228e76fb434010ff5e3f1044ce62196217 (patch)
tree13588762a236191c06092cd85f71b4342c63bfca /drivers
parent854209499f803b7670599c720a9cf1b154e7334c (diff)
input: add keyboard controller support for tegra
implement a keyboard input driver for tegra's internal matrix keyboard controller (16x8 support on Tegra 2 class processors). supports * power management * limit wakeup to a subset of keys * platform-defined remapping of key to keycode * platform-defined pin configuration add support for converting tegra ODM kit APIs to kbc platform data, so existing clients will continue to function Change-Id: If1496e7ada4f6f18a3e98a15ebc5f925f254bf65 Reviewed-on: http://git-master/r/933 Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com> Tested-by: Gary King <gking@nvidia.com> Reviewed-by: Gary King <gking@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/keyboard/tegra-kbc.c677
1 files changed, 493 insertions, 184 deletions
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index ea400bdeadca..1a5768fb602f 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -4,7 +4,7 @@
* Keyboard class input driver for the NVIDIA Tegra SoC internal matrix
* keyboard controller
*
- * Copyright (c) 2009, NVIDIA Corporation.
+ * 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
@@ -21,277 +21,586 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#define RM_SUPPORT
+
#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/regulator/consumer.h>
+#include <linux/clk.h>
+#include <mach/kbc.h>
+
+#ifdef RM_SUPPORT
+#include <linux/tegra_devices.h>
#include <mach/nvrm_linux.h>
-#include <nvddk_kbc.h>
-#include <nvos.h>
-#include <nvodm_kbc_keymapping.h>
+#include "nvrm_power.h"
+#endif
+
+#define KBC_CONTROL_0 0
+#define KBC_INT_0 4
+#define KBC_ROW_CFG0_0 8
+#define KBC_COL_CFG0_0 0x18
+#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;
+ spinlock_t lock;
+ unsigned int repoll_time;
+ struct tegra_kbc_plat *pdata;
+ struct work_struct key_repeat;
+#ifdef RM_SUPPORT
+ NvU32 client_id;
+#else
+ struct clk *clk;
+ struct regulator *reg;
+#endif
+};
+
+#ifdef RM_SUPPORT
+static int alloc_resource(struct tegra_kbc *kbc, struct platform_device *pdev)
+{
+ NvError e = NvRmPowerRegister(s_hRmGlobal, NULL, &kbc->client_id);
+ if (e!=NvSuccess) return -ENXIO;
+ return 0;
+}
+
+static void free_resource(struct tegra_kbc *kbc)
+{
+ NvRmPowerUnRegister(s_hRmGlobal, kbc->client_id);
+}
+
+static void enable_power(struct tegra_kbc *kbc)
+{
+ NvError e;
+ e = NvRmPowerVoltageControl(s_hRmGlobal,
+ NVRM_MODULE_ID(NvRmModuleID_Kbc, 0),
+ kbc->client_id, NvRmVoltsUnspecified,
+ NvRmVoltsUnspecified, NULL, 0, NULL);
+ BUG_ON(e!=NvSuccess);
+}
+
+static void disable_power(struct tegra_kbc *kbc)
+{
+ NvError e;
+ e = NvRmPowerVoltageControl(s_hRmGlobal,
+ NVRM_MODULE_ID(NvRmModuleID_Kbc, 0),
+ kbc->client_id, NvRmVoltsOff,
+ NvRmVoltsOff, NULL, 0, NULL);
+ BUG_ON(e!=NvSuccess);
+}
+
+static void enable_clock(struct tegra_kbc *kbc)
+{
+ NvError e;
+ e = NvRmPowerModuleClockControl(s_hRmGlobal,
+ NVRM_MODULE_ID(NvRmModuleID_Kbc,0), 0, NV_TRUE);
+ BUG_ON(e!=NvSuccess);
+ NvRmModuleReset(s_hRmGlobal, NVRM_MODULE_ID(NvRmModuleID_Kbc, 0));
+}
+static void disable_clock(struct tegra_kbc *kbc)
+{
+ NvRmPowerModuleClockControl(s_hRmGlobal,
+ NVRM_MODULE_ID(NvRmModuleID_Kbc,0), 0, NV_FALSE);
+}
-static int tegra_kbc_event(struct input_dev *dev, unsigned int type,
- unsigned int code, int value)
+#else
+static int alloc_resource(struct tegra_kbc *kbc, struct platform_device *pdev)
{
+ kbc->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(kbc->clk)) {
+ int err;
+ dev_err(&pdev->dev, "failed to get keypad clock\n");
+ err = PTR_ERR(kbc->clk);
+ kbc->clk = NULL;
+ return err;
+ }
+ kbc->reg = regulator_get(&pdev->dev, "Vcc");
+ if (IS_ERR(kbc->reg)) {
+ dev_err(&pdev->dev, "no regulator support\n");
+ kbc->reg = NULL;
+ }
return 0;
}
-struct NvOdmKeyVirtTableDetail **key_tab = NULL;
-NvU32 key_tab_total = 0;
+static void free_resource(struct tegra_kbc *kbc)
+{
+ if (kbc->clk) clk_put(kbc->clk);
+ if (kbc->reg) regulator_put(kbc->reg);
+}
-struct tegra_kbc_driver_data {
- struct input_dev *input_dev;
- struct task_struct *task;
- NvOsSemaphoreHandle semaphore;
- NvDdkKbcHandle ddkHandle;
- int done;
-};
+static void enable_power(struct tegra_kbc *kbc)
+{
+ if (kbc->reg) regulator_enable(kbc->reg);
+}
-#define in_table(_code, _tabl) \
- (((_code)>=(_tabl)->StartScanCode) && ((_code)<=(_tabl)->EndScanCode))
+static void disable_power(struct tegra_kbc *kbc)
+{
+ if (kbc->reg) regulator_disable(kbc->reg);
+}
-#define table_size(_tabl) ((_tabl)->EndScanCode - (_tabl)->StartScanCode + 1)
+static void enable_clock(struct tegra_kbc *kbc)
+{
+ clk_enable(kbc->clk);
+}
-static NvU32 tegra_kbc_handle_keyev(struct tegra_kbc_driver_data *kbc)
+static void disable_clock(struct tegra_kbc *kbc)
{
- NvDdkKbcKeyEvent key_ev[16];
- NvU32 codes[16];
- NvU32 k_idx;
- NvU32 l_idx;
- NvU32 EventCount;
- NvU32 WaitTime;
- NvU32 i;
- NvU32 value;
- NvU32 key;
-
- WaitTime = NvDdkKbcGetKeyEvents(kbc->ddkHandle,
- &EventCount, codes, key_ev);
-
- for (i = 0; i < EventCount; i++) {
- if (key_ev[i] == NvDdkKbcKeyEvent_KeyPress) {
- value = 1;
- } else if (key_ev[i] == NvDdkKbcKeyEvent_KeyRelease) {
- value = 0;
- } else
- continue;
+ clk_disable(kbc->clk);
+}
+#endif
- key = 0;
+static int tegra_kbc_keycode(const struct tegra_kbc *kbc, int r, int c) {
+ unsigned int i = kbc_indexof(r,c);
- for (l_idx = 0; l_idx < key_tab_total; ++l_idx) {
- if (!in_table(codes[i], key_tab[l_idx]))
- continue;
- k_idx = codes[i] - key_tab[l_idx]->StartScanCode;
- key = key_tab[l_idx]->pVirtualKeyTable[k_idx];
- }
+ if (kbc->pdata->keymap)
+ return kbc->pdata->keymap[i];
- input_report_key(kbc->input_dev, key, value);
- }
+ return i;
+}
+
+#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);
- if (WaitTime) {
- NvOsSleepMS(WaitTime);
+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);
+ disable_power(kbc);
+ } else {
+ tegra_kbc_close(kbc->idev);
}
- return WaitTime;
+ return 0;
}
-static int tegra_kbc_thread(void *pdata)
+static int tegra_kbc_resume(struct platform_device *pdev)
{
- struct tegra_kbc_driver_data *kbc = pdata;
- NvU32 loop;
+ struct tegra_kbc *kbc = platform_get_drvdata(pdev);
- for (;;) {
- /* FIXME should we use a NvOsSemaphoreWaitTimeout instead? */
- NvOsSemaphoreWait(kbc->semaphore);
- if (kbc->done)
- break;
- do {
- loop = tegra_kbc_handle_keyev(kbc);
- } while (loop);
- }
+ if (device_may_wakeup(&pdev->dev)) {
+ enable_power(kbc);
+ 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_cleanup(struct tegra_kbc_driver_data *kbc)
+static void tegra_kbc_report_keys(struct tegra_kbc *kbc, int *fifo)
{
- if (!kbc)
- return;
+ int curr_fifo[KBC_MAX_KPENT];
+ u32 kp_ent_val[(KBC_MAX_KPENT*8 + 3) / 4];
+ u32 *kp_ents = kp_ent_val;
+ u32 kp_ent;
+ unsigned long flags;
+ int i, j, valid=0;
+
+ 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);
+
+ for (i=0; i<KBC_MAX_KPENT; i++) {
+ if (!(i&3)) kp_ent=*kp_ents++;
+
+ if (kp_ent & 0x80) {
+ int c = kp_ent & 0x7;
+ int r = (kp_ent >> 3) & 0xf;
+ int k = tegra_kbc_keycode(kbc, r, c);
+ if (likely(k!=-1)) curr_fifo[valid++] = k;
+ }
+ kp_ent >>= 8;
+ }
- if (kbc->task) {
- kbc->done = 1;
- NvOsSemaphoreSignal(kbc->semaphore);
- kthread_stop(kbc->task);
+ 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;
+ }
}
- if (kbc->ddkHandle) {
- NvDdkKbcStop(kbc->ddkHandle);
- NvDdkKbcClose(kbc->ddkHandle);
+ 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);
}
+}
- if (kbc->semaphore)
- NvOsSemaphoreDestroy(kbc->semaphore);
-
- if (kbc->input_dev)
- input_free_device(kbc->input_dev);
+static void tegra_kbc_key_repeat(struct work_struct *work)
+{
+ struct tegra_kbc *kbc;
+ unsigned long flags;
+ u32 val;
+ int fifo[KBC_MAX_KPENT];
+ int i;
+
+ kbc = container_of(work, struct tegra_kbc, key_repeat);
+ for (i=0; i<ARRAY_SIZE(fifo); i++) fifo[i] = -1;
+
+ while (1) {
+ val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf;
+ if (!val) {
+ /* release any pressed keys and exit the loop */
+ for (i=0; i<ARRAY_SIZE(fifo); i++) {
+ if (fifo[i]==-1) continue;
+ input_report_key(kbc->idev, fifo[i], 0);
+ }
+ break;
+ }
+ tegra_kbc_report_keys(kbc, fifo);
+ /* FIXME: why is this here? */
+ msleep((val==1) ? kbc->repoll_time : 1);
+ }
- kfree(kbc);
+ 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);
}
-static int __init tegra_kbc_probe(struct platform_device *pdev)
+static void tegra_kbc_close(struct input_dev *dev)
{
- struct tegra_kbc_driver_data *kbc = NULL;
- struct input_dev *input_dev = NULL;
- int err;
- NvError nverr;
- NvU32 l_idx;
- NvU32 k_idx;
- NvU32 TotalKey;
+ struct tegra_kbc *kbc = input_get_drvdata(dev);
+ unsigned long flags;
+ u32 val;
- kbc = kzalloc(sizeof(struct tegra_kbc_driver_data), GFP_KERNEL);
- input_dev = input_allocate_device();
+ val = readl(kbc->mmio + KBC_CONTROL_0);
+ val &= ~1;
+ writel(val, kbc->mmio + KBC_CONTROL_0);
+ spin_unlock_irqrestore(&kbc->lock, flags);
- if (input_dev == NULL || kbc == NULL) {
- input_free_device(input_dev);
- kfree(kbc);
- err = -ENOMEM;
- pr_err("tegra_kbc_probe: Failed to allocate input device\n");
- return err;
- }
- nverr = NvOsSemaphoreCreate(&kbc->semaphore, 0);
- if (nverr != NvSuccess) {
- err = -1;
- pr_err("tegra_kbc_probe: Semaphore creation failed\n");
- goto fail;
- }
+ disable_clock(kbc);
+ disable_power(kbc);
+}
- nverr = NvDdkKbcOpen (s_hRmGlobal, &kbc->ddkHandle);
- if (nverr != NvSuccess) {
- err = -1;
- pr_err("tegra_kbc_probe: NvDdkKbcOpen failed\n");
- goto fail;
+#ifdef CONFIG_ARCH_TEGRA_1x_SOC
+#define tegra_kbc_setup_wakekeys(kbc, filter) do { } while (0)
+#else
+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);
+ }
}
+}
+#endif
- nverr = NvDdkKbcStart(kbc->ddkHandle, kbc->semaphore);
- if (nverr != NvSuccess) {
- err = -1;
- pr_err("tegra_kbc_probe: NvDdkKbcStart failed\n");
- goto fail;
+static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
+{
+ const struct tegra_kbc_plat *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);
}
+}
- kbc->task = kthread_create(tegra_kbc_thread, kbc, "tegra_kbc_thread");
- if(kbc->task == NULL) {
- err = -1;
- goto fail;
+static int tegra_kbc_open(struct input_dev *dev)
+{
+ struct tegra_kbc *kbc = input_get_drvdata(dev);
+ unsigned long flags;
+ u32 val = 0;
+
+ enable_power(kbc);
+ enable_clock(kbc);
+
+ tegra_kbc_config_pins(kbc);
+ tegra_kbc_setup_wakekeys(kbc, false);
+
+ /* atomically clear out any remaining entries in the key FIFO
+ * and enable keyboard interrupts */
+ spin_lock_irqsave(&kbc->lock, flags);
+ 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);
}
- wake_up_process( kbc->task );
+ writel(0x7, kbc->mmio + KBC_INT_0);
+ spin_unlock_irqrestore(&kbc->lock, flags);
- kbc->input_dev = input_dev;
- input_dev->event = tegra_kbc_event;
- input_dev->name = "tegra-kbc";
- __set_bit(EV_KEY, input_dev->evbit);
+ writel(kbc->pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
- key_tab_total = NvOdmKbcKeyMappingGetVirtualKeyMappingList(&key_tab);
+ 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);
- for (l_idx = 0; l_idx < key_tab_total; ++l_idx) {
- TotalKey = table_size(key_tab[l_idx]);
- for (k_idx = 0; k_idx < TotalKey; ++k_idx) {
- __set_bit(key_tab[l_idx]->pVirtualKeyTable[k_idx],
- input_dev->keybit);
- }
- }
+ return 0;
+}
- platform_set_drvdata(pdev, kbc);
- err = input_register_device(input_dev);
- if (err) {
- pr_err("tegra_kbc_probe: Unable to register %s input device\n",
- input_dev->name);
- goto fail;
- }
+static int __devexit tegra_kbc_remove(struct platform_device *pdev)
+{
+ struct tegra_kbc *kbc = platform_get_drvdata(pdev);
+ struct resource *res;
- return 0;
+ free_irq(kbc->irq, pdev);
+ disable_clock(kbc);
+ disable_power(kbc);
+ free_resource(kbc);
-fail:
- tegra_kbc_cleanup(kbc);
- return err;
+ 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 int tegra_kbc_remove(struct platform_device *pdev)
+static irqreturn_t tegra_kbc_isr(int irq, void *args)
{
- struct tegra_kbc_driver_data *kbc = platform_get_drvdata(pdev);
- tegra_kbc_cleanup(kbc);
- return 0;
+ 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_work(&kbc->key_repeat);
+ return IRQ_HANDLED;
}
-static int tegra_kbc_suspend(struct platform_device *pdev, pm_message_t state)
+static int __init tegra_kbc_probe(struct platform_device *pdev)
{
- struct tegra_kbc_driver_data *kbc = platform_get_drvdata(pdev);
- NvError e = NvError_Success;
+ struct tegra_kbc *kbc;
+ struct tegra_kbc_plat *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;
- if (!kbc)
- return -1;
+ kbc->pdata = pdata;
+ kbc->irq = -EINVAL;
- if (!kbc->ddkHandle) {
- printk("%s: device handle is NULL\n", __func__);
- return -1;
+ 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;
+ }
+ err = alloc_resource(kbc, pdev);
+ if (err) goto fail;
- /* power down hardware */
- e = NvDdkKbcSuspend(kbc->ddkHandle);
- if (e != NvSuccess) {
- printk("%s: hardware power down fail\n", __func__);
- return -1;
+ 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;
+ }
}
- return 0;
-}
+ kbc->repoll_time = 5 + (16+pdata->debounce_cnt)*nr + pdata->repeat_cnt;
+ kbc->repoll_time = (kbc->repoll_time*1000 + 16384) / 32768;
-static int tegra_kbc_resume(struct platform_device *pdev)
-{
- struct tegra_kbc_driver_data *kbc = platform_get_drvdata(pdev);
- NvError e = NvError_Success;
+ kbc->idev->evbit[0] = BIT_MASK(EV_KEY);
- if (!kbc)
- return -1;
+ 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;
+ keycode = tegra_kbc_keycode(kbc, j, i);
+ if (keycode==-1) continue;
+ set_bit(keycode, kbc->idev->keybit);
+ }
+ }
- if (!kbc->ddkHandle) {
- printk("%s: device handle is NULL\n", __func__);
- return -1;
+ /* keycode FIFO needs to be read atomically; leave local
+ * interrupts disabled when handling KBC interrupt */
+ INIT_WORK(&kbc->key_repeat, tegra_kbc_key_repeat);
+ err = request_irq(irq, tegra_kbc_isr, IRQF_DISABLED, pdev->name, kbc);
+ if (err) {
+ dev_err(&pdev->dev, "failed to request keypad IRQ\n");
+ goto fail;
}
+ kbc->irq = irq;
- /* power up hardware */
- e = NvDdkKbcResume(kbc->ddkHandle);
- if (e != NvSuccess) {
- printk("%s: hardware power up fail\n", __func__);
- return -1;
+ 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);
+ free_resource(kbc);
+ 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,
- .driver = {
- .name = "tegra_kbc",
- },
+#endif
+ .driver = {
+ .name = "tegra_kbc"
+ }
};
-static int __devinit tegra_kbc_init(void)
+static void __exit tegra_kbc_exit(void)
{
- return platform_driver_register(&tegra_kbc_driver);
+ platform_driver_unregister(&tegra_kbc_driver);
}
-static void __exit tegra_kbc_exit(void)
+static int __devinit tegra_kbc_init(void)
{
- platform_driver_unregister(&tegra_kbc_driver);
+ return platform_driver_register(&tegra_kbc_driver);
}
-module_init(tegra_kbc_init);
module_exit(tegra_kbc_exit);
+module_init(tegra_kbc_init);
-MODULE_DESCRIPTION("Tegra Key board controller driver");
-
+MODULE_DESCRIPTION("Tegra matrix keyboard controller driver");