diff options
Diffstat (limited to 'drivers/input/keyboard')
29 files changed, 861 insertions, 344 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 615c21f2a55..cdc385b2cf7 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -221,6 +221,22 @@ config KEYBOARD_TCA6416 To compile this driver as a module, choose M here: the module will be called tca6416_keypad. +config KEYBOARD_TCA8418 + tristate "TCA8418 Keypad Support" + depends on I2C + help + This driver implements basic keypad functionality + for keys connected through TCA8418 keypad decoder. + + Say Y here if your device has keys connected to + TCA8418 keypad decoder. + + If enabled the complete TCA8418 device will be managed through + this driver. + + To compile this driver as a module, choose M here: the + module will be called tca8418_keypad. + config KEYBOARD_MATRIX tristate "GPIO driven matrix keypad support" depends on GENERIC_GPIO @@ -425,9 +441,10 @@ config KEYBOARD_PMIC8XXX config KEYBOARD_SAMSUNG tristate "Samsung keypad support" - depends on SAMSUNG_DEV_KEYPAD + depends on HAVE_CLK help - Say Y here if you want to use the Samsung keypad. + Say Y here if you want to use the keypad on your Samsung mobile + device. To compile this driver as a module, choose M here: the module will be called samsung-keypad. diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index ddde0fd476f..df7061f1291 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o +obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o diff --git a/drivers/input/keyboard/adp5520-keys.c b/drivers/input/keyboard/adp5520-keys.c index 3db8006dac3..e9e8674dfda 100644 --- a/drivers/input/keyboard/adp5520-keys.c +++ b/drivers/input/keyboard/adp5520-keys.c @@ -202,18 +202,7 @@ static struct platform_driver adp5520_keys_driver = { .probe = adp5520_keys_probe, .remove = __devexit_p(adp5520_keys_remove), }; - -static int __init adp5520_keys_init(void) -{ - return platform_driver_register(&adp5520_keys_driver); -} -module_init(adp5520_keys_init); - -static void __exit adp5520_keys_exit(void) -{ - platform_driver_unregister(&adp5520_keys_driver); -} -module_exit(adp5520_keys_exit); +module_platform_driver(adp5520_keys_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("Keys ADP5520 Driver"); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 19cfc0cf558..e05a2e7073c 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1305,7 +1305,7 @@ static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; bool old_extra; unsigned char old_set; @@ -1313,7 +1313,11 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun if (!atkbd->write) return -EIO; - if (strict_strtoul(buf, 10, &value) || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; if (atkbd->extra != value) { @@ -1389,11 +1393,15 @@ static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; bool old_scroll; - if (strict_strtoul(buf, 10, &value) || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; if (atkbd->scroll != value) { @@ -1433,7 +1441,7 @@ static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; unsigned char old_set; bool old_extra; @@ -1441,7 +1449,11 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) if (!atkbd->write) return -EIO; - if (strict_strtoul(buf, 10, &value) || (value != 2 && value != 3)) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value != 2 && value != 3) return -EINVAL; if (atkbd->set != value) { @@ -1484,14 +1496,18 @@ static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; bool old_softrepeat, old_softraw; if (!atkbd->write) return -EIO; - if (strict_strtoul(buf, 10, &value) || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; if (atkbd->softrepeat != value) { @@ -1534,11 +1550,15 @@ static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf) static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; - unsigned long value; + unsigned int value; int err; bool old_softraw; - if (strict_strtoul(buf, 10, &value) || value > 1) + err = kstrtouint(buf, 10, &value); + if (err) + return err; + + if (value > 1) return -EINVAL; if (atkbd->softraw != value) { diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index 7d989603f87..8eb9116e0a5 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -384,7 +384,7 @@ static int bfin_kpad_resume(struct platform_device *pdev) # define bfin_kpad_resume NULL #endif -struct platform_driver bfin_kpad_device_driver = { +static struct platform_driver bfin_kpad_device_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, @@ -394,19 +394,7 @@ struct platform_driver bfin_kpad_device_driver = { .suspend = bfin_kpad_suspend, .resume = bfin_kpad_resume, }; - -static int __init bfin_kpad_init(void) -{ - return platform_driver_register(&bfin_kpad_device_driver); -} - -static void __exit bfin_kpad_exit(void) -{ - platform_driver_unregister(&bfin_kpad_device_driver); -} - -module_init(bfin_kpad_init); -module_exit(bfin_kpad_exit); +module_platform_driver(bfin_kpad_device_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index 4662c5da801..0ba69f3fcb5 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -390,19 +390,7 @@ static struct platform_driver ep93xx_keypad_driver = { .suspend = ep93xx_keypad_suspend, .resume = ep93xx_keypad_resume, }; - -static int __init ep93xx_keypad_init(void) -{ - return platform_driver_register(&ep93xx_keypad_driver); -} - -static void __exit ep93xx_keypad_exit(void) -{ - platform_driver_unregister(&ep93xx_keypad_driver); -} - -module_init(ep93xx_keypad_init); -module_exit(ep93xx_keypad_exit); +module_platform_driver(ep93xx_keypad_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 4c17aff2065..20c8ab17221 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -241,19 +241,7 @@ static struct platform_driver gpio_keys_polled_driver = { .owner = THIS_MODULE, }, }; - -static int __init gpio_keys_polled_init(void) -{ - return platform_driver_register(&gpio_keys_polled_driver); -} - -static void __exit gpio_keys_polled_exit(void) -{ - platform_driver_unregister(&gpio_keys_polled_driver); -} - -module_init(gpio_keys_polled_init); -module_exit(gpio_keys_polled_exit); +module_platform_driver(gpio_keys_polled_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index ccebd2d0915..fb87b3bcadb 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -619,19 +619,7 @@ static struct platform_driver imx_keypad_driver = { .probe = imx_keypad_probe, .remove = __devexit_p(imx_keypad_remove), }; - -static int __init imx_keypad_init(void) -{ - return platform_driver_register(&imx_keypad_driver); -} - -static void __exit imx_keypad_exit(void) -{ - platform_driver_unregister(&imx_keypad_driver); -} - -module_init(imx_keypad_init); -module_exit(imx_keypad_exit); +module_platform_driver(imx_keypad_driver); MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>"); MODULE_DESCRIPTION("IMX Keypad Port Driver"); diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c index 7197c569874..24f3ea01c4d 100644 --- a/drivers/input/keyboard/jornada680_kbd.c +++ b/drivers/input/keyboard/jornada680_kbd.c @@ -260,19 +260,7 @@ static struct platform_driver jornada680kbd_driver = { .probe = jornada680kbd_probe, .remove = __devexit_p(jornada680kbd_remove), }; - -static int __init jornada680kbd_init(void) -{ - return platform_driver_register(&jornada680kbd_driver); -} - -static void __exit jornada680kbd_exit(void) -{ - platform_driver_unregister(&jornada680kbd_driver); -} - -module_init(jornada680kbd_init); -module_exit(jornada680kbd_exit); +module_platform_driver(jornada680kbd_driver); MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver"); diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index 0aa6740e60d..eeafc30b207 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -174,16 +174,4 @@ static struct platform_driver jornada720_kbd_driver = { .probe = jornada720_kbd_probe, .remove = __devexit_p(jornada720_kbd_remove), }; - -static int __init jornada720_kbd_init(void) -{ - return platform_driver_register(&jornada720_kbd_driver); -} - -static void __exit jornada720_kbd_exit(void) -{ - platform_driver_unregister(&jornada720_kbd_driver); -} - -module_init(jornada720_kbd_init); -module_exit(jornada720_kbd_exit); +module_platform_driver(jornada720_kbd_driver); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 82d1dc8badd..21823bfc791 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -545,13 +545,12 @@ static ssize_t lm8323_pwm_store_time(struct device *dev, { struct led_classdev *led_cdev = dev_get_drvdata(dev); struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev); - int ret; - unsigned long time; + int ret, time; - ret = strict_strtoul(buf, 10, &time); + ret = kstrtoint(buf, 10, &time); /* Numbers only, please. */ if (ret) - return -EINVAL; + return ret; pwm->fade_time = time; @@ -613,9 +612,9 @@ static ssize_t lm8323_set_disable(struct device *dev, { struct lm8323_chip *lm = dev_get_drvdata(dev); int ret; - unsigned long i; + unsigned int i; - ret = strict_strtoul(buf, 10, &i); + ret = kstrtouint(buf, 10, &i); mutex_lock(&lm->lock); lm->kp_enabled = !i; diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index e2ae657717e..9b223d73de3 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -496,19 +496,7 @@ static struct platform_driver matrix_keypad_driver = { #endif }, }; - -static int __init matrix_keypad_init(void) -{ - return platform_driver_register(&matrix_keypad_driver); -} - -static void __exit matrix_keypad_exit(void) -{ - platform_driver_unregister(&matrix_keypad_driver); -} - -module_init(matrix_keypad_init); -module_exit(matrix_keypad_exit); +module_platform_driver(matrix_keypad_driver); MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index fcdec5e2b29..e35566aa102 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -379,7 +379,7 @@ static const struct dev_pm_ops ske_keypad_dev_pm_ops = { }; #endif -struct platform_driver ske_keypad_driver = { +static struct platform_driver ske_keypad_driver = { .driver = { .name = "nmk-ske-keypad", .owner = THIS_MODULE, diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 323bcdfff24..6b630d9d3df 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -473,20 +473,7 @@ static struct platform_driver omap_kp_driver = { .owner = THIS_MODULE, }, }; - -static int __init omap_kp_init(void) -{ - printk(KERN_INFO "OMAP Keypad Driver\n"); - return platform_driver_register(&omap_kp_driver); -} - -static void __exit omap_kp_exit(void) -{ - platform_driver_unregister(&omap_kp_driver); -} - -module_init(omap_kp_init); -module_exit(omap_kp_exit); +module_platform_driver(omap_kp_driver); MODULE_AUTHOR("Timo Teräs"); MODULE_DESCRIPTION("OMAP Keypad Driver"); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index c51a3c4a7fe..d5c5d77f4b8 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -335,18 +335,7 @@ static struct platform_driver omap4_keypad_driver = { .owner = THIS_MODULE, }, }; - -static int __init omap4_keypad_init(void) -{ - return platform_driver_register(&omap4_keypad_driver); -} -module_init(omap4_keypad_init); - -static void __exit omap4_keypad_exit(void) -{ - platform_driver_unregister(&omap4_keypad_driver); -} -module_exit(omap4_keypad_exit); +module_platform_driver(omap4_keypad_driver); MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("OMAP4 Keypad Driver"); diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c index 1f1a5563f60..abe728c7b88 100644 --- a/drivers/input/keyboard/opencores-kbd.c +++ b/drivers/input/keyboard/opencores-kbd.c @@ -163,18 +163,7 @@ static struct platform_driver opencores_kbd_device_driver = { .name = "opencores-kbd", }, }; - -static int __init opencores_kbd_init(void) -{ - return platform_driver_register(&opencores_kbd_device_driver); -} -module_init(opencores_kbd_init); - -static void __exit opencores_kbd_exit(void) -{ - platform_driver_unregister(&opencores_kbd_device_driver); -} -module_exit(opencores_kbd_exit); +module_platform_driver(opencores_kbd_device_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Javier Herrero <jherrero@hvsistemas.es>"); diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index e7cc51d0fb3..01a1c9f8a38 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -780,18 +780,7 @@ static struct platform_driver pmic8xxx_kp_driver = { .pm = &pm8xxx_kp_pm_ops, }, }; - -static int __init pmic8xxx_kp_init(void) -{ - return platform_driver_register(&pmic8xxx_kp_driver); -} -module_init(pmic8xxx_kp_init); - -static void __exit pmic8xxx_kp_exit(void) -{ - platform_driver_unregister(&pmic8xxx_kp_driver); -} -module_exit(pmic8xxx_kp_exit); +module_platform_driver(pmic8xxx_kp_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("PMIC8XXX keypad driver"); diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index eca6ae63de1..29fe1b2be1c 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -602,19 +602,7 @@ static struct platform_driver pxa27x_keypad_driver = { #endif }, }; - -static int __init pxa27x_keypad_init(void) -{ - return platform_driver_register(&pxa27x_keypad_driver); -} - -static void __exit pxa27x_keypad_exit(void) -{ - platform_driver_unregister(&pxa27x_keypad_driver); -} - -module_init(pxa27x_keypad_init); -module_exit(pxa27x_keypad_exit); +module_platform_driver(pxa27x_keypad_driver); MODULE_DESCRIPTION("PXA27x Keypad Controller Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/pxa930_rotary.c b/drivers/input/keyboard/pxa930_rotary.c index 35451bf780c..d7f1134b789 100644 --- a/drivers/input/keyboard/pxa930_rotary.c +++ b/drivers/input/keyboard/pxa930_rotary.c @@ -195,18 +195,7 @@ static struct platform_driver pxa930_rotary_driver = { .probe = pxa930_rotary_probe, .remove = __devexit_p(pxa930_rotary_remove), }; - -static int __init pxa930_rotary_init(void) -{ - return platform_driver_register(&pxa930_rotary_driver); -} -module_init(pxa930_rotary_init); - -static void __exit pxa930_rotary_exit(void) -{ - platform_driver_unregister(&pxa930_rotary_driver); -} -module_exit(pxa930_rotary_exit); +module_platform_driver(pxa930_rotary_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller"); diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index f689f49e310..17ba7f9f80f 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -20,9 +20,13 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/sched.h> -#include <plat/keypad.h> +#include <linux/input/samsung-keypad.h> #define SAMSUNG_KEYIFCON 0x00 #define SAMSUNG_KEYIFSTSCLR 0x04 @@ -63,36 +67,33 @@ enum samsung_keypad_type { struct samsung_keypad { struct input_dev *input_dev; + struct platform_device *pdev; struct clk *clk; void __iomem *base; wait_queue_head_t wait; bool stopped; + bool wake_enabled; int irq; + enum samsung_keypad_type type; unsigned int row_shift; unsigned int rows; unsigned int cols; unsigned int row_state[SAMSUNG_MAX_COLS]; +#ifdef CONFIG_OF + int row_gpios[SAMSUNG_MAX_ROWS]; + int col_gpios[SAMSUNG_MAX_COLS]; +#endif unsigned short keycodes[]; }; -static int samsung_keypad_is_s5pv210(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - enum samsung_keypad_type type = - platform_get_device_id(pdev)->driver_data; - - return type == KEYPAD_TYPE_S5PV210; -} - static void samsung_keypad_scan(struct samsung_keypad *keypad, unsigned int *row_state) { - struct device *dev = keypad->input_dev->dev.parent; unsigned int col; unsigned int val; for (col = 0; col < keypad->cols; col++) { - if (samsung_keypad_is_s5pv210(dev)) { + if (keypad->type == KEYPAD_TYPE_S5PV210) { val = S5PV210_KEYIFCOLEN_MASK; val &= ~(1 << col) << 8; } else { @@ -158,6 +159,8 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) unsigned int val; bool key_down; + pm_runtime_get_sync(&keypad->pdev->dev); + do { val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); /* Clear interrupt. */ @@ -172,6 +175,8 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) } while (key_down && !keypad->stopped); + pm_runtime_put_sync(&keypad->pdev->dev); + return IRQ_HANDLED; } @@ -179,6 +184,8 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) { unsigned int val; + pm_runtime_get_sync(&keypad->pdev->dev); + /* Tell IRQ thread that it may poll the device. */ keypad->stopped = false; @@ -191,12 +198,16 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) /* KEYIFCOL reg clear. */ writel(0, keypad->base + SAMSUNG_KEYIFCOL); + + pm_runtime_put_sync(&keypad->pdev->dev); } static void samsung_keypad_stop(struct samsung_keypad *keypad) { unsigned int val; + pm_runtime_get_sync(&keypad->pdev->dev); + /* Signal IRQ thread to stop polling and disable the handler. */ keypad->stopped = true; wake_up(&keypad->wait); @@ -217,6 +228,8 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad) * re-enable the handler. */ enable_irq(keypad->irq); + + pm_runtime_put_sync(&keypad->pdev->dev); } static int samsung_keypad_open(struct input_dev *input_dev) @@ -235,6 +248,126 @@ static void samsung_keypad_close(struct input_dev *input_dev) samsung_keypad_stop(keypad); } +#ifdef CONFIG_OF +static struct samsung_keypad_platdata *samsung_keypad_parse_dt( + struct device *dev) +{ + struct samsung_keypad_platdata *pdata; + struct matrix_keymap_data *keymap_data; + uint32_t *keymap, num_rows = 0, num_cols = 0; + struct device_node *np = dev->of_node, *key_np; + unsigned int key_count = 0; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for platform data\n"); + return NULL; + } + + of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows); + of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols); + if (!num_rows || !num_cols) { + dev_err(dev, "number of keypad rows/columns not specified\n"); + return NULL; + } + pdata->rows = num_rows; + pdata->cols = num_cols; + + keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL); + if (!keymap_data) { + dev_err(dev, "could not allocate memory for keymap data\n"); + return NULL; + } + pdata->keymap_data = keymap_data; + + for_each_child_of_node(np, key_np) + key_count++; + + keymap_data->keymap_size = key_count; + keymap = devm_kzalloc(dev, sizeof(uint32_t) * key_count, GFP_KERNEL); + if (!keymap) { + dev_err(dev, "could not allocate memory for keymap\n"); + return NULL; + } + keymap_data->keymap = keymap; + + for_each_child_of_node(np, key_np) { + u32 row, col, key_code; + of_property_read_u32(key_np, "keypad,row", &row); + of_property_read_u32(key_np, "keypad,column", &col); + of_property_read_u32(key_np, "linux,code", &key_code); + *keymap++ = KEY(row, col, key_code); + } + + if (of_get_property(np, "linux,input-no-autorepeat", NULL)) + pdata->no_autorepeat = true; + if (of_get_property(np, "linux,input-wakeup", NULL)) + pdata->wakeup = true; + + return pdata; +} + +static void samsung_keypad_parse_dt_gpio(struct device *dev, + struct samsung_keypad *keypad) +{ + struct device_node *np = dev->of_node; + int gpio, ret, row, col; + + for (row = 0; row < keypad->rows; row++) { + gpio = of_get_named_gpio(np, "row-gpios", row); + keypad->row_gpios[row] = gpio; + if (!gpio_is_valid(gpio)) { + dev_err(dev, "keypad row[%d]: invalid gpio %d\n", + row, gpio); + continue; + } + + ret = gpio_request(gpio, "keypad-row"); + if (ret) + dev_err(dev, "keypad row[%d] gpio request failed\n", + row); + } + + for (col = 0; col < keypad->cols; col++) { + gpio = of_get_named_gpio(np, "col-gpios", col); + keypad->col_gpios[col] = gpio; + if (!gpio_is_valid(gpio)) { + dev_err(dev, "keypad column[%d]: invalid gpio %d\n", + col, gpio); + continue; + } + + ret = gpio_request(gpio, "keypad-col"); + if (ret) + dev_err(dev, "keypad column[%d] gpio request failed\n", + col); + } +} + +static void samsung_keypad_dt_gpio_free(struct samsung_keypad *keypad) +{ + int cnt; + + for (cnt = 0; cnt < keypad->rows; cnt++) + if (gpio_is_valid(keypad->row_gpios[cnt])) + gpio_free(keypad->row_gpios[cnt]); + + for (cnt = 0; cnt < keypad->cols; cnt++) + if (gpio_is_valid(keypad->col_gpios[cnt])) + gpio_free(keypad->col_gpios[cnt]); +} +#else +static +struct samsung_keypad_platdata *samsung_keypad_parse_dt(struct device *dev) +{ + return NULL; +} + +static void samsung_keypad_dt_gpio_free(struct samsung_keypad *keypad) +{ +} +#endif + static int __devinit samsung_keypad_probe(struct platform_device *pdev) { const struct samsung_keypad_platdata *pdata; @@ -246,7 +379,10 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) unsigned int keymap_size; int error; - pdata = pdev->dev.platform_data; + if (pdev->dev.of_node) + pdata = samsung_keypad_parse_dt(&pdev->dev); + else + pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "no platform data defined\n"); return -EINVAL; @@ -298,11 +434,23 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) } keypad->input_dev = input_dev; + keypad->pdev = pdev; keypad->row_shift = row_shift; keypad->rows = pdata->rows; keypad->cols = pdata->cols; + keypad->stopped = true; init_waitqueue_head(&keypad->wait); + if (pdev->dev.of_node) { +#ifdef CONFIG_OF + samsung_keypad_parse_dt_gpio(&pdev->dev, keypad); + keypad->type = of_device_is_compatible(pdev->dev.of_node, + "samsung,s5pv210-keypad"); +#endif + } else { + keypad->type = platform_get_device_id(pdev)->driver_data; + } + input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; @@ -337,18 +485,29 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) goto err_put_clk; } + device_init_wakeup(&pdev->dev, pdata->wakeup); + platform_set_drvdata(pdev, keypad); + pm_runtime_enable(&pdev->dev); + error = input_register_device(keypad->input_dev); if (error) goto err_free_irq; - device_init_wakeup(&pdev->dev, pdata->wakeup); - platform_set_drvdata(pdev, keypad); + if (pdev->dev.of_node) { + devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap); + devm_kfree(&pdev->dev, (void *)pdata->keymap_data); + devm_kfree(&pdev->dev, (void *)pdata); + } return 0; err_free_irq: free_irq(keypad->irq, keypad); + pm_runtime_disable(&pdev->dev); + device_init_wakeup(&pdev->dev, 0); + platform_set_drvdata(pdev, NULL); err_put_clk: clk_put(keypad->clk); + samsung_keypad_dt_gpio_free(keypad); err_unmap_base: iounmap(keypad->base); err_free_mem: @@ -362,6 +521,7 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) { struct samsung_keypad *keypad = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); device_init_wakeup(&pdev->dev, 0); platform_set_drvdata(pdev, NULL); @@ -374,6 +534,7 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) free_irq(keypad->irq, keypad); clk_put(keypad->clk); + samsung_keypad_dt_gpio_free(keypad); iounmap(keypad->base); kfree(keypad); @@ -381,11 +542,57 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_RUNTIME +static int samsung_keypad_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + unsigned int val; + int error; + + if (keypad->stopped) + return 0; + + /* This may fail on some SoCs due to lack of controller support */ + error = enable_irq_wake(keypad->irq); + if (!error) + keypad->wake_enabled = true; + + val = readl(keypad->base + SAMSUNG_KEYIFCON); + val |= SAMSUNG_KEYIFCON_WAKEUPEN; + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); + + return 0; +} + +static int samsung_keypad_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + unsigned int val; + + if (keypad->stopped) + return 0; + + clk_enable(keypad->clk); + + val = readl(keypad->base + SAMSUNG_KEYIFCON); + val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + if (keypad->wake_enabled) + disable_irq_wake(keypad->irq); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, bool enable) { - struct device *dev = keypad->input_dev->dev.parent; unsigned int val; clk_enable(keypad->clk); @@ -393,11 +600,11 @@ static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, val = readl(keypad->base + SAMSUNG_KEYIFCON); if (enable) { val |= SAMSUNG_KEYIFCON_WAKEUPEN; - if (device_may_wakeup(dev)) + if (device_may_wakeup(&keypad->pdev->dev)) enable_irq_wake(keypad->irq); } else { val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; - if (device_may_wakeup(dev)) + if (device_may_wakeup(&keypad->pdev->dev)) disable_irq_wake(keypad->irq); } writel(val, keypad->base + SAMSUNG_KEYIFCON); @@ -440,11 +647,23 @@ static int samsung_keypad_resume(struct device *dev) return 0; } +#endif static const struct dev_pm_ops samsung_keypad_pm_ops = { - .suspend = samsung_keypad_suspend, - .resume = samsung_keypad_resume, + SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume) + SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend, + samsung_keypad_runtime_resume, NULL) }; + +#ifdef CONFIG_OF +static const struct of_device_id samsung_keypad_dt_match[] = { + { .compatible = "samsung,s3c6410-keypad" }, + { .compatible = "samsung,s5pv210-keypad" }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); +#else +#define samsung_keypad_dt_match NULL #endif static struct platform_device_id samsung_keypad_driver_ids[] = { @@ -465,27 +684,14 @@ static struct platform_driver samsung_keypad_driver = { .driver = { .name = "samsung-keypad", .owner = THIS_MODULE, -#ifdef CONFIG_PM + .of_match_table = samsung_keypad_dt_match, .pm = &samsung_keypad_pm_ops, -#endif }, .id_table = samsung_keypad_driver_ids, }; - -static int __init samsung_keypad_init(void) -{ - return platform_driver_register(&samsung_keypad_driver); -} -module_init(samsung_keypad_init); - -static void __exit samsung_keypad_exit(void) -{ - platform_driver_unregister(&samsung_keypad_driver); -} -module_exit(samsung_keypad_exit); +module_platform_driver(samsung_keypad_driver); MODULE_DESCRIPTION("Samsung keypad driver"); MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-keypad"); diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index 934aeb583b3..da54ad5db15 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -337,19 +337,7 @@ static struct platform_driver sh_keysc_device_driver = { .pm = &sh_keysc_dev_pm_ops, } }; - -static int __init sh_keysc_init(void) -{ - return platform_driver_register(&sh_keysc_device_driver); -} - -static void __exit sh_keysc_exit(void) -{ - platform_driver_unregister(&sh_keysc_device_driver); -} - -module_init(sh_keysc_init); -module_exit(sh_keysc_exit); +module_platform_driver(sh_keysc_device_driver); MODULE_AUTHOR("Magnus Damm"); MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver"); diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index d712dffd215..c88bd63dc9c 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -326,18 +326,7 @@ static struct platform_driver spear_kbd_driver = { #endif }, }; - -static int __init spear_kbd_init(void) -{ - return platform_driver_register(&spear_kbd_driver); -} -module_init(spear_kbd_init); - -static void __exit spear_kbd_exit(void) -{ - platform_driver_unregister(&spear_kbd_driver); -} -module_exit(spear_kbd_exit); +module_platform_driver(spear_kbd_driver); MODULE_AUTHOR("Rajeev Kumar"); MODULE_DESCRIPTION("SPEAr Keyboard Driver"); diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index ab7610ca10e..9397cf9c625 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -368,18 +368,7 @@ static struct platform_driver stmpe_keypad_driver = { .probe = stmpe_keypad_probe, .remove = __devexit_p(stmpe_keypad_remove), }; - -static int __init stmpe_keypad_init(void) -{ - return platform_driver_register(&stmpe_keypad_driver); -} -module_init(stmpe_keypad_init); - -static void __exit stmpe_keypad_exit(void) -{ - platform_driver_unregister(&stmpe_keypad_driver); -} -module_exit(stmpe_keypad_exit); +module_platform_driver(stmpe_keypad_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("STMPExxxx keypad driver"); diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index f60c9e82f20..2dee3e4e7c6 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -74,11 +74,13 @@ /** * struct tc_keypad - data structure used by keypad driver + * @tc3589x: pointer to tc35893 * @input: pointer to input device object * @board: keypad platform device * @krow: number of rows * @kcol: number of coloumns * @keymap: matrix scan code table for keycodes + * @keypad_stopped: holds keypad status */ struct tc_keypad { struct tc3589x *tc3589x; @@ -453,18 +455,7 @@ static struct platform_driver tc3589x_keypad_driver = { .probe = tc3589x_keypad_probe, .remove = __devexit_p(tc3589x_keypad_remove), }; - -static int __init tc3589x_keypad_init(void) -{ - return platform_driver_register(&tc3589x_keypad_driver); -} -module_init(tc3589x_keypad_init); - -static void __exit tc3589x_keypad_exit(void) -{ - return platform_driver_unregister(&tc3589x_keypad_driver); -} -module_exit(tc3589x_keypad_exit); +module_platform_driver(tc3589x_keypad_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer"); diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c new file mode 100644 index 00000000000..958ec107bfb --- /dev/null +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -0,0 +1,430 @@ +/* + * Driver for TCA8418 I2C keyboard + * + * Copyright (C) 2011 Fuel7, Inc. All rights reserved. + * + * Author: Kyle Manna <kyle.manna@fuel7.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + * + * If you can't comply with GPLv2, alternative licensing terms may be + * arranged. Please contact Fuel7, Inc. (http://fuel7.com/) for proprietary + * alternative licensing inquiries. + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/tca8418_keypad.h> + +/* TCA8418 hardware limits */ +#define TCA8418_MAX_ROWS 8 +#define TCA8418_MAX_COLS 10 + +/* TCA8418 register offsets */ +#define REG_CFG 0x01 +#define REG_INT_STAT 0x02 +#define REG_KEY_LCK_EC 0x03 +#define REG_KEY_EVENT_A 0x04 +#define REG_KEY_EVENT_B 0x05 +#define REG_KEY_EVENT_C 0x06 +#define REG_KEY_EVENT_D 0x07 +#define REG_KEY_EVENT_E 0x08 +#define REG_KEY_EVENT_F 0x09 +#define REG_KEY_EVENT_G 0x0A +#define REG_KEY_EVENT_H 0x0B +#define REG_KEY_EVENT_I 0x0C +#define REG_KEY_EVENT_J 0x0D +#define REG_KP_LCK_TIMER 0x0E +#define REG_UNLOCK1 0x0F +#define REG_UNLOCK2 0x10 +#define REG_GPIO_INT_STAT1 0x11 +#define REG_GPIO_INT_STAT2 0x12 +#define REG_GPIO_INT_STAT3 0x13 +#define REG_GPIO_DAT_STAT1 0x14 +#define REG_GPIO_DAT_STAT2 0x15 +#define REG_GPIO_DAT_STAT3 0x16 +#define REG_GPIO_DAT_OUT1 0x17 +#define REG_GPIO_DAT_OUT2 0x18 +#define REG_GPIO_DAT_OUT3 0x19 +#define REG_GPIO_INT_EN1 0x1A +#define REG_GPIO_INT_EN2 0x1B +#define REG_GPIO_INT_EN3 0x1C +#define REG_KP_GPIO1 0x1D +#define REG_KP_GPIO2 0x1E +#define REG_KP_GPIO3 0x1F +#define REG_GPI_EM1 0x20 +#define REG_GPI_EM2 0x21 +#define REG_GPI_EM3 0x22 +#define REG_GPIO_DIR1 0x23 +#define REG_GPIO_DIR2 0x24 +#define REG_GPIO_DIR3 0x25 +#define REG_GPIO_INT_LVL1 0x26 +#define REG_GPIO_INT_LVL2 0x27 +#define REG_GPIO_INT_LVL3 0x28 +#define REG_DEBOUNCE_DIS1 0x29 +#define REG_DEBOUNCE_DIS2 0x2A +#define REG_DEBOUNCE_DIS3 0x2B +#define REG_GPIO_PULL1 0x2C +#define REG_GPIO_PULL2 0x2D +#define REG_GPIO_PULL3 0x2E + +/* TCA8418 bit definitions */ +#define CFG_AI BIT(7) +#define CFG_GPI_E_CFG BIT(6) +#define CFG_OVR_FLOW_M BIT(5) +#define CFG_INT_CFG BIT(4) +#define CFG_OVR_FLOW_IEN BIT(3) +#define CFG_K_LCK_IEN BIT(2) +#define CFG_GPI_IEN BIT(1) +#define CFG_KE_IEN BIT(0) + +#define INT_STAT_CAD_INT BIT(4) +#define INT_STAT_OVR_FLOW_INT BIT(3) +#define INT_STAT_K_LCK_INT BIT(2) +#define INT_STAT_GPI_INT BIT(1) +#define INT_STAT_K_INT BIT(0) + +/* TCA8418 register masks */ +#define KEY_LCK_EC_KEC 0x7 +#define KEY_EVENT_CODE 0x7f +#define KEY_EVENT_VALUE 0x80 + + +static const struct i2c_device_id tca8418_id[] = { + { TCA8418_NAME, 8418, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tca8418_id); + +struct tca8418_keypad { + unsigned int rows; + unsigned int cols; + unsigned int keypad_mask; /* Mask for keypad col/rol regs */ + unsigned int irq; + unsigned int row_shift; + + struct i2c_client *client; + struct input_dev *input; + + /* Flexible array member, must be at end of struct */ + unsigned short keymap[]; +}; + +/* + * Write a byte to the TCA8418 + */ +static int tca8418_write_byte(struct tca8418_keypad *keypad_data, + int reg, u8 val) +{ + int error; + + error = i2c_smbus_write_byte_data(keypad_data->client, reg, val); + if (error < 0) { + dev_err(&keypad_data->client->dev, + "%s failed, reg: %d, val: %d, error: %d\n", + __func__, reg, val, error); + return error; + } + + return 0; +} + +/* + * Read a byte from the TCA8418 + */ +static int tca8418_read_byte(struct tca8418_keypad *keypad_data, + int reg, u8 *val) +{ + int error; + + error = i2c_smbus_read_byte_data(keypad_data->client, reg); + if (error < 0) { + dev_err(&keypad_data->client->dev, + "%s failed, reg: %d, error: %d\n", + __func__, reg, error); + return error; + } + + *val = (u8)error; + + return 0; +} + +static void tca8418_read_keypad(struct tca8418_keypad *keypad_data) +{ + int error, col, row; + u8 reg, state, code; + + /* Initial read of the key event FIFO */ + error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®); + + /* Assume that key code 0 signifies empty FIFO */ + while (error >= 0 && reg > 0) { + state = reg & KEY_EVENT_VALUE; + code = reg & KEY_EVENT_CODE; + + row = code / TCA8418_MAX_COLS; + col = code % TCA8418_MAX_COLS; + + row = (col) ? row : row - 1; + col = (col) ? col - 1 : TCA8418_MAX_COLS - 1; + + code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift); + input_event(keypad_data->input, EV_MSC, MSC_SCAN, code); + input_report_key(keypad_data->input, + keypad_data->keymap[code], state); + + /* Read for next loop */ + error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, ®); + } + + if (error < 0) + dev_err(&keypad_data->client->dev, + "unable to read REG_KEY_EVENT_A\n"); + + input_sync(keypad_data->input); +} + +/* + * Threaded IRQ handler and this can (and will) sleep. + */ +static irqreturn_t tca8418_irq_handler(int irq, void *dev_id) +{ + struct tca8418_keypad *keypad_data = dev_id; + u8 reg; + int error; + + error = tca8418_read_byte(keypad_data, REG_INT_STAT, ®); + if (error) { + dev_err(&keypad_data->client->dev, + "unable to read REG_INT_STAT\n"); + goto exit; + } + + if (reg & INT_STAT_OVR_FLOW_INT) + dev_warn(&keypad_data->client->dev, "overflow occurred\n"); + + if (reg & INT_STAT_K_INT) + tca8418_read_keypad(keypad_data); + +exit: + /* Clear all interrupts, even IRQs we didn't check (GPI, CAD, LCK) */ + reg = 0xff; + error = tca8418_write_byte(keypad_data, REG_INT_STAT, reg); + if (error) + dev_err(&keypad_data->client->dev, + "unable to clear REG_INT_STAT\n"); + + return IRQ_HANDLED; +} + +/* + * Configure the TCA8418 for keypad operation + */ +static int __devinit tca8418_configure(struct tca8418_keypad *keypad_data) +{ + int reg, error; + + /* Write config register, if this fails assume device not present */ + error = tca8418_write_byte(keypad_data, REG_CFG, + CFG_INT_CFG | CFG_OVR_FLOW_IEN | CFG_KE_IEN); + if (error < 0) + return -ENODEV; + + + /* Assemble a mask for row and column registers */ + reg = ~(~0 << keypad_data->rows); + reg += (~(~0 << keypad_data->cols)) << 8; + keypad_data->keypad_mask = reg; + + /* Set registers to keypad mode */ + error |= tca8418_write_byte(keypad_data, REG_KP_GPIO1, reg); + error |= tca8418_write_byte(keypad_data, REG_KP_GPIO2, reg >> 8); + error |= tca8418_write_byte(keypad_data, REG_KP_GPIO3, reg >> 16); + + /* Enable column debouncing */ + error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS1, reg); + error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS2, reg >> 8); + error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS3, reg >> 16); + + return error; +} + +static int __devinit tca8418_keypad_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct tca8418_keypad_platform_data *pdata = + client->dev.platform_data; + struct tca8418_keypad *keypad_data; + struct input_dev *input; + int error, row_shift, max_keys; + + /* Copy the platform data */ + if (!pdata) { + dev_dbg(&client->dev, "no platform data\n"); + return -EINVAL; + } + + if (!pdata->keymap_data) { + dev_err(&client->dev, "no keymap data defined\n"); + return -EINVAL; + } + + if (!pdata->rows || pdata->rows > TCA8418_MAX_ROWS) { + dev_err(&client->dev, "invalid rows\n"); + return -EINVAL; + } + + if (!pdata->cols || pdata->cols > TCA8418_MAX_COLS) { + dev_err(&client->dev, "invalid columns\n"); + return -EINVAL; + } + + /* Check i2c driver capabilities */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + row_shift = get_count_order(pdata->cols); + max_keys = pdata->rows << row_shift; + + /* Allocate memory for keypad_data, keymap and input device */ + keypad_data = kzalloc(sizeof(*keypad_data) + + max_keys * sizeof(keypad_data->keymap[0]), GFP_KERNEL); + if (!keypad_data) + return -ENOMEM; + + keypad_data->rows = pdata->rows; + keypad_data->cols = pdata->cols; + keypad_data->client = client; + keypad_data->row_shift = row_shift; + + /* Initialize the chip or fail if chip isn't present */ + error = tca8418_configure(keypad_data); + if (error < 0) + goto fail1; + + /* Configure input device */ + input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto fail1; + } + keypad_data->input = input; + + input->name = client->name; + input->dev.parent = &client->dev; + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x001; + input->id.version = 0x0001; + + input->keycode = keypad_data->keymap; + input->keycodesize = sizeof(keypad_data->keymap[0]); + input->keycodemax = max_keys; + + __set_bit(EV_KEY, input->evbit); + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + input_set_drvdata(input, keypad_data); + + matrix_keypad_build_keymap(pdata->keymap_data, row_shift, + input->keycode, input->keybit); + + if (pdata->irq_is_gpio) + client->irq = gpio_to_irq(client->irq); + + error = request_threaded_irq(client->irq, NULL, tca8418_irq_handler, + IRQF_TRIGGER_FALLING, + client->name, keypad_data); + if (error) { + dev_dbg(&client->dev, + "Unable to claim irq %d; error %d\n", + client->irq, error); + goto fail2; + } + + error = input_register_device(input); + if (error) { + dev_dbg(&client->dev, + "Unable to register input device, error: %d\n", error); + goto fail3; + } + + i2c_set_clientdata(client, keypad_data); + return 0; + +fail3: + free_irq(client->irq, keypad_data); +fail2: + input_free_device(input); +fail1: + kfree(keypad_data); + return error; +} + +static int __devexit tca8418_keypad_remove(struct i2c_client *client) +{ + struct tca8418_keypad *keypad_data = i2c_get_clientdata(client); + + free_irq(keypad_data->client->irq, keypad_data); + + input_unregister_device(keypad_data->input); + + kfree(keypad_data); + + return 0; +} + + +static struct i2c_driver tca8418_keypad_driver = { + .driver = { + .name = TCA8418_NAME, + .owner = THIS_MODULE, + }, + .probe = tca8418_keypad_probe, + .remove = __devexit_p(tca8418_keypad_remove), + .id_table = tca8418_id, +}; + +static int __init tca8418_keypad_init(void) +{ + return i2c_add_driver(&tca8418_keypad_driver); +} +subsys_initcall(tca8418_keypad_init); + +static void __exit tca8418_keypad_exit(void) +{ + i2c_del_driver(&tca8418_keypad_driver); +} +module_exit(tca8418_keypad_exit); + +MODULE_AUTHOR("Kyle Manna <kyle.manna@fuel7.com>"); +MODULE_DESCRIPTION("Keypad driver for TCA8418"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index cf3228b0ab9..a136e2e832b 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/interrupt.h> +#include <linux/of.h> #include <linux/clk.h> #include <linux/slab.h> #include <mach/clk.h> @@ -52,6 +53,7 @@ /* KBC Interrupt Register */ #define KBC_INT_0 0x4 #define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) +#define KBC_INT_KEYPRESS_INT_STATUS (1 << 0) #define KBC_ROW_CFG0_0 0x8 #define KBC_COL_CFG0_0 0x18 @@ -74,15 +76,17 @@ struct tegra_kbc { unsigned int cp_to_wkup_dly; bool use_fn_map; bool use_ghost_filter; + bool keypress_caused_wake; const struct tegra_kbc_platform_data *pdata; unsigned short keycode[KBC_MAX_KEY * 2]; unsigned short current_keys[KBC_MAX_KPENT]; unsigned int num_pressed_keys; + u32 wakeup_key; struct timer_list timer; struct clk *clk; }; -static const u32 tegra_kbc_default_keymap[] = { +static const u32 tegra_kbc_default_keymap[] __devinitdata = { KEY(0, 2, KEY_W), KEY(0, 3, KEY_S), KEY(0, 4, KEY_A), @@ -217,7 +221,8 @@ static const u32 tegra_kbc_default_keymap[] = { KEY(31, 4, KEY_HELP), }; -static const struct matrix_keymap_data tegra_kbc_default_keymap_data = { +static const +struct matrix_keymap_data tegra_kbc_default_keymap_data __devinitdata = { .keymap = tegra_kbc_default_keymap, .keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap), }; @@ -409,6 +414,9 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args) */ tegra_kbc_set_fifo_interrupt(kbc, false); mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); + } else if (val & KBC_INT_KEYPRESS_INT_STATUS) { + /* We can be here only through system resume path */ + kbc->keypress_caused_wake = true; } spin_unlock_irqrestore(&kbc->lock, flags); @@ -576,6 +584,56 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, return true; } +#ifdef CONFIG_OF +static struct tegra_kbc_platform_data * __devinit +tegra_kbc_dt_parse_pdata(struct platform_device *pdev) +{ + struct tegra_kbc_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + + if (!np) + return NULL; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (!of_property_read_u32(np, "debounce-delay", &prop)) + pdata->debounce_cnt = prop; + + if (!of_property_read_u32(np, "repeat-delay", &prop)) + pdata->repeat_cnt = prop; + + if (of_find_property(np, "needs-ghost-filter", NULL)) + pdata->use_ghost_filter = true; + + if (of_find_property(np, "wakeup-source", NULL)) + pdata->wakeup = true; + + /* + * All currently known keymaps with device tree support use the same + * pin_cfg, so set it up here. + */ + for (i = 0; i < KBC_MAX_ROW; i++) { + pdata->pin_cfg[i].num = i; + pdata->pin_cfg[i].is_row = true; + } + + for (i = 0; i < KBC_MAX_COL; i++) { + pdata->pin_cfg[KBC_MAX_ROW + i].num = i; + pdata->pin_cfg[KBC_MAX_ROW + i].is_row = false; + } + + return pdata; +} +#else +static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( + struct platform_device *pdev) +{ + return NULL; +} +#endif + static int __devinit tegra_kbc_probe(struct platform_device *pdev) { const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; @@ -590,21 +648,28 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) unsigned int scan_time_rows; if (!pdata) - return -EINVAL; + pdata = tegra_kbc_dt_parse_pdata(pdev); - if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) + if (!pdata) return -EINVAL; + if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) { + err = -EINVAL; + goto err_free_pdata; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to get I/O memory\n"); - return -ENXIO; + err = -ENXIO; + goto err_free_pdata; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); - return -ENXIO; + err = -ENXIO; + goto err_free_pdata; } kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); @@ -674,9 +739,10 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, input_dev->keycode, input_dev->keybit); + kbc->wakeup_key = pdata->wakeup_key; - err = request_irq(kbc->irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH, - pdev->name, kbc); + err = request_irq(kbc->irq, tegra_kbc_isr, + IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc); if (err) { dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); goto err_put_clk; @@ -706,6 +772,9 @@ err_free_mem_region: err_free_mem: input_free_device(input_dev); kfree(kbc); +err_free_pdata: + if (!pdev->dev.platform_data) + kfree(pdata); return err; } @@ -715,6 +784,8 @@ static int __devexit tegra_kbc_remove(struct platform_device *pdev) struct tegra_kbc *kbc = platform_get_drvdata(pdev); struct resource *res; + platform_set_drvdata(pdev, NULL); + free_irq(kbc->irq, pdev); clk_put(kbc->clk); @@ -723,9 +794,14 @@ static int __devexit tegra_kbc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); - kfree(kbc); + /* + * If we do not have platform data attached to the device we + * allocated it ourselves and thus need to free it. + */ + if (!pdev->dev.platform_data) + kfree(kbc->pdata); - platform_set_drvdata(pdev, NULL); + kfree(kbc); return 0; } @@ -754,6 +830,8 @@ static int tegra_kbc_suspend(struct device *dev) tegra_kbc_setup_wakekeys(kbc, true); msleep(30); + kbc->keypress_caused_wake = false; + enable_irq(kbc->irq); enable_irq_wake(kbc->irq); } else { if (kbc->idev->users) @@ -780,7 +858,19 @@ static int tegra_kbc_resume(struct device *dev) tegra_kbc_set_fifo_interrupt(kbc, true); - enable_irq(kbc->irq); + if (kbc->keypress_caused_wake && kbc->wakeup_key) { + /* + * We can't report events directly from the ISR + * because timekeeping is stopped when processing + * wakeup request and we get a nasty warning when + * we try to call do_gettimeofday() in evdev + * handler. + */ + input_report_key(kbc->idev, kbc->wakeup_key, 1); + input_sync(kbc->idev); + input_report_key(kbc->idev, kbc->wakeup_key, 0); + input_sync(kbc->idev); + } } else { if (kbc->idev->users) err = tegra_kbc_start(kbc); @@ -793,6 +883,12 @@ static int tegra_kbc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume); +static const struct of_device_id tegra_kbc_of_match[] = { + { .compatible = "nvidia,tegra20-kbc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_kbc_of_match); + static struct platform_driver tegra_kbc_driver = { .probe = tegra_kbc_probe, .remove = __devexit_p(tegra_kbc_remove), @@ -800,20 +896,10 @@ static struct platform_driver tegra_kbc_driver = { .name = "tegra-kbc", .owner = THIS_MODULE, .pm = &tegra_kbc_pm_ops, + .of_match_table = tegra_kbc_of_match, }, }; - -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_platform_driver(tegra_kbc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>"); diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c index 66e55e5cfdd..fb39c94b6fd 100644 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -322,19 +322,7 @@ static struct platform_driver keypad_driver = { .driver.name = "tnetv107x-keypad", .driver.owner = THIS_MODULE, }; - -static int __init keypad_init(void) -{ - return platform_driver_register(&keypad_driver); -} - -static void __exit keypad_exit(void) -{ - platform_driver_unregister(&keypad_driver); -} - -module_init(keypad_init); -module_exit(keypad_exit); +module_platform_driver(keypad_driver); MODULE_AUTHOR("Cyril Chemparathy"); MODULE_DESCRIPTION("TNETV107X Keypad Driver"); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index a26922cf0e8..a588578037e 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -460,18 +460,7 @@ static struct platform_driver twl4030_kp_driver = { .owner = THIS_MODULE, }, }; - -static int __init twl4030_kp_init(void) -{ - return platform_driver_register(&twl4030_kp_driver); -} -module_init(twl4030_kp_init); - -static void __exit twl4030_kp_exit(void) -{ - platform_driver_unregister(&twl4030_kp_driver); -} -module_exit(twl4030_kp_exit); +module_platform_driver(twl4030_kp_driver); MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("TWL4030 Keypad Driver"); diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c index 318586dadac..99bbb7e775a 100644 --- a/drivers/input/keyboard/w90p910_keypad.c +++ b/drivers/input/keyboard/w90p910_keypad.c @@ -262,19 +262,7 @@ static struct platform_driver w90p910_keypad_driver = { .owner = THIS_MODULE, }, }; - -static int __init w90p910_keypad_init(void) -{ - return platform_driver_register(&w90p910_keypad_driver); -} - -static void __exit w90p910_keypad_exit(void) -{ - platform_driver_unregister(&w90p910_keypad_driver); -} - -module_init(w90p910_keypad_init); -module_exit(w90p910_keypad_exit); +module_platform_driver(w90p910_keypad_driver); MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); MODULE_DESCRIPTION("w90p910 keypad driver"); |