> -----Original Message----- > From: kyungmin78@xxxxxxxxx [mailto:kyungmin78@xxxxxxxxx] On Behalf Of > Kyungmin Park > Sent: Monday, September 07, 2009 12:25 PM > To: 양진성 > Cc: linux-input@xxxxxxxxxxxxxxx; laforge@xxxxxxxxxxxx; ben-linux@xxxxxxxxx; > 김경일/AP개발팀/E3/삼성전자; 김국진/AP개발팀/E5/삼성전자 > Subject: Re: [PATCH 03/03] [INPUT][KEYBOARD] Add new keypad driver for s3c > series SoCs > > Hi, > > 2009/9/5 양진성 <jsgood.yang@xxxxxxxxxxx>: > > This keypad driver supports Samsung s3c based SoCs such as s3c6410. > > This driver is written with input device compatibles. > > > > Signed-off-by: Jinsung Yang <jsgood.yang@xxxxxxxxxxx> > > Signed-off-by: Kyeongil Kim <ki0351.kim@xxxxxxxxxxx> > > --- > > drivers/input/keyboard/s3c-keypad.c | 468 > +++++++++++++++++++++++++++++++++++ > > 1 files changed, 468 insertions(+), 0 deletions(-) > > create mode 100644 drivers/input/keyboard/s3c-keypad.c > > > > diff --git a/drivers/input/keyboard/s3c-keypad.c > b/drivers/input/keyboard/s3c-keypad.c > > new file mode 100644 > > index 0000000..73b9a90 > > --- /dev/null > > +++ b/drivers/input/keyboard/s3c-keypad.c > > @@ -0,0 +1,468 @@ > > +/* > > + * linux/drivers/input/keyboard/s3c_keypad.c > > + * > > + * Driver for Samsung SoC matrix keypad controller. > > + * > > + * 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 <linux/module.h> > > +#include <linux/kernel.h> > > +#include <linux/init.h> > > +#include <linux/interrupt.h> > > +#include <linux/input.h> > > +#include <linux/platform_device.h> > > +#include <linux/miscdevice.h> > > +#include <linux/clk.h> > > +#include <linux/delay.h> > > +#include <linux/io.h> > > + > > +#include <mach/hardware.h> > > +#include <asm/irq.h> > > + > > +#include <plat/keypad.h> > > +#include <plat/regs-keypad.h> > > + > > +#undef DEBUG > > + > > +static const unsigned char s3c_keycode[] = { > > + 1, 2, KEY_1, KEY_Q, KEY_A, 6, 7, KEY_LEFT, > > + 9, 10, KEY_2, KEY_W, KEY_S, KEY_Z, KEY_RIGHT, 16, > > + 17, 18, KEY_3, KEY_E, KEY_D, KEY_X, 23, KEY_UP, > > + 25, 26, KEY_4, KEY_R, KEY_F, KEY_C, 31, 32, > > + 33, KEY_O, KEY_5, KEY_T, KEY_G, KEY_V, KEY_DOWN, KEY_BACKSPACE, > > + KEY_P, KEY_0, KEY_6, KEY_Y, KEY_H, KEY_SPACE, 47, 48, > > + KEY_M, KEY_L, KEY_7, KEY_U, KEY_J, KEY_N, 55, KEY_ENTER, > > + KEY_LEFTSHIFT, KEY_9, KEY_8, KEY_I, KEY_K, KEY_B, 63, KEY_COMMA, > > +}; > > Why do you use fixed keycode? Dose it provided from board specific > platform? and If we want to use only 3 * 3 keypad then how do you > handle this one? > That keycode array is just default value. The input device layer supports EVIOCGKEYCODE and EVIOCSKEYCODE ioctls to change keymap by user application. > > + > > +struct s3c_keypad { > > + struct s3c_platform_keypad *pdata; > > + unsigned char keycodes[ARRAY_SIZE(s3c_keycode)]; > > We don't need full keycode array here. It should be allocated with > required size. > > > + struct input_dev *dev; > > + struct timer_list timer; > > + void __iomem *regs; > > + struct clk *clk; > > + int irq; > > + int timer_enabled; > > + unsigned int prevmask_low; > > + unsigned int prevmask_high; > > + unsigned int keyifcon; > > + unsigned int keyiffc; > > +}; > > + > > +static int s3c_keypad_scan(struct s3c_keypad *keypad, u32 *keymask_low, > > + u32 *keymask_high) > > +{ > > + struct s3c_platform_keypad *pdata = keypad->pdata; > > + int i, j = 0; > > + u32 cval, rval, cfg; > > + > > + for (i = 0; i < pdata->nr_cols; i++) { > > + cval = readl(keypad->regs + S3C_KEYIFCOL); > > + cval |= S3C_KEYIF_COL_DMASK; > > + cval &= ~(1 << i); > > + writel(cval, keypad->regs + S3C_KEYIFCOL); > > + udelay(pdata->delay); > > + > > + rval = ~(readl(keypad->regs + S3C_KEYIFROW)) & > > + S3C_KEYIF_ROW_DMASK; > > + > > + if ((i * pdata->nr_rows) < pdata->max_masks) > > + *keymask_low |= (rval << (i * pdata->nr_rows)); > > + else { > > + *keymask_high |= (rval << (j * pdata->nr_rows)); > > + j++; > > + } > > + } > > + > > + cfg = readl(keypad->regs + S3C_KEYIFCOL); > > + cfg &= ~S3C_KEYIF_COL_MASK_ALL; > > + writel(cfg, keypad->regs + S3C_KEYIFCOL); > > + > > + return 0; > > +} > > + > > +static void s3c_keypad_timer_handler(unsigned long data) > > +{ > > + struct s3c_keypad *keypad = (struct s3c_keypad *)data; > > + struct s3c_platform_keypad *pdata = keypad->pdata; > > + struct input_dev *input = keypad->dev; > > + u32 keymask_low = 0, keymask_high = 0; > > + u32 press_mask_low, press_mask_high; > > + u32 release_mask_low, release_mask_high, code, cfg; > > + int i; > > + > > + s3c_keypad_scan(keypad, &keymask_low, &keymask_high); > > + > > + if (keymask_low != keypad->prevmask_low) { > > + press_mask_low = ((keymask_low ^ keypad->prevmask_low) & > > + keymask_low); > > + release_mask_low = ((keymask_low ^ keypad->prevmask_low) & > > + keypad->prevmask_low); > > + > > + i = 0; > > + while (press_mask_low) { > > + if (press_mask_low & 1) { > > + code = keypad->keycodes[i]; > > + input_report_key(input, code, 1); > > + dev_dbg(&input->dev, "low pressed: %d\n", i); > > + } > > + press_mask_low >>= 1; > > + i++; > > + } > > + > > + i = 0; > > + while (release_mask_low) { > > + if (release_mask_low & 1) { > > + code = keypad->keycodes[i]; > > + input_report_key(input, code, 0); > > + dev_dbg(&input->dev, "low released : %d\n", > i); > > + } > > + release_mask_low >>= 1; > > + i++; > > + } > > + keypad->prevmask_low = keymask_low; > > + } > > + > > + if (keymask_high != keypad->prevmask_high) { > > + press_mask_high = ((keymask_high ^ keypad->prevmask_high) > & > > + keymask_high); > > + release_mask_high = ((keymask_high ^ keypad->prevmask_high) > & > > + keypad->prevmask_high); > > + > > + i = 0; > > + while (press_mask_high) { > > + if (press_mask_high & 1) { > > + code = keypad->keycodes[i + pdata- > >max_masks]; > > + input_report_key(input, code, 1); > > + dev_dbg(&input->dev, "high pressed: %d %d\n", > > + keypad->keycodes[i + pdata- > >max_masks], > > + i); > > + } > > + press_mask_high >>= 1; > > + i++; > > + } > > + > > + i = 0; > > + while (release_mask_high) { > > + if (release_mask_high & 1) { > > + code = keypad->keycodes[i + pdata- > >max_masks]; > > + input_report_key(input, code, 0); > > + dev_dbg(&input->dev, "high released: %d\n", > > + keypad->keycodes[i + pdata- > >max_masks]); > > + } > > + release_mask_high >>= 1; > > + i++; > > + } > > + keypad->prevmask_high = keymask_high; > > + } > > + > > + if (keymask_low | keymask_high) { > > + mod_timer(&keypad->timer, jiffies + HZ / 10); > > + } else { > > + cfg = readl(keypad->regs + S3C_KEYIFCON); > > + cfg &= ~S3C_KEYIF_CON_MASK_ALL; > > + cfg |= (S3C_KEYIF_INT_F_EN | S3C_KEYIF_INT_R_EN | > > + S3C_KEYIF_DF_EN | S3C_KEYIF_FC_EN); > > + writel(cfg, keypad->regs + S3C_KEYIFCON); > > + > > + keypad->timer_enabled = 0; > > + } > > +} > > + > > +static irqreturn_t s3c_keypad_irq(int irq, void *dev_id) > > +{ > > + struct s3c_keypad *keypad = dev_id; > > + u32 cfg; > > + > > + /* disable keypad interrupt and schedule for keypad timer handler > */ > > + cfg = readl(keypad->regs + S3C_KEYIFCON); > > + cfg &= ~(S3C_KEYIF_INT_F_EN | S3C_KEYIF_INT_R_EN); > > + writel(cfg, keypad->regs + S3C_KEYIFCON); > > + > > + keypad->timer.expires = jiffies + (HZ / 100); > > + if (keypad->timer_enabled) { > > + mod_timer(&keypad->timer, keypad->timer.expires); > > + } else { > > + add_timer(&keypad->timer); > > + keypad->timer_enabled = 1; > > + } > > + > > + /* clear the keypad interrupt status */ > > + writel(S3C_KEYIF_STSCLR_CLEAR, keypad->regs + S3C_KEYIFSTSCLR); > > + > > + return IRQ_HANDLED; > > +} > > Why do you use timer. As other input drivers how about to use workqueue? > > > + > > +static int s3c_keypad_open(struct input_dev *dev) > > +{ > > + struct s3c_keypad *keypad = input_get_drvdata(dev); > > + u32 cfg; > > + > > + clk_enable(keypad->clk); > > + > > + /* init keypad h/w block */ > > + cfg = readl(keypad->regs + S3C_KEYIFCON); > > + cfg &= ~S3C_KEYIF_CON_MASK_ALL; > > + cfg |= (S3C_KEYIF_INT_F_EN | S3C_KEYIF_INT_R_EN | > > + S3C_KEYIF_DF_EN | S3C_KEYIF_FC_EN); > > + writel(cfg, keypad->regs + S3C_KEYIFCON); > > + > > + cfg = readl(keypad->regs + S3C_KEYIFFC); > > + cfg |= 0x1; > > What's the meaning of '0x1'? > > > + writel(cfg, keypad->regs + S3C_KEYIFFC); > > + > > + cfg = readl(keypad->regs + S3C_KEYIFCOL); > > + cfg &= ~S3C_KEYIF_COL_MASK_ALL; > > + writel(cfg, keypad->regs + S3C_KEYIFCOL); > > + > > + /* Scan timer init */ > > + init_timer(&keypad->timer); > > + keypad->timer.function = s3c_keypad_timer_handler; > > + keypad->timer.data = (unsigned long)keypad; > > + keypad->timer.expires = jiffies + (HZ / 10); > > + > > + if (keypad->timer_enabled) { > > + mod_timer(&keypad->timer, keypad->timer.expires); > > + } else { > > useless curly braces > > > + add_timer(&keypad->timer); > > + keypad->timer_enabled = 1; > > + } > > + > > + return 0; > > +} > > + > > +static void s3c_keypad_close(struct input_dev *dev) > > +{ > > + struct s3c_keypad *keypad = input_get_drvdata(dev); > > + > > + clk_disable(keypad->clk); > > +} > > + > > +#define res_size(res) ((res)->end - (res)->start + 1) > > + > > +static int __devinit s3c_keypad_probe(struct platform_device *pdev) > > +{ > > + struct s3c_platform_keypad *pdata; > > + struct s3c_keypad *keypad; > > + struct input_dev *input; > > + struct resource *res; > > + int irq, error, i; > > + > > + pdata = pdev->dev.platform_data; > > + if (pdata == NULL) { > > + dev_err(&pdev->dev, "no platform data\n"); > > + return -EINVAL; > > + } > > + > > + keypad = kzalloc(sizeof(struct s3c_keypad), GFP_KERNEL); > > + if (keypad == NULL) { > > + dev_err(&pdev->dev, "failed to allocate driver data\n"); > > + return -ENOMEM; > > + } > > + > > + keypad->pdata = pdata; > > + memcpy(keypad->keycodes, s3c_keycode, sizeof(keypad->keycodes)); > > memcpy??? if you don't modify s3c_keycode. just assign it. > > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + if (res == NULL) { > > + dev_err(&pdev->dev, "failed to get I/O memory\n"); > > + error = -ENXIO; > > + goto err_get_io; > > + } > > + > > + res = request_mem_region(res->start, res_size(res), pdev->name); > > + if (res == NULL) { > > + dev_err(&pdev->dev, "failed to request I/O memory\n"); > > + error = -EBUSY; > > + goto err_get_io; > > + } > > + > > + keypad->regs = ioremap(res->start, res_size(res)); > > + if (keypad->regs == NULL) { > > + dev_err(&pdev->dev, "failed to remap I/O memory\n"); > > + error = -ENXIO; > > + goto err_map_io; > > + } > > + > > + keypad->clk = clk_get(&pdev->dev, "keypad"); > > + if (IS_ERR(keypad->clk)) { > > + dev_err(&pdev->dev, "failed to get keypad clock\n"); > > + error = PTR_ERR(keypad->clk); > > + goto err_clk; > > + } > > + > > + /* Create and register the input driver. */ > > + input = input_allocate_device(); > > + if (!input) { > > + dev_err(&pdev->dev, "failed to allocate input device\n"); > > + error = -ENOMEM; > > + goto err_alloc_input; > > + } > > + > > + input->name = pdev->name; > > + input->id.bustype = BUS_HOST; > > + input->open = s3c_keypad_open; > > + input->close = s3c_keypad_close; > > + input->dev.parent = &pdev->dev; > > + input->keycode = (void *)keypad->keycodes; > > + input->keycodesize = sizeof(keypad->keycodes[0]); > > + input->keycodemax = ARRAY_SIZE(keypad->keycodes); > > + > > + keypad->dev = input; > > + > > + input_set_drvdata(input, keypad); > > + > > + __set_bit(EV_KEY, input->evbit); > > + __set_bit(EV_REP, input->evbit); > > + > > + for (i = 0; i < pdata->max_keys; i++) { > > + keypad->keycodes[i] = s3c_keycode[i]; > > + if (keypad->keycodes[i] <= 0) > > + continue; > > + > > + __set_bit(keypad->keycodes[i] & KEY_MAX, input->keybit); > > + } > > + > > + irq = platform_get_irq(pdev, 0); > > + if (irq < 0) { > > + dev_err(&pdev->dev, "failed to get keypad irq\n"); > > + error = -ENXIO; > > + goto err_get_irq; > > + } > > + > > + platform_set_drvdata(pdev, keypad); > > + > > + error = request_irq(irq, s3c_keypad_irq, IRQF_DISABLED, > > + pdev->name, keypad); > > + if (error) { > > + dev_err(&pdev->dev, "failed to request IRQ\n"); > > + goto err_req_irq; > > + } > > + > > + keypad->irq = irq; > > + > > + /* Register the input device */ > > + error = input_register_device(input); > > + if (error) { > > + dev_err(&pdev->dev, "failed to register input device\n"); > > + goto err_reg_input; > > + } > > + > > + device_init_wakeup(&pdev->dev, 1); > > + dev_info(&pdev->dev, "Samsung Keypad Interface driver\n"); > > + > > + return 0; > > + > > +err_reg_input: > > + free_irq(irq, pdev); > > + > > +err_req_irq: > > + platform_set_drvdata(pdev, NULL); > > + > > +err_get_irq: > > + input_free_device(input); > > + > > +err_alloc_input: > > + clk_put(keypad->clk); > > + > > +err_clk: > > + iounmap(keypad->regs); > > + > > +err_map_io: > > + release_mem_region(res->start, res_size(res)); > > + > > +err_get_io: > > + kfree(keypad); > > + > > + return error; > > +} > > + > > +static int __devexit s3c_keypad_remove(struct platform_device *pdev) > > +{ > > + struct s3c_keypad *keypad = platform_get_drvdata(pdev); > > + struct resource *res; > > + > > + free_irq(keypad->irq, pdev); > > + > > + clk_disable(keypad->clk); > > + clk_put(keypad->clk); > > + > > + input_unregister_device(keypad->dev); > > + input_free_device(keypad->dev); > > + > > + iounmap(keypad->regs); > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + release_mem_region(res->start, res_size(res)); > > + > > + platform_set_drvdata(pdev, NULL); > > + kfree(keypad); > > + > > + return 0; > > +} > > + > > +#ifdef CONFIG_PM > > +static int s3c_keypad_suspend(struct platform_device *dev, pm_message_t > state) > > +{ > > + struct s3c_keypad *keypad = platform_get_drvdata(pdev); > > + > > + keypad->keyifcon = readl(keypad->regs + S3C_KEYIFCON); > > + keypad->keyiffc = readl(keypad->regs + S3C_KEYIFFC); > > + > > + disable_irq(IRQ_KEYPAD); > > + clk_disable(keypad->clk); > > + > > + return 0; > > +} > > + > > +static int s3c_keypad_resume(struct platform_device *dev) > > +{ > > + struct s3c_keypad *keypad = platform_get_drvdata(pdev); > > + > > + clk_enable(keypad->clock); > > + > > + writel(keypad->keyifcon, keypad->regs + S3C_KEYIFCON); > > + writel(keypad->keyiffc, keypad->regs + S3C_KEYIFFC); > > + > > + enable_irq(IRQ_KEYPAD); > > + > > + return 0; > > +} > > +#else > > +#define s3c_keypad_suspend NULL > > +#define s3c_keypad_resume NULL > > +#endif /* CONFIG_PM */ > > + > > +static struct platform_driver s3c_keypad_driver = { > > + .probe = s3c_keypad_probe, > > + .remove = s3c_keypad_remove, > > + .suspend = s3c_keypad_suspend, > > + .resume = s3c_keypad_resume, > > + .driver = { > > + .owner = THIS_MODULE, > > + .name = "s3c-keypad", > > + }, > > +}; > > + > > +static int __init s3c_keypad_init(void) > > +{ > > + return platform_driver_register(&s3c_keypad_driver); > > +} > > + > > +static void __exit s3c_keypad_exit(void) > > +{ > > + platform_driver_unregister(&s3c_keypad_driver); > > +} > > + > > +module_init(s3c_keypad_init); > > +module_exit(s3c_keypad_exit); > > + > > +MODULE_AUTHOR("Kyeongil, Kim <ki0351.kim@xxxxxxxxxxx>"); > > +MODULE_AUTHOR("Jinsung, Yang <jsgood.yang@xxxxxxxxxxx>"); > > +MODULE_LICENSE("GPL v2"); > > +MODULE_DESCRIPTION("Keypad interface for Samsung SoC"); > > + > > Finally, > We did duplicated works. We already implement the keypad driver for > s5pc100 & s5pc110. but we use workqueue structure instead of timer. > > I will post the our works. > > Thank you, > Kyungmin Park -- 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