diff options
Diffstat (limited to 'drivers/input/keyboard/spear-keyboard.c')
| -rw-r--r-- | drivers/input/keyboard/spear-keyboard.c | 337 |
1 files changed, 195 insertions, 142 deletions
diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index bee03d64c45..258af10e581 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -12,53 +12,58 @@ #include <linux/clk.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_wakeup.h> #include <linux/slab.h> #include <linux/types.h> -#include <plat/keyboard.h> +#include <linux/platform_data/keyboard-spear.h> /* Keyboard Registers */ -#define MODE_REG 0x00 /* 16 bit reg */ -#define STATUS_REG 0x0C /* 2 bit reg */ -#define DATA_REG 0x10 /* 8 bit reg */ +#define MODE_CTL_REG 0x00 +#define STATUS_REG 0x0C +#define DATA_REG 0x10 #define INTR_MASK 0x54 /* Register Values */ -/* - * pclk freq mask = (APB FEQ -1)= 82 MHZ.Programme bit 15-9 in mode - * control register as 1010010(82MHZ) - */ -#define PCLK_FREQ_MSK 0xA400 /* 82 MHz */ -#define START_SCAN 0x0100 -#define SCAN_RATE_10 0x0000 -#define SCAN_RATE_20 0x0004 -#define SCAN_RATE_40 0x0008 -#define SCAN_RATE_80 0x000C -#define MODE_KEYBOARD 0x0002 -#define DATA_AVAIL 0x2 - -#define KEY_MASK 0xFF000000 -#define KEY_VALUE 0x00FFFFFF -#define ROW_MASK 0xF0 -#define COLUMN_MASK 0x0F -#define ROW_SHIFT 4 +#define NUM_ROWS 16 +#define NUM_COLS 16 +#define MODE_CTL_PCLK_FREQ_SHIFT 9 +#define MODE_CTL_PCLK_FREQ_MSK 0x7F + +#define MODE_CTL_KEYBOARD (0x2 << 0) +#define MODE_CTL_SCAN_RATE_10 (0x0 << 2) +#define MODE_CTL_SCAN_RATE_20 (0x1 << 2) +#define MODE_CTL_SCAN_RATE_40 (0x2 << 2) +#define MODE_CTL_SCAN_RATE_80 (0x3 << 2) +#define MODE_CTL_KEYNUM_SHIFT 6 +#define MODE_CTL_START_SCAN (0x1 << 8) + +#define STATUS_DATA_AVAIL (0x1 << 1) + +#define DATA_ROW_MASK 0xF0 +#define DATA_COLUMN_MASK 0x0F + +#define ROW_SHIFT 4 struct spear_kbd { struct input_dev *input; - struct resource *res; void __iomem *io_base; struct clk *clk; unsigned int irq; + unsigned int mode; + unsigned int suspended_rate; unsigned short last_key; - unsigned short keycodes[256]; + unsigned short keycodes[NUM_ROWS * NUM_COLS]; + bool rep; + bool irq_wake_enabled; + u32 mode_ctl_reg; }; static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) @@ -66,10 +71,10 @@ static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) struct spear_kbd *kbd = dev_id; struct input_dev *input = kbd->input; unsigned int key; - u8 sts, val; + u32 sts, val; - sts = readb(kbd->io_base + STATUS_REG); - if (sts & DATA_AVAIL) + sts = readl_relaxed(kbd->io_base + STATUS_REG); + if (!(sts & STATUS_DATA_AVAIL)) return IRQ_NONE; if (kbd->last_key != KEY_RESERVED) { @@ -78,7 +83,8 @@ static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) } /* following reads active (row, col) pair */ - val = readb(kbd->io_base + DATA_REG); + val = readl_relaxed(kbd->io_base + DATA_REG) & + (DATA_ROW_MASK | DATA_COLUMN_MASK); key = kbd->keycodes[val]; input_event(input, EV_MSC, MSC_SCAN, val); @@ -88,7 +94,7 @@ static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) kbd->last_key = key; /* clear interrupt */ - writeb(0, kbd->io_base + STATUS_REG); + writel_relaxed(0, kbd->io_base + STATUS_REG); return IRQ_HANDLED; } @@ -97,7 +103,7 @@ static int spear_kbd_open(struct input_dev *dev) { struct spear_kbd *kbd = input_get_drvdata(dev); int error; - u16 val; + u32 val; kbd->last_key = KEY_RESERVED; @@ -105,15 +111,20 @@ static int spear_kbd_open(struct input_dev *dev) if (error) return error; + /* keyboard rate to be programmed is input clock (in MHz) - 1 */ + val = clk_get_rate(kbd->clk) / 1000000 - 1; + val = (val & MODE_CTL_PCLK_FREQ_MSK) << MODE_CTL_PCLK_FREQ_SHIFT; + /* program keyboard */ - val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK; - writew(val, kbd->io_base + MODE_REG); - writeb(1, kbd->io_base + STATUS_REG); + val = MODE_CTL_SCAN_RATE_80 | MODE_CTL_KEYBOARD | val | + (kbd->mode << MODE_CTL_KEYNUM_SHIFT); + writel_relaxed(val, kbd->io_base + MODE_CTL_REG); + writel_relaxed(1, kbd->io_base + STATUS_REG); /* start key scan */ - val = readw(kbd->io_base + MODE_REG); - val |= START_SCAN; - writew(val, kbd->io_base + MODE_REG); + val = readl_relaxed(kbd->io_base + MODE_CTL_REG); + val |= MODE_CTL_START_SCAN; + writel_relaxed(val, kbd->io_base + MODE_CTL_REG); return 0; } @@ -121,85 +132,106 @@ static int spear_kbd_open(struct input_dev *dev) static void spear_kbd_close(struct input_dev *dev) { struct spear_kbd *kbd = input_get_drvdata(dev); - u16 val; + u32 val; /* stop key scan */ - val = readw(kbd->io_base + MODE_REG); - val &= ~START_SCAN; - writew(val, kbd->io_base + MODE_REG); + val = readl_relaxed(kbd->io_base + MODE_CTL_REG); + val &= ~MODE_CTL_START_SCAN; + writel_relaxed(val, kbd->io_base + MODE_CTL_REG); clk_disable(kbd->clk); kbd->last_key = KEY_RESERVED; } -static int __devinit spear_kbd_probe(struct platform_device *pdev) +#ifdef CONFIG_OF +static int spear_kbd_parse_dt(struct platform_device *pdev, + struct spear_kbd *kbd) { - const struct kbd_platform_data *pdata = pdev->dev.platform_data; - const struct matrix_keymap_data *keymap; - struct spear_kbd *kbd; - struct input_dev *input_dev; - struct resource *res; - int irq; + struct device_node *np = pdev->dev.of_node; int error; + u32 val, suspended_rate; - if (!pdata) { - dev_err(&pdev->dev, "Invalid platform data\n"); + if (!np) { + dev_err(&pdev->dev, "Missing DT data\n"); return -EINVAL; } - keymap = pdata->keymap; - if (!keymap) { - dev_err(&pdev->dev, "no keymap defined\n"); - return -EINVAL; - } + if (of_property_read_bool(np, "autorepeat")) + kbd->rep = true; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no keyboard resource defined\n"); - return -EBUSY; + if (of_property_read_u32(np, "suspended_rate", &suspended_rate)) + kbd->suspended_rate = suspended_rate; + + error = of_property_read_u32(np, "st,mode", &val); + if (error) { + dev_err(&pdev->dev, "DT: Invalid or missing mode\n"); + return error; } + kbd->mode = val; + return 0; +} +#else +static inline int spear_kbd_parse_dt(struct platform_device *pdev, + struct spear_kbd *kbd) +{ + return -ENOSYS; +} +#endif + +static int spear_kbd_probe(struct platform_device *pdev) +{ + struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev); + const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL; + struct spear_kbd *kbd; + struct input_dev *input_dev; + struct resource *res; + int irq; + int error; + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "not able to get irq for the device\n"); return irq; } - kbd = kzalloc(sizeof(*kbd), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!kbd || !input_dev) { - dev_err(&pdev->dev, "out of memory\n"); - error = -ENOMEM; - goto err_free_mem; + kbd = devm_kzalloc(&pdev->dev, sizeof(*kbd), GFP_KERNEL); + if (!kbd) { + dev_err(&pdev->dev, "not enough memory for driver data\n"); + return -ENOMEM; + } + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "unable to allocate input device\n"); + return -ENOMEM; } kbd->input = input_dev; kbd->irq = irq; - kbd->res = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!kbd->res) { - dev_err(&pdev->dev, "keyboard region already claimed\n"); - error = -EBUSY; - goto err_free_mem; - } - kbd->io_base = ioremap(res->start, resource_size(res)); - if (!kbd->io_base) { - dev_err(&pdev->dev, "ioremap failed for kbd_region\n"); - error = -ENOMEM; - goto err_release_mem_region; + if (!pdata) { + error = spear_kbd_parse_dt(pdev, kbd); + if (error) + return error; + } else { + kbd->mode = pdata->mode; + kbd->rep = pdata->rep; + kbd->suspended_rate = pdata->suspended_rate; } - kbd->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(kbd->clk)) { - error = PTR_ERR(kbd->clk); - goto err_iounmap; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + kbd->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(kbd->io_base)) + return PTR_ERR(kbd->io_base); + + kbd->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(kbd->clk)) + return PTR_ERR(kbd->clk); input_dev->name = "Spear Keyboard"; input_dev->phys = "keyboard/input0"; - input_dev->dev.parent = &pdev->dev; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; @@ -207,65 +239,51 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev) input_dev->open = spear_kbd_open; input_dev->close = spear_kbd_close; - __set_bit(EV_KEY, input_dev->evbit); - if (pdata->rep) + error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS, + kbd->keycodes, input_dev); + if (error) { + dev_err(&pdev->dev, "Failed to build keymap\n"); + return error; + } + + if (kbd->rep) __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); - input_dev->keycode = kbd->keycodes; - input_dev->keycodesize = sizeof(kbd->keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes); - - matrix_keypad_build_keymap(keymap, ROW_SHIFT, - input_dev->keycode, input_dev->keybit); - input_set_drvdata(input_dev, kbd); - error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd); + error = devm_request_irq(&pdev->dev, irq, spear_kbd_interrupt, 0, + "keyboard", kbd); if (error) { - dev_err(&pdev->dev, "request_irq fail\n"); - goto err_put_clk; + dev_err(&pdev->dev, "request_irq failed\n"); + return error; } + error = clk_prepare(kbd->clk); + if (error) + return error; + error = input_register_device(input_dev); if (error) { dev_err(&pdev->dev, "Unable to register keyboard device\n"); - goto err_free_irq; + clk_unprepare(kbd->clk); + return error; } device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, kbd); return 0; - -err_free_irq: - free_irq(kbd->irq, kbd); -err_put_clk: - clk_put(kbd->clk); -err_iounmap: - iounmap(kbd->io_base); -err_release_mem_region: - release_mem_region(res->start, resource_size(res)); -err_free_mem: - input_free_device(input_dev); - kfree(kbd); - - return error; } -static int __devexit spear_kbd_remove(struct platform_device *pdev) +static int spear_kbd_remove(struct platform_device *pdev) { struct spear_kbd *kbd = platform_get_drvdata(pdev); - free_irq(kbd->irq, kbd); input_unregister_device(kbd->input); - clk_put(kbd->clk); - iounmap(kbd->io_base); - release_mem_region(kbd->res->start, resource_size(kbd->res)); - kfree(kbd); + clk_unprepare(kbd->clk); - device_init_wakeup(&pdev->dev, 1); - platform_set_drvdata(pdev, NULL); + device_init_wakeup(&pdev->dev, 0); return 0; } @@ -276,14 +294,48 @@ static int spear_kbd_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct spear_kbd *kbd = platform_get_drvdata(pdev); struct input_dev *input_dev = kbd->input; + unsigned int rate = 0, mode_ctl_reg, val; mutex_lock(&input_dev->mutex); + /* explicitly enable clock as we may program device */ + clk_enable(kbd->clk); + + mode_ctl_reg = readl_relaxed(kbd->io_base + MODE_CTL_REG); + + if (device_may_wakeup(&pdev->dev)) { + if (!enable_irq_wake(kbd->irq)) + kbd->irq_wake_enabled = true; + + /* + * reprogram the keyboard operating frequency as on some + * platform it may change during system suspended + */ + if (kbd->suspended_rate) + rate = kbd->suspended_rate / 1000000 - 1; + else + rate = clk_get_rate(kbd->clk) / 1000000 - 1; + + val = mode_ctl_reg & + ~(MODE_CTL_PCLK_FREQ_MSK << MODE_CTL_PCLK_FREQ_SHIFT); + val |= (rate & MODE_CTL_PCLK_FREQ_MSK) + << MODE_CTL_PCLK_FREQ_SHIFT; + writel_relaxed(val, kbd->io_base + MODE_CTL_REG); + + } else { + if (input_dev->users) { + writel_relaxed(mode_ctl_reg & ~MODE_CTL_START_SCAN, + kbd->io_base + MODE_CTL_REG); + clk_disable(kbd->clk); + } + } + + /* store current configuration */ if (input_dev->users) - clk_enable(kbd->clk); + kbd->mode_ctl_reg = mode_ctl_reg; - if (device_may_wakeup(&pdev->dev)) - enable_irq_wake(kbd->irq); + /* restore previous clk state */ + clk_disable(kbd->clk); mutex_unlock(&input_dev->mutex); @@ -298,46 +350,47 @@ static int spear_kbd_resume(struct device *dev) mutex_lock(&input_dev->mutex); - if (device_may_wakeup(&pdev->dev)) - disable_irq_wake(kbd->irq); + if (device_may_wakeup(&pdev->dev)) { + if (kbd->irq_wake_enabled) { + kbd->irq_wake_enabled = false; + disable_irq_wake(kbd->irq); + } + } else { + if (input_dev->users) + clk_enable(kbd->clk); + } + /* restore current configuration */ if (input_dev->users) - clk_enable(kbd->clk); + writel_relaxed(kbd->mode_ctl_reg, kbd->io_base + MODE_CTL_REG); mutex_unlock(&input_dev->mutex); return 0; } +#endif + +static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume); -static const struct dev_pm_ops spear_kbd_pm_ops = { - .suspend = spear_kbd_suspend, - .resume = spear_kbd_resume, +#ifdef CONFIG_OF +static const struct of_device_id spear_kbd_id_table[] = { + { .compatible = "st,spear300-kbd" }, + {} }; +MODULE_DEVICE_TABLE(of, spear_kbd_id_table); #endif static struct platform_driver spear_kbd_driver = { .probe = spear_kbd_probe, - .remove = __devexit_p(spear_kbd_remove), + .remove = spear_kbd_remove, .driver = { .name = "keyboard", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &spear_kbd_pm_ops, -#endif + .of_match_table = of_match_ptr(spear_kbd_id_table), }, }; - -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"); |
