diff options
Diffstat (limited to 'drivers/input/keyboard/omap4-keypad.c')
-rw-r--r-- | drivers/input/keyboard/omap4-keypad.c | 240 |
1 files changed, 176 insertions, 64 deletions
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index e809ac095a38..c05f98c41410 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -27,6 +27,7 @@ #include <linux/platform_device.h> #include <linux/errno.h> #include <linux/io.h> +#include <linux/of.h> #include <linux/input.h> #include <linux/slab.h> #include <linux/pm_runtime.h> @@ -68,19 +69,53 @@ #define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF +enum { + KBD_REVISION_OMAP4 = 0, + KBD_REVISION_OMAP5, +}; + struct omap4_keypad { struct input_dev *input; void __iomem *base; - int irq; + unsigned int irq; unsigned int rows; unsigned int cols; + u32 reg_offset; + u32 irqreg_offset; unsigned int row_shift; + bool no_autorepeat; unsigned char key_state[8]; - unsigned short keymap[]; + unsigned short *keymap; }; +static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset) +{ + return __raw_readl(keypad_data->base + + keypad_data->reg_offset + offset); +} + +static void kbd_writel(struct omap4_keypad *keypad_data, u32 offset, u32 value) +{ + __raw_writel(value, + keypad_data->base + keypad_data->reg_offset + offset); +} + +static int kbd_read_irqreg(struct omap4_keypad *keypad_data, u32 offset) +{ + return __raw_readl(keypad_data->base + + keypad_data->irqreg_offset + offset); +} + +static void kbd_write_irqreg(struct omap4_keypad *keypad_data, + u32 offset, u32 value) +{ + __raw_writel(value, + keypad_data->base + keypad_data->irqreg_offset + offset); +} + + /* Interrupt handler */ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) { @@ -91,12 +126,11 @@ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) u32 *new_state = (u32 *) key_state; /* Disable interrupts */ - __raw_writel(OMAP4_VAL_IRQDISABLE, - keypad_data->base + OMAP4_KBD_IRQENABLE); + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, + OMAP4_VAL_IRQDISABLE); - *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0); - *(new_state + 1) = __raw_readl(keypad_data->base - + OMAP4_KBD_FULLCODE63_32); + *new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); + *(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); for (row = 0; row < keypad_data->rows; row++) { changed = key_state[row] ^ keypad_data->key_state[row]; @@ -121,12 +155,13 @@ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) sizeof(keypad_data->key_state)); /* clear pending interrupts */ - __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS), - keypad_data->base + OMAP4_KBD_IRQSTATUS); + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, + kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)); /* enable interrupts */ - __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, - keypad_data->base + OMAP4_KBD_IRQENABLE); + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, + OMAP4_DEF_IRQENABLE_EVENTEN | + OMAP4_DEF_IRQENABLE_LONGKEY); return IRQ_HANDLED; } @@ -139,16 +174,17 @@ static int omap4_keypad_open(struct input_dev *input) disable_irq(keypad_data->irq); - __raw_writel(OMAP4_VAL_FUNCTIONALCFG, - keypad_data->base + OMAP4_KBD_CTRL); - __raw_writel(OMAP4_VAL_DEBOUNCINGTIME, - keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME); - __raw_writel(OMAP4_VAL_IRQDISABLE, - keypad_data->base + OMAP4_KBD_IRQSTATUS); - __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, - keypad_data->base + OMAP4_KBD_IRQENABLE); - __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA, - keypad_data->base + OMAP4_KBD_WAKEUPENABLE); + kbd_writel(keypad_data, OMAP4_KBD_CTRL, + OMAP4_VAL_FUNCTIONALCFG); + kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME, + OMAP4_VAL_DEBOUNCINGTIME); + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, + OMAP4_VAL_IRQDISABLE); + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, + OMAP4_DEF_IRQENABLE_EVENTEN | + OMAP4_DEF_IRQENABLE_LONGKEY); + kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE, + OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA); enable_irq(keypad_data->irq); @@ -162,36 +198,63 @@ static void omap4_keypad_close(struct input_dev *input) disable_irq(keypad_data->irq); /* Disable interrupts */ - __raw_writel(OMAP4_VAL_IRQDISABLE, - keypad_data->base + OMAP4_KBD_IRQENABLE); + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, + OMAP4_VAL_IRQDISABLE); /* clear pending interrupts */ - __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS), - keypad_data->base + OMAP4_KBD_IRQSTATUS); + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, + kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)); enable_irq(keypad_data->irq); pm_runtime_put_sync(input->dev.parent); } +#ifdef CONFIG_OF +static int __devinit omap4_keypad_parse_dt(struct device *dev, + struct omap4_keypad *keypad_data) +{ + struct device_node *np = dev->of_node; + + if (!np) { + dev_err(dev, "missing DT data"); + return -EINVAL; + } + + of_property_read_u32(np, "keypad,num-rows", &keypad_data->rows); + of_property_read_u32(np, "keypad,num-columns", &keypad_data->cols); + if (!keypad_data->rows || !keypad_data->cols) { + dev_err(dev, "number of keypad rows/columns not specified\n"); + return -EINVAL; + } + + if (of_get_property(np, "linux,input-no-autorepeat", NULL)) + keypad_data->no_autorepeat = true; + + return 0; +} +#else +static inline int omap4_keypad_parse_dt(struct device *dev, + struct omap4_keypad *keypad_data) +{ + return -ENOSYS; +} +#endif + static int __devinit omap4_keypad_probe(struct platform_device *pdev) { - const struct omap4_keypad_platform_data *pdata; + const struct omap4_keypad_platform_data *pdata = + dev_get_platdata(&pdev->dev); + const struct matrix_keymap_data *keymap_data = + pdata ? pdata->keymap_data : NULL; struct omap4_keypad *keypad_data; struct input_dev *input_dev; struct resource *res; - resource_size_t size; - unsigned int row_shift, max_keys; + unsigned int max_keys; + int rev; int irq; int error; - /* platform data */ - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "no base address specified\n"); @@ -204,25 +267,24 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) return -EINVAL; } - if (!pdata->keymap_data) { - dev_err(&pdev->dev, "no keymap data defined\n"); - return -EINVAL; - } - - row_shift = get_count_order(pdata->cols); - max_keys = pdata->rows << row_shift; - - keypad_data = kzalloc(sizeof(struct omap4_keypad) + - max_keys * sizeof(keypad_data->keymap[0]), - GFP_KERNEL); + keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL); if (!keypad_data) { dev_err(&pdev->dev, "keypad_data memory allocation failed\n"); return -ENOMEM; } - size = resource_size(res); + keypad_data->irq = irq; - res = request_mem_region(res->start, size, pdev->name); + if (pdata) { + keypad_data->rows = pdata->rows; + keypad_data->cols = pdata->cols; + } else { + error = omap4_keypad_parse_dt(&pdev->dev, keypad_data); + if (error) + return error; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); if (!res) { dev_err(&pdev->dev, "can't request mem region\n"); error = -EBUSY; @@ -236,16 +298,41 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) goto err_release_mem; } - keypad_data->irq = irq; - keypad_data->row_shift = row_shift; - keypad_data->rows = pdata->rows; - keypad_data->cols = pdata->cols; + + /* + * Enable clocks for the keypad module so that we can read + * revision register. + */ + pm_runtime_enable(&pdev->dev); + error = pm_runtime_get_sync(&pdev->dev); + if (error) { + dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); + goto err_unmap; + } + rev = __raw_readl(keypad_data->base + OMAP4_KBD_REVISION); + rev &= 0x03 << 30; + rev >>= 30; + switch (rev) { + case KBD_REVISION_OMAP4: + keypad_data->reg_offset = 0x00; + keypad_data->irqreg_offset = 0x00; + break; + case KBD_REVISION_OMAP5: + keypad_data->reg_offset = 0x10; + keypad_data->irqreg_offset = 0x0c; + break; + default: + dev_err(&pdev->dev, + "Keypad reports unsupported revision %d", rev); + error = -EINVAL; + goto err_pm_put_sync; + } /* input device allocation */ keypad_data->input = input_dev = input_allocate_device(); if (!input_dev) { error = -ENOMEM; - goto err_unmap; + goto err_pm_put_sync; } input_dev->name = pdev->name; @@ -258,19 +345,29 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) input_dev->open = omap4_keypad_open; input_dev->close = omap4_keypad_close; - input_dev->keycode = keypad_data->keymap; - input_dev->keycodesize = sizeof(keypad_data->keymap[0]); - input_dev->keycodemax = max_keys; - - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_REP, input_dev->evbit); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); + if (!keypad_data->no_autorepeat) + __set_bit(EV_REP, input_dev->evbit); input_set_drvdata(input_dev, keypad_data); - matrix_keypad_build_keymap(pdata->keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); + keypad_data->row_shift = get_count_order(keypad_data->cols); + max_keys = keypad_data->rows << keypad_data->row_shift; + keypad_data->keymap = kzalloc(max_keys * sizeof(keypad_data->keymap[0]), + GFP_KERNEL); + if (!keypad_data->keymap) { + dev_err(&pdev->dev, "Not enough memory for keymap\n"); + error = -ENOMEM; + goto err_free_input; + } + + error = matrix_keypad_build_keymap(keymap_data, NULL, + keypad_data->rows, keypad_data->cols, + keypad_data->keymap, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_free_keymap; + } error = request_irq(keypad_data->irq, omap4_keypad_interrupt, IRQF_TRIGGER_RISING, @@ -280,7 +377,7 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) goto err_free_input; } - pm_runtime_enable(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); error = input_register_device(keypad_data->input); if (error < 0) { @@ -294,12 +391,16 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) err_pm_disable: pm_runtime_disable(&pdev->dev); free_irq(keypad_data->irq, keypad_data); +err_free_keymap: + kfree(keypad_data->keymap); err_free_input: input_free_device(input_dev); +err_pm_put_sync: + pm_runtime_put_sync(&pdev->dev); err_unmap: iounmap(keypad_data->base); err_release_mem: - release_mem_region(res->start, size); + release_mem_region(res->start, resource_size(res)); err_free_keypad: kfree(keypad_data); return error; @@ -321,18 +422,29 @@ static int __devexit omap4_keypad_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); + kfree(keypad_data->keymap); kfree(keypad_data); + platform_set_drvdata(pdev, NULL); return 0; } +#ifdef CONFIG_OF +static const struct of_device_id omap_keypad_dt_match[] = { + { .compatible = "ti,omap4-keypad" }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_keypad_dt_match); +#endif + static struct platform_driver omap4_keypad_driver = { .probe = omap4_keypad_probe, .remove = __devexit_p(omap4_keypad_remove), .driver = { .name = "omap4-keypad", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_keypad_dt_match), }, }; module_platform_driver(omap4_keypad_driver); |