On Sun, May 30, 2010 at 12:06:24PM +0900, Joonyoung Shim wrote: > This patch adds support for keypad driver running on Samsung cpus. This > driver is tested on GONI and Aquila board using S5PC110 cpu. > > Signed-off-by: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> > Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> > --- > drivers/input/keyboard/Kconfig | 9 + > drivers/input/keyboard/Makefile | 1 + > drivers/input/keyboard/samsung-keypad.c | 364 +++++++++++++++++++++++++++++++ > 3 files changed, 374 insertions(+), 0 deletions(-) > create mode 100644 drivers/input/keyboard/samsung-keypad.c > > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig > index d8fa5d7..bf6a50f 100644 > --- a/drivers/input/keyboard/Kconfig > +++ b/drivers/input/keyboard/Kconfig > @@ -342,6 +342,15 @@ config KEYBOARD_PXA930_ROTARY > To compile this driver as a module, choose M here: the > module will be called pxa930_rotary. > > +config KEYBOARD_SAMSUNG > + tristate "Samsung keypad support" > + depends on SAMSUNG_DEV_KEYPAD > + help > + Say Y here if you want to use the Samsung keypad. > + > + To compile this driver as a module, choose M here: the > + module will be called samsung-keypad. > + > config KEYBOARD_STOWAWAY > tristate "Stowaway keyboard" > select SERIO > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile > index 4596d0c..8f973ed 100644 > --- a/drivers/input/keyboard/Makefile > +++ b/drivers/input/keyboard/Makefile > @@ -32,6 +32,7 @@ obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o > obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o > obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o > obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o > +obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o > obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o > obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o > obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o > diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c > new file mode 100644 > index 0000000..f4bcf97 > --- /dev/null > +++ b/drivers/input/keyboard/samsung-keypad.c > @@ -0,0 +1,364 @@ > +/* > + * samsung-keypad.c -- Samsung keypad driver > + * > + * Copyright (C) 2010 Samsung Electronics Co.Ltd > + * Author: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> > + * Author: Donghwa Lee <dh09.lee@xxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2 of the License, or (at your > + * option) any later version. > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/init.h> > +#include <linux/input.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <plat/cpu.h> you should _not_ be including <plat/cpu.h> here. and it looks form the rest of the file that there's nothing from it being used. > +#include <plat/keypad.h> > +#include <plat/regs-keypad.h> > + > +struct samsung_kp { > + struct input_dev *input_dev; > + struct timer_list timer; > + struct clk *clk; > + struct work_struct work; > + void __iomem *base; > + unsigned short *keycodes; > + unsigned int row_shift; > + unsigned int rows; > + unsigned int cols; > + unsigned int row_state[SAMSUNG_MAX_COLS]; > + int irq; > +}; > + > +static void samsung_kp_scan(struct samsung_kp *keypad, unsigned int *row_state) > +{ > + unsigned int col; > + unsigned int val; > + > + for (col = 0; col < keypad->cols; col++) { > +#if CONFIG_ARCH_S5PV210 > + val = S5PV210_KEYIFCOLEN_MASK; > + val &= ~(1 << col) << 8; > +#else > + val = SAMSUNG_KEYIFCOL_MASK; > + val &= ~(1 << col); > +#endif no platform specific #ifdefs > + writel(val, keypad->base + SAMSUNG_KEYIFCOL); > + mdelay(1); > + > + val = readl(keypad->base + SAMSUNG_KEYIFROW); > + row_state[col] = ~val & ((1 << keypad->rows) - 1); > + } > + > + /* KEYIFCOL reg clear */ > + writel(0, keypad->base + SAMSUNG_KEYIFCOL); > +} > + > +static void samsung_kp_worker(struct work_struct *work) > +{ > + struct samsung_kp *keypad = container_of(work, struct samsung_kp, work); > + unsigned int row_state[SAMSUNG_MAX_COLS]; > + unsigned int val; > + unsigned int changed; > + unsigned int pressed; > + unsigned int key_down = 0; > + int col, row; > + > + clk_enable(keypad->clk); > + > + val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); > + > + /* interrupt clear */ > + writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); > + > + val = readl(keypad->base + SAMSUNG_KEYIFCON); > + val &= ~(SAMSUNG_INT_F_EN | SAMSUNG_INT_R_EN); > + writel(val, keypad->base + SAMSUNG_KEYIFCON); > + > + samsung_kp_scan(keypad, row_state); > + > + for (col = 0; col < keypad->cols; col++) { > + changed = row_state[col] ^ keypad->row_state[col]; > + key_down |= row_state[col]; > + if (!changed) > + continue; > + > + for (row = 0; row < keypad->rows; row++) { > + if (!(changed & (1 << row))) > + continue; > + > + pressed = row_state[col] & (1 << row); > + > + dev_dbg(&keypad->input_dev->dev, > + "key %s, row: %d, col: %d\n", > + pressed ? "pressed" : "released", row, col); > + > + val = MATRIX_SCAN_CODE(row, col, keypad->row_shift); > + > + input_report_key(keypad->input_dev, > + keypad->keycodes[val], pressed); > + input_sync(keypad->input_dev); > + } > + } > + memcpy(keypad->row_state, row_state, sizeof(row_state)); > + > + if (key_down) > + mod_timer(&keypad->timer, jiffies + HZ / 20); > + else { > + /* enable interrupt bit */ > + val = readl(keypad->base + SAMSUNG_KEYIFCON); > + val |= (SAMSUNG_INT_F_EN | SAMSUNG_INT_R_EN); > + writel(val, keypad->base + SAMSUNG_KEYIFCON); > + enable_irq(keypad->irq); > + } > + clk_disable(keypad->clk); > +} > + > +static irqreturn_t samsung_kp_interrupt(int irq, void *dev_id) > +{ > + struct samsung_kp *keypad = dev_id; > + > + if (!work_pending(&keypad->work)) { > + disable_irq_nosync(keypad->irq); > + schedule_work(&keypad->work); > + } > + > + return IRQ_HANDLED; > +} > + > +static void samsung_kp_timer(unsigned long data) > +{ > + struct samsung_kp *keypad = (struct samsung_kp *)data; > + > + schedule_work(&keypad->work); > +} > + > +static int __devinit samsung_kp_probe(struct platform_device *pdev) > +{ > + const struct samsung_kp_platdata *pdata; > + const struct matrix_keymap_data *keymap_data; > + struct samsung_kp *keypad; > + struct resource *res; > + struct input_dev *input_dev; > + unsigned short *keycodes; > + unsigned int row_shift; > + unsigned int val; > + int ret; > + > + pdata = pdev->dev.platform_data; > + if (!pdata) { > + dev_err(&pdev->dev, "no platform data defined\n"); > + return -EINVAL; > + } > + > + keymap_data = pdata->keymap_data; > + if (!keymap_data) { > + dev_err(&pdev->dev, "no keymap data defined\n"); > + return -EINVAL; > + } > + > + if ((pdata->rows <= 0) || (pdata->rows > SAMSUNG_MAX_ROWS)) > + return -EINVAL; how about making pdata->rows unsigned then if -ve values are illegal > + if ((pdata->cols <= 0) || (pdata->cols > SAMSUNG_MAX_COLS)) > + return -EINVAL; > + > + /* initialize the gpio */ > + if (pdata->cfg_gpio) > + pdata->cfg_gpio(pdata->rows, pdata->cols); > + > + row_shift = get_count_order(pdata->cols); > + keypad = kzalloc(sizeof(*keypad), GFP_KERNEL); > + keycodes = kzalloc((pdata->rows << row_shift) * sizeof(*keycodes), > + GFP_KERNEL); you could allocate this in one go. > + input_dev = input_allocate_device(); > + if (!keypad || !keycodes || !input_dev) { > + ret = -ENOMEM; > + goto err_free_mem; > + } > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + ret = -ENODEV; > + goto err_free_mem; > + } > + > + keypad->base = ioremap(res->start, resource_size(res)); > + if (!keypad->base) { > + ret = -EBUSY; > + goto err_free_mem; > + } > + > + keypad->clk = clk_get(&pdev->dev, "keypad"); > + if (IS_ERR(keypad->clk)) { > + dev_err(&pdev->dev, "failed to get keypad clk\n"); > + ret = PTR_ERR(keypad->clk); > + goto err_unmap_base; > + } > + clk_enable(keypad->clk); > + > + keypad->input_dev = input_dev; > + keypad->keycodes = keycodes; > + keypad->row_shift = row_shift; > + keypad->rows = pdata->rows; > + keypad->cols = pdata->cols; > + > + INIT_WORK(&keypad->work, samsung_kp_worker); > + > + setup_timer(&keypad->timer, samsung_kp_timer, (unsigned long)keypad); > + > + /* enable interrupt and wakeup bit */ > + val = SAMSUNG_INT_F_EN | SAMSUNG_INT_R_EN | SAMSUNG_WAKEUPEN; > + writel(val, keypad->base + SAMSUNG_KEYIFCON); > + > + /* KEYIFCOL reg clear */ > + writel(0, keypad->base + SAMSUNG_KEYIFCOL); > + > + keypad->irq = platform_get_irq(pdev, 0); > + if (keypad->irq < 0) { > + ret = keypad->irq; > + goto err_disable_clk; > + } > + > + ret = request_irq(keypad->irq, samsung_kp_interrupt, 0, > + dev_name(&pdev->dev), keypad); > + > + if (ret) > + goto err_disable_clk; > + > + input_dev->name = pdev->name; > + input_dev->id.bustype = BUS_HOST; > + input_dev->dev.parent = &pdev->dev; > + > + input_dev->evbit[0] = BIT_MASK(EV_KEY); > + if (pdata->rep) > + input_dev->evbit[0] |= BIT_MASK(EV_REP); > + > + input_dev->keycode = keycodes; > + input_dev->keycodesize = sizeof(*keycodes); > + input_dev->keycodemax = pdata->rows << row_shift; > + > + matrix_keypad_build_keymap(keymap_data, row_shift, > + input_dev->keycode, input_dev->keybit); > + > + ret = input_register_device(keypad->input_dev); > + if (ret) > + goto err_free_irq; > + > + platform_set_drvdata(pdev, keypad); > + clk_disable(keypad->clk); > + > + return 0; > + > +err_free_irq: > + free_irq(keypad->irq, keypad); > +err_disable_clk: > + clk_disable(keypad->clk); > + clk_put(keypad->clk); > +err_unmap_base: > + iounmap(keypad->base); > +err_free_mem: > + input_free_device(input_dev); > + kfree(keycodes); > + kfree(keypad); > + > + return ret; > +} > + > +static int __devexit samsung_kp_remove(struct platform_device *pdev) > +{ > + struct samsung_kp *keypad = platform_get_drvdata(pdev); > + > + free_irq(keypad->irq, keypad); > + cancel_work_sync(&keypad->work); > + del_timer_sync(&keypad->timer); > + > + platform_set_drvdata(pdev, NULL); > + input_unregister_device(keypad->input_dev); > + > + clk_disable(keypad->clk); > + clk_put(keypad->clk); > + > + iounmap(keypad->base); > + kfree(keypad->keycodes); > + kfree(keypad); > + > + return 0; > +} > + > +#ifdef CONFIG_PM > +static int samsung_kp_suspend(struct device *dev) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct samsung_kp *keypad = platform_get_drvdata(pdev); > + > + disable_irq(keypad->irq); > + > + return 0; > +} > + > +static int samsung_kp_resume(struct device pdev) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct samsung_kp *keypad = platform_get_drvdata(pdev); > + unsigned int val; > + > + clk_enable(keypad->clk); > + > + /* enable interrupt and wakeup bit */ > + val = SAMSUNG_INT_F_EN | SAMSUNG_INT_R_EN | SAMSUNG_WAKEUPEN; > + writel(val, keypad->base + SAMSUNG_KEYIFCON); > + > + /* KEYIFCOL reg clear */ > + writel(0, keypad->base + SAMSUNG_KEYIFCOL); > + > + enable_irq(keypad->irq); > + clk_disable(keypad->clk); > + > + return 0; > +} > + > +static const struct dev_pm_ops samsung_kp_pm_ops = { > + .suspend = samsung_kp_suspend, > + .resume = samsung_kp_resume, > +}; > +#endif > + > +static struct platform_driver samsung_kp_driver = { > + .probe = samsung_kp_probe, > + .remove = __devexit_p(samsung_kp_remove), > + .driver = { > + .name = "samsung-keypad", > + .owner = THIS_MODULE, > +#ifdef CONFIG_PM > + .pm = &samsung_kp_pm_ops, > +#endif > + }, > +}; > + > +static int __init samsung_kp_init(void) > +{ > + return platform_driver_register(&samsung_kp_driver); > +} > + > +static void __exit samsung_kp_exit(void) > +{ > + platform_driver_unregister(&samsung_kp_driver); > +} > + > +module_init(samsung_kp_init); > +module_exit(samsung_kp_exit); > + > +MODULE_DESCRIPTION("Samsung keypad driver"); > +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@xxxxxxxxxxx>"); > +MODULE_AUTHOR("Donghwa Lee <dh09.lee@xxxxxxxxxxx>"); > +MODULE_LICENSE("GPL"); > -- > 1.7.0.4 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@xxxxxxxxxxxxxxxxxxx > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- -- Ben Q: What's a light-year? A: One-third less calories than a regular year. -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html