From 1877da5d3d5c0eb79594b815d132e6d626e4df2c Mon Sep 17 00:00:00 2001 From: kfx Date: Thu, 18 Nov 2010 19:53:04 +0800 Subject: [PATCH] add key(gpio and adc) drivers --- arch/arm/mach-rk29/Makefile | 2 +- arch/arm/mach-rk29/board-rk29sdk.c | 23 +- arch/arm/mach-rk29/include/mach/key.h | 30 ++ arch/arm/mach-rk29/key.c | 95 ++++++ drivers/adc/core.c | 6 +- drivers/input/keyboard/Kconfig | 6 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/rk29_keys.c | 407 ++++++++++++++++++++++++++ 8 files changed, 565 insertions(+), 5 deletions(-) create mode 100755 arch/arm/mach-rk29/include/mach/key.h create mode 100755 arch/arm/mach-rk29/key.c create mode 100755 drivers/input/keyboard/rk29_keys.c diff --git a/arch/arm/mach-rk29/Makefile b/arch/arm/mach-rk29/Makefile index e9a3e38df099..486c4533338a 100644 --- a/arch/arm/mach-rk29/Makefile +++ b/arch/arm/mach-rk29/Makefile @@ -1,3 +1,3 @@ -obj-y += timer.o io.o devices.o iomux.o clock.o rk29-pl330.o dma.o gpio.o +obj-y += timer.o io.o devices.o iomux.o clock.o rk29-pl330.o dma.o gpio.o key.o obj-$(CONFIG_RK29_VPU) += vpu.o obj-$(CONFIG_MACH_RK29SDK) += board-rk29sdk.o diff --git a/arch/arm/mach-rk29/board-rk29sdk.c b/arch/arm/mach-rk29/board-rk29sdk.c index dd73e752951d..8e9b8fdcd79f 100755 --- a/arch/arm/mach-rk29/board-rk29sdk.c +++ b/arch/arm/mach-rk29/board-rk29sdk.c @@ -39,7 +39,6 @@ #include #include - #include #include @@ -416,6 +415,14 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = { #ifdef CONFIG_I2C1_RK29 static struct i2c_board_info __initdata board_i2c1_devices[] = { +#if defined (CONFIG_RK1000_CONTROL1) + { + .type = "rk1000_control", + .addr = 0x40, + .flags = 0, + }, +#endif + }; #endif @@ -583,6 +590,16 @@ struct platform_device rk29_device_gpu = { .resource = resources_gpu, }; #endif +#ifdef CONFIG_KEYS_RK29 +extern struct rk29_keys_platform_data rk29_keys_pdata; +static struct platform_device rk29_device_keys = { + .name = "rk29-keys", + .id = -1, + .dev = { + .platform_data = &rk29_keys_pdata, + }, +}; +#endif static void __init rk29_board_iomux_init(void) { @@ -639,7 +656,9 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_I2C3_RK29 &rk29_device_i2c3, #endif - +#ifdef CONFIG_KEYS_RK29 + &rk29_device_keys, +#endif #ifdef CONFIG_SDMMC0_RK29 &rk29_device_sdmmc0, #endif diff --git a/arch/arm/mach-rk29/include/mach/key.h b/arch/arm/mach-rk29/include/mach/key.h new file mode 100755 index 000000000000..5630497a2c5f --- /dev/null +++ b/arch/arm/mach-rk29/include/mach/key.h @@ -0,0 +1,30 @@ +#ifndef __RK29_KEYS_H__ +#define __RK29_KEYS_H__ +#include + +#define DEFAULT_DEBOUNCE_INTERVAL 10 //10ms +#define LONG_PRESS_COUNT 100 //100 * 10 = 1000ms +#define ONE_SEC_COUNT (1000/DEFAULT_DEBOUNCE_INTERVAL) + +#define ADC_SAMPLE_TIME 10 + +struct rk29_keys_button { + int code; + int code_long_press; + int gpio; + int adc_value; + int adc_state; + int active_low; + char *desc; + int wakeup; +}; + + +struct rk29_keys_platform_data { + struct rk29_keys_button *buttons; + int nbuttons; + int chn; + int rep; +}; + +#endif diff --git a/arch/arm/mach-rk29/key.c b/arch/arm/mach-rk29/key.c new file mode 100755 index 000000000000..8afc28f0d92b --- /dev/null +++ b/arch/arm/mach-rk29/key.c @@ -0,0 +1,95 @@ +#include +#include + +#define EV_ENCALL KEY_F4 +#define EV_MENU KEY_F1 + +#define PRESS_LEV_LOW 1 +#define PRESS_LEV_HIGH 0 + +static struct rk29_keys_button key_button[] = { +#ifdef CONFIG_MACH_RK29SDK + [0] = { + .desc = "vol+", + .code = KEY_VOLUMEDOWN, + .gpio = RK29_PIN0_PB0, + .active_low = PRESS_LEV_LOW, + }, + [1] = { + .desc = "vol-", + .code = KEY_VOLUMEUP, + .gpio = RK29_PIN0_PB1, + .active_low = PRESS_LEV_LOW, + }, + [2] = { + .desc = "menu", + .code = EV_MENU, + .gpio = RK29_PIN0_PB2, + .active_low = PRESS_LEV_LOW, + }, + [3] = { + .desc = "home", + .code = KEY_HOME, + .code_long_press = KEY_F4, + .gpio = RK29_PIN0_PB3, + .active_low = PRESS_LEV_LOW, + }, + [4] = { + .desc = "back", + .code = KEY_BACK, + .gpio = RK29_PIN0_PB4, + .active_low = PRESS_LEV_LOW, + }, + [5] = { + .desc = "esc", + .code = KEY_ESC, + .gpio = RK29_PIN0_PB5, + .active_low = PRESS_LEV_LOW, + }, +#if 0 + [6] = { + .desc = "vol+", + .code = KEY_VOLUMEDOWN, + .adc_value = 95, + .active_low = PRESS_LEV_LOW, + }, + [7] = { + .desc = "vol-", + .code = KEY_VOLUMEUP, + .adc_value = 249, + .active_low = PRESS_LEV_LOW, + }, + [8] = { + .desc = "menu", + .code = EV_MENU, + .adc_value = 406, + .active_low = PRESS_LEV_LOW, + }, + [9] = { + .desc = "home", + .code = KEY_HOME, + .code_long_press = KEY_F4, + .adc_value = 561, + .active_low = PRESS_LEV_LOW, + }, + [10] = { + .desc = "back", + .code = KEY_BACK, + .adc_value = 726, + .active_low = PRESS_LEV_LOW, + }, + [11] = { + .desc = "esc", + .code = KEY_ESC, + .adc_value = 899, + .active_low = PRESS_LEV_LOW, + }, +#endif +#endif +}; +struct rk29_keys_platform_data rk29_keys_pdata = { + .buttons = key_button, + .nbuttons = ARRAY_SIZE(key_button), + .chn = -1, //chn: 0-7, if do not use ADC,set 'chn' -1 +}; + diff --git a/drivers/adc/core.c b/drivers/adc/core.c index 97704207d596..9ad1dae2e6ff 100755 --- a/drivers/adc/core.c +++ b/drivers/adc/core.c @@ -125,7 +125,7 @@ adc_sync_read_callback(struct adc_client *client, void *param, int result) int adc_sync_read(struct adc_client *client) { struct adc_request *req = NULL; - int err; + int err, tmo; if(client == NULL) { printk(KERN_ERR "client point is NULL"); @@ -153,7 +153,9 @@ int adc_sync_read(struct adc_client *client) kfree(req); return err; } - wait_for_completion(&req->completion); + tmo = wait_for_completion_timeout(&req->completion,msecs_to_jiffies(100)); + if(tmo == 0) + return -ETIMEDOUT; return client->result; } EXPORT_SYMBOL(adc_sync_read); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 1e1ac840d559..eae1a07310fc 100755 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -11,6 +11,12 @@ menuconfig INPUT_KEYBOARD If unsure, say Y. if INPUT_KEYBOARD +config KEYS_RK29 + tristate "rk29 keyboard" + depends on ARCH_RK29 + default y + help + rk29 keyboard drivers(gpio and adc) config KEYBOARD_AAED2000 tristate "AAED-2000 keyboard" diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index cc99b8c69030..afbeafdb2385 100755 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -4,6 +4,7 @@ # Each configuration option enables a list of files. +obj-$(CONFIG_KEYS_RK29) += rk29_keys.o obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o diff --git a/drivers/input/keyboard/rk29_keys.c b/drivers/input/keyboard/rk29_keys.c new file mode 100755 index 000000000000..60ae7fa1ccc8 --- /dev/null +++ b/drivers/input/keyboard/rk29_keys.c @@ -0,0 +1,407 @@ +/* + * Driver for keys on GPIO lines capable of generating interrupts. + * + * Copyright 2005 Phil Blundell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define EMPTY_ADVALUE 950 +#define DRIFT_ADVALUE 70 +#define INVALID_ADVALUE 10 + + +#if 1 +#define key_dbg(bdata, format, arg...) \ + dev_printk(KERN_INFO , &bdata->input->dev , format , ## arg) +#else +#define key_dbg(bdata, format, arg...) +#endif + +struct rk29_button_data { + int state; + int long_press_count; + struct rk29_keys_button *button; + struct input_dev *input; + struct timer_list timer; + struct work_struct work; +}; + +struct rk29_keys_drvdata { + int nbuttons; + struct input_dev *input; + struct adc_client *client; + struct timer_list timer; + struct rk29_button_data data[0]; +}; + +static void keys_do_something(struct work_struct *work) +{ + struct rk29_button_data *bdata = + container_of(work, struct rk29_button_data, work); + + key_dbg(bdata, "do what?\n"); + return; +} + +static void keys_long_press_timer(unsigned long _data) +{ + int state; + struct rk29_button_data *bdata = (struct rk29_button_data *)_data; + struct rk29_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; + unsigned int type = EV_KEY; + if(button->gpio) + state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low); + else + state = !!button->adc_state; + if(state) { + if(bdata->long_press_count != 0) { + if(bdata->long_press_count % (LONG_PRESS_COUNT+ONE_SEC_COUNT) == 0){ + key_dbg(bdata, "key[%s]: report ev[%d] state[0]\n", button->desc, button->code_long_press); + input_event(input, type, button->code_long_press, 0); + input_sync(input); + } + else if(bdata->long_press_count%LONG_PRESS_COUNT == 0) { + key_dbg(bdata, "key[%s]: report ev[%d] state[1]\n", button->desc, button->code_long_press); + input_event(input, type, button->code_long_press, 1); + input_sync(input); + } + } + bdata->long_press_count++; + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL)); + } + else { + if(bdata->long_press_count <= LONG_PRESS_COUNT) { + bdata->long_press_count = 0; + key_dbg(bdata, "key[%s]: report ev[%d] state[1], report ev[%d] state[0]\n", + button->desc, button->code, button->code); + input_event(input, type, button->code, 1); + input_sync(input); + input_event(input, type, button->code, 0); + input_sync(input); + } + else { + if(bdata->state != state) + key_dbg(bdata, "key[%s]: report ev[%d] state[0]\n", button->desc, button->code_long_press); + input_event(input, type, button->code_long_press, 0); + input_sync(input); + } + } + bdata->state = state; +} +static void keys_timer(unsigned long _data) +{ + int state; + struct rk29_button_data *bdata = (struct rk29_button_data *)_data; + struct rk29_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; + unsigned int type = EV_KEY; + + if(button->gpio) + state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low); + else + state = !!button->adc_state; + if(bdata->state != state) { + bdata->state = state; + key_dbg(bdata, "key[%s]: report ev[%d] state[%d]\n", button->desc, button->code, bdata->state); + input_event(input, type, button->code, bdata->state); + input_sync(input); + } + if(state) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL)); +} + +static irqreturn_t keys_isr(int irq, void *dev_id) +{ + struct rk29_button_data *bdata = dev_id; + struct rk29_keys_button *button = bdata->button; + BUG_ON(irq != gpio_to_irq(button->gpio)); + if(button->wakeup) + schedule_work(&bdata->work); + bdata->long_press_count = 0; + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(DEFAULT_DEBOUNCE_INTERVAL)); + return IRQ_HANDLED; +} +static void callback(struct adc_client *client, void *client_param, int result) +{ + struct rk29_keys_drvdata *ddata = (struct rk29_keys_drvdata *)client_param; + int i; + for (i = 0; i < ddata->nbuttons; i++) { + struct rk29_button_data *bdata = &ddata->data[i]; + struct rk29_keys_button *button = bdata->button; + if(!button->adc_value) + continue; + if(result < button->adc_value + DRIFT_ADVALUE && + result > button->adc_value - DRIFT_ADVALUE) + button->adc_state = 1; + else + button->adc_state = 0; + + } + return; +} +static void adc_timer(unsigned long _data) +{ + struct rk29_keys_drvdata *ddata = (struct rk29_keys_drvdata *)_data; + + adc_async_read(ddata->client); + mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(ADC_SAMPLE_TIME)); +} + +static int __devinit keys_probe(struct platform_device *pdev) +{ + struct rk29_keys_platform_data *pdata = pdev->dev.platform_data; + struct rk29_keys_drvdata *ddata; + struct input_dev *input; + int i, error = 0; + int wakeup = 0; + + if(!pdata) + return -EINVAL; + + ddata = kzalloc(sizeof(struct rk29_keys_drvdata) + + pdata->nbuttons * sizeof(struct rk29_button_data), + GFP_KERNEL); + input = input_allocate_device(); + if (!ddata || !input) { + error = -ENOMEM; + goto fail0; + } + + platform_set_drvdata(pdev, ddata); + + input->name = pdev->name; + input->phys = "gpio-keys/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + ddata->nbuttons = pdata->nbuttons; + ddata->input = input; + if(pdata->chn >= 0) { + ddata->client = adc_register(pdata->chn, callback, (void *)ddata); + if(!ddata->client) { + error = -EINVAL; + goto fail1; + } + setup_timer(&ddata->timer, + adc_timer, (unsigned long)ddata); + mod_timer(&ddata->timer, jiffies + msecs_to_jiffies(100)); + } + for (i = 0; i < pdata->nbuttons; i++) { + struct rk29_keys_button *button = &pdata->buttons[i]; + struct rk29_button_data *bdata = &ddata->data[i]; + int irq; + unsigned int type = EV_KEY; + + bdata->input = input; + bdata->button = button; + if(button->code_long_press) + setup_timer(&bdata->timer, + keys_long_press_timer, (unsigned long)bdata); + else if(button->code) + setup_timer(&bdata->timer, + keys_timer, (unsigned long)bdata); + INIT_WORK(&bdata->work, keys_do_something); + if(button->gpio) { + error = gpio_request(button->gpio, button->desc ?: "keys"); + if (error < 0) { + pr_err("gpio-keys: failed to request GPIO %d," + " error %d\n", button->gpio, error); + goto fail2; + } + + error = gpio_direction_input(button->gpio); + if (error < 0) { + pr_err("gpio-keys: failed to configure input" + " direction for GPIO %d, error %d\n", + button->gpio, error); + gpio_free(button->gpio); + goto fail2; + } + + irq = gpio_to_irq(button->gpio); + if (irq < 0) { + error = irq; + pr_err("gpio-keys: Unable to get irq number" + " for GPIO %d, error %d\n", + button->gpio, error); + gpio_free(button->gpio); + goto fail2; + } + + error = request_irq(irq, keys_isr, + (button->active_low)?IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING, + button->desc ? button->desc : "keys", + bdata); + if (error) { + pr_err("gpio-keys: Unable to claim irq %d; error %d\n", + irq, error); + gpio_free(button->gpio); + goto fail2; + } + } + if (button->wakeup) + wakeup = 1; + + input_set_capability(input, type, button->code); + } + + error = input_register_device(input); + if (error) { + pr_err("gpio-keys: Unable to register input device, " + "error: %d\n", error); + goto fail2; + } + + device_init_wakeup(&pdev->dev, wakeup); + + return 0; + + fail2: + while (--i >= 0) { + free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); + del_timer_sync(&ddata->data[i].timer); + cancel_work_sync(&ddata->data[i].work); + gpio_free(pdata->buttons[i].gpio); + } + if(pdata->chn >= 0 && ddata->client); + adc_unregister(ddata->client); + fail1: + platform_set_drvdata(pdev, NULL); + fail0: + input_free_device(input); + kfree(ddata); + + return error; +} + +static int __devexit keys_remove(struct platform_device *pdev) +{ + struct rk29_keys_platform_data *pdata = pdev->dev.platform_data; + struct rk29_keys_drvdata *ddata = platform_get_drvdata(pdev); + struct input_dev *input = ddata->input; + int i; + + device_init_wakeup(&pdev->dev, 0); + + for (i = 0; i < pdata->nbuttons; i++) { + int irq = gpio_to_irq(pdata->buttons[i].gpio); + free_irq(irq, &ddata->data[i]); + del_timer_sync(&ddata->data[i].timer); + cancel_work_sync(&ddata->data[i].work); + gpio_free(pdata->buttons[i].gpio); + } + if(pdata->chn >= 0 && ddata->client); + adc_unregister(ddata->client); + input_unregister_device(input); + + return 0; +} + + +#ifdef CONFIG_PM +static int keys_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk29_keys_platform_data *pdata = pdev->dev.platform_data; + int i; + + if (device_may_wakeup(&pdev->dev)) { + for (i = 0; i < pdata->nbuttons; i++) { + struct rk29_keys_button *button = &pdata->buttons[i]; + if (button->wakeup) { + int irq = gpio_to_irq(button->gpio); + enable_irq_wake(irq); + } + } + } + + return 0; +} + +static int keys_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rk29_keys_platform_data *pdata = pdev->dev.platform_data; + int i; + + if (device_may_wakeup(&pdev->dev)) { + for (i = 0; i < pdata->nbuttons; i++) { + struct rk29_keys_button *button = &pdata->buttons[i]; + if (button->wakeup) { + int irq = gpio_to_irq(button->gpio); + disable_irq_wake(irq); + } + } + } + + return 0; +} + +static const struct dev_pm_ops keys_pm_ops = { + .suspend = keys_suspend, + .resume = keys_resume, +}; +#endif + +static struct platform_driver keys_device_driver = { + .probe = keys_probe, + .remove = __devexit_p(keys_remove), + .driver = { + .name = "gpio-keys", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &keys_pm_ops, +#endif + } +}; + +static int __init keys_init(void) +{ + return platform_driver_register(&keys_device_driver); +} + +static void __exit keys_exit(void) +{ + platform_driver_unregister(&keys_device_driver); +} + +module_init(keys_init); +module_exit(keys_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Phil Blundell "); +MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs"); +MODULE_ALIAS("platform:gpio-keys"); -- 2.34.1