From 7871116830fe633b55d8104deddac603f003306a Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 4 May 2010 19:59:08 +0530 Subject: tegra kbc: Fixing android boot if key is kept press during boot. Following changes are done: - Created the new workqueue for the kbc scan process. On default workqueue, the msleep() was not causing the schedule() to booting process. - If there is no entry in fifo then wait for the next scan to complete before re-reading the number of entries in the fifo. if there is no entry then rep key released. - Fixed timing calculation and size of the event entry array. - Fixed the debaunce count calculation from debaunce time from odm. Tested on whistler, with normal boot as well as with continous key pressed d the entire boot time. Change-Id: I53b321ae6f5e1f425b51edcd63f7de4ae404e505 Reviewed-on: http://git-master/r/1238 Reviewed-by: Hu He Tested-by: Hu He Reviewed-by: Gary King --- arch/arm/mach-tegra/board_nvodm.c | 2 +- arch/arm/mach-tegra/odm_to_plat.c | 6 ++-- drivers/input/keyboard/tegra-kbc.c | 59 ++++++++++++++++++++++++++------------ 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/arch/arm/mach-tegra/board_nvodm.c b/arch/arm/mach-tegra/board_nvodm.c index d9eefc00db3f..f13f503a3683 100644 --- a/arch/arm/mach-tegra/board_nvodm.c +++ b/arch/arm/mach-tegra/board_nvodm.c @@ -430,7 +430,7 @@ static void kbc_init(void) struct tegra_kbc_plat *pdata = NULL; static struct resource res[2]; - dev = platform_device_alloc("tegra_kbc", -1); + dev = platform_device_alloc("tegra-kbc", -1); if (!dev) goto fail; diff --git a/arch/arm/mach-tegra/odm_to_plat.c b/arch/arm/mach-tegra/odm_to_plat.c index 7ccc982d1f25..209268572276 100644 --- a/arch/arm/mach-tegra/odm_to_plat.c +++ b/arch/arm/mach-tegra/odm_to_plat.c @@ -71,12 +71,14 @@ struct tegra_kbc_plat *tegra_kbc_odm_to_plat(void) NvOdmKbcGetParameter(NvOdmKbcParameter_DebounceTime, 1, &temp); - pdata->debounce_cnt = temp; + /* debounce time is reported from ODM in milliseconds, + * but needs to be specified in 32KHz ticks */ + pdata->debounce_cnt = temp *32; /* repeat cycle is reported from ODM in milliseconds, * but needs to be specified in 32KHz ticks */ NvOdmKbcGetParameter(NvOdmKbcParameter_RepeatCycleTime, 1, &temp); - pdata->repeat_cnt = temp * 4096 / 125; + pdata->repeat_cnt = temp * 32; temp = NvOdmPeripheralEnumerate(&srch_attr, &srch_val, 1, &guid, 1); if (!temp) { diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 1a5768fb602f..efd584dcd125 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -59,6 +59,7 @@ struct tegra_kbc { unsigned int repoll_time; struct tegra_kbc_plat *pdata; struct work_struct key_repeat; + struct workqueue_struct *kbc_work_queue; #ifdef RM_SUPPORT NvU32 client_id; #else @@ -208,7 +209,7 @@ static int tegra_kbc_resume(struct platform_device *pdev) static void tegra_kbc_report_keys(struct tegra_kbc *kbc, int *fifo) { int curr_fifo[KBC_MAX_KPENT]; - u32 kp_ent_val[(KBC_MAX_KPENT*8 + 3) / 4]; + u32 kp_ent_val[(KBC_MAX_KPENT + 3) / 4]; u32 *kp_ents = kp_ent_val; u32 kp_ent; unsigned long flags; @@ -273,13 +274,13 @@ static void tegra_kbc_key_repeat(struct work_struct *work) if (!val) { /* release any pressed keys and exit the loop */ for (i=0; iidev, fifo[i], 0); } break; } tegra_kbc_report_keys(kbc, fifo); - /* FIXME: why is this here? */ msleep((val==1) ? kbc->repoll_time : 1); } @@ -296,6 +297,7 @@ static void tegra_kbc_close(struct input_dev *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); @@ -373,18 +375,6 @@ static int tegra_kbc_open(struct input_dev *dev) 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); - } - writel(0x7, kbc->mmio + KBC_INT_0); - spin_unlock_irqrestore(&kbc->lock, flags); - writel(kbc->pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); val = kbc->pdata->debounce_cnt << 4; @@ -393,6 +383,22 @@ static int tegra_kbc_open(struct input_dev *dev) val |= 1; /* enable */ writel(val, kbc->mmio + KBC_CONTROL_0); + /* 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; } @@ -410,6 +416,7 @@ static int __devexit tegra_kbc_remove(struct platform_device *pdev) input_unregister_device(kbc->idev); input_free_device(kbc->idev); iounmap(kbc->mmio); + destroy_workqueue(kbc->kbc_work_queue); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, res_size(res)); @@ -438,8 +445,7 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args) writel(ctl, kbc->mmio + KBC_CONTROL_0); return IRQ_HANDLED; } - - schedule_work(&kbc->key_repeat); + queue_work(kbc->kbc_work_queue, &kbc->key_repeat); return IRQ_HANDLED; } @@ -454,6 +460,7 @@ static int __init tegra_kbc_probe(struct platform_device *pdev) int cols[KBC_MAX_COL]; int i, j; int nr = 0; + char name[64]; if (!pdata) return -EINVAL; @@ -534,8 +541,10 @@ static int __init tegra_kbc_probe(struct platform_device *pdev) } } + /* The debaunce count is maximum 0x3FF clocks i.e. 10 bit configuration */ + pdata->debounce_cnt = (pdata->debounce_cnt > 0x3FF)?0x3FF:pdata->debounce_cnt; kbc->repoll_time = 5 + (16+pdata->debounce_cnt)*nr + pdata->repeat_cnt; - kbc->repoll_time = (kbc->repoll_time*1000 + 16384) / 32768; + kbc->repoll_time = (kbc->repoll_time + 31) / 32; kbc->idev->evbit[0] = BIT_MASK(EV_KEY); @@ -550,9 +559,20 @@ static int __init tegra_kbc_probe(struct platform_device *pdev) } } + + /* create the workqueue for the kbc path */ + snprintf(name, sizeof(name), "tegra-kbc"); + kbc->kbc_work_queue = create_singlethread_workqueue(name); + if (kbc->kbc_work_queue == NULL) { + dev_err(&pdev->dev, "Failed to create work queue\n"); + err = -ENODEV; + goto fail; + } + /* 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"); @@ -574,6 +594,7 @@ fail: if (kbc->idev) input_free_device(kbc->idev); free_resource(kbc); if (kbc->mmio) iounmap(kbc->mmio); + if (kbc->kbc_work_queue) destroy_workqueue(kbc->kbc_work_queue); kfree(kbc); return err; } @@ -586,7 +607,7 @@ static struct platform_driver tegra_kbc_driver = { .resume = tegra_kbc_resume, #endif .driver = { - .name = "tegra_kbc" + .name = "tegra-kbc" } }; -- cgit v1.2.3