Commit 795e6bf33561ff03e253a6a756d5eb663b4a56bd

Authored by Magnus Damm
Committed by Paul Mundt
1 parent 253ba4e79e

sh: SuperH KEYSC platform driver

Add a platform driver for the SuperH KEYSC block.  The driver expects to get
mode, timing information and keypad layout from the board code as platform
data.  The board code is resonsible for pin configuration.

Both sh7343 and sh7722 should be supported, but only the sh7722 processor has
been tested so far.  SH_KEYSC_MODE_3 is yet to be tested.

Signed-off-by: Magnus Damm <damm@igel.co.jp>
Cc: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>

Showing 4 changed files with 304 additions and 0 deletions Side-by-side Diff

drivers/input/keyboard/Kconfig
... ... @@ -314,5 +314,14 @@
314 314 To compile this driver as a module, choose M here: the
315 315 module will be called bf54x-keys.
316 316  
  317 +config KEYBOARD_SH_KEYSC
  318 + tristate "SuperH KEYSC keypad support"
  319 + depends on SUPERH
  320 + help
  321 + Say Y here if you want to use a keypad attached to the KEYSC block
  322 + on SuperH processors such as sh7722 and sh7343.
  323 +
  324 + To compile this driver as a module, choose M here: the
  325 + module will be called sh_keysc.
317 326 endif
drivers/input/keyboard/Makefile
... ... @@ -26,4 +26,5 @@
26 26 obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
27 27 obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
28 28 obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
  29 +obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
drivers/input/keyboard/sh_keysc.c
  1 +/*
  2 + * SuperH KEYSC Keypad Driver
  3 + *
  4 + * Copyright (C) 2008 Magnus Damm
  5 + *
  6 + * Based on gpio_keys.c, Copyright 2005 Phil Blundell
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 + */
  12 +
  13 +#include <linux/kernel.h>
  14 +#include <linux/module.h>
  15 +#include <linux/init.h>
  16 +#include <linux/interrupt.h>
  17 +#include <linux/irq.h>
  18 +#include <linux/delay.h>
  19 +#include <linux/platform_device.h>
  20 +#include <linux/input.h>
  21 +#include <linux/io.h>
  22 +#include <asm/sh_keysc.h>
  23 +
  24 +#define KYCR1_OFFS 0x00
  25 +#define KYCR2_OFFS 0x04
  26 +#define KYINDR_OFFS 0x08
  27 +#define KYOUTDR_OFFS 0x0c
  28 +
  29 +#define KYCR2_IRQ_LEVEL 0x10
  30 +#define KYCR2_IRQ_DISABLED 0x00
  31 +
  32 +static const struct {
  33 + unsigned char kymd, keyout, keyin;
  34 +} sh_keysc_mode[] = {
  35 + [SH_KEYSC_MODE_1] = { 0, 6, 5 },
  36 + [SH_KEYSC_MODE_2] = { 1, 5, 6 },
  37 + [SH_KEYSC_MODE_3] = { 2, 4, 7 },
  38 +};
  39 +
  40 +struct sh_keysc_priv {
  41 + void __iomem *iomem_base;
  42 + unsigned long last_keys;
  43 + struct input_dev *input;
  44 + struct sh_keysc_info pdata;
  45 +};
  46 +
  47 +static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
  48 +{
  49 + struct platform_device *pdev = dev_id;
  50 + struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
  51 + struct sh_keysc_info *pdata = &priv->pdata;
  52 + unsigned long keys, keys1, keys0, mask;
  53 + unsigned char keyin_set, tmp;
  54 + int i, k;
  55 +
  56 + dev_dbg(&pdev->dev, "isr!\n");
  57 +
  58 + keys1 = ~0;
  59 + keys0 = 0;
  60 +
  61 + do {
  62 + keys = 0;
  63 + keyin_set = 0;
  64 +
  65 + iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS);
  66 +
  67 + for (i = 0; i < sh_keysc_mode[pdata->mode].keyout; i++) {
  68 + iowrite16(0xfff ^ (3 << (i * 2)),
  69 + priv->iomem_base + KYOUTDR_OFFS);
  70 + udelay(pdata->delay);
  71 + tmp = ioread16(priv->iomem_base + KYINDR_OFFS);
  72 + keys |= tmp << (sh_keysc_mode[pdata->mode].keyin * i);
  73 + tmp ^= (1 << sh_keysc_mode[pdata->mode].keyin) - 1;
  74 + keyin_set |= tmp;
  75 + }
  76 +
  77 + iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
  78 + iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8),
  79 + priv->iomem_base + KYCR2_OFFS);
  80 +
  81 + keys ^= ~0;
  82 + keys &= (1 << (sh_keysc_mode[pdata->mode].keyin *
  83 + sh_keysc_mode[pdata->mode].keyout)) - 1;
  84 + keys1 &= keys;
  85 + keys0 |= keys;
  86 +
  87 + dev_dbg(&pdev->dev, "keys 0x%08lx\n", keys);
  88 +
  89 + } while (ioread16(priv->iomem_base + KYCR2_OFFS) & 0x01);
  90 +
  91 + dev_dbg(&pdev->dev, "last_keys 0x%08lx keys0 0x%08lx keys1 0x%08lx\n",
  92 + priv->last_keys, keys0, keys1);
  93 +
  94 + for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
  95 + k = pdata->keycodes[i];
  96 + if (!k)
  97 + continue;
  98 +
  99 + mask = 1 << i;
  100 +
  101 + if (!((priv->last_keys ^ keys0) & mask))
  102 + continue;
  103 +
  104 + if ((keys1 | keys0) & mask) {
  105 + input_event(priv->input, EV_KEY, k, 1);
  106 + priv->last_keys |= mask;
  107 + }
  108 +
  109 + if (!(keys1 & mask)) {
  110 + input_event(priv->input, EV_KEY, k, 0);
  111 + priv->last_keys &= ~mask;
  112 + }
  113 +
  114 + }
  115 + input_sync(priv->input);
  116 +
  117 + return IRQ_HANDLED;
  118 +}
  119 +
  120 +#define res_size(res) ((res)->end - (res)->start + 1)
  121 +
  122 +static int __devinit sh_keysc_probe(struct platform_device *pdev)
  123 +{
  124 + struct sh_keysc_priv *priv;
  125 + struct sh_keysc_info *pdata;
  126 + struct resource *res;
  127 + struct input_dev *input;
  128 + int i, k;
  129 + int irq, error;
  130 +
  131 + if (!pdev->dev.platform_data) {
  132 + dev_err(&pdev->dev, "no platform data defined\n");
  133 + error = -EINVAL;
  134 + goto err0;
  135 + }
  136 +
  137 + error = -ENXIO;
  138 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  139 + if (res == NULL) {
  140 + dev_err(&pdev->dev, "failed to get I/O memory\n");
  141 + goto err0;
  142 + }
  143 +
  144 + irq = platform_get_irq(pdev, 0);
  145 + if (irq < 0) {
  146 + dev_err(&pdev->dev, "failed to get irq\n");
  147 + goto err0;
  148 + }
  149 +
  150 + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  151 + if (priv == NULL) {
  152 + dev_err(&pdev->dev, "failed to allocate driver data\n");
  153 + error = -ENOMEM;
  154 + goto err0;
  155 + }
  156 +
  157 + platform_set_drvdata(pdev, priv);
  158 + memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata));
  159 + pdata = &priv->pdata;
  160 +
  161 + res = request_mem_region(res->start, res_size(res), pdev->name);
  162 + if (res == NULL) {
  163 + dev_err(&pdev->dev, "failed to request I/O memory\n");
  164 + error = -EBUSY;
  165 + goto err1;
  166 + }
  167 +
  168 + priv->iomem_base = ioremap_nocache(res->start, res_size(res));
  169 + if (priv->iomem_base == NULL) {
  170 + dev_err(&pdev->dev, "failed to remap I/O memory\n");
  171 + error = -ENXIO;
  172 + goto err2;
  173 + }
  174 +
  175 + priv->input = input_allocate_device();
  176 + if (!priv->input) {
  177 + dev_err(&pdev->dev, "failed to allocate input device\n");
  178 + error = -ENOMEM;
  179 + goto err3;
  180 + }
  181 +
  182 + input = priv->input;
  183 + input->evbit[0] = BIT_MASK(EV_KEY);
  184 +
  185 + input->name = pdev->name;
  186 + input->phys = "sh-keysc-keys/input0";
  187 + input->dev.parent = &pdev->dev;
  188 +
  189 + input->id.bustype = BUS_HOST;
  190 + input->id.vendor = 0x0001;
  191 + input->id.product = 0x0001;
  192 + input->id.version = 0x0100;
  193 +
  194 + error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
  195 + if (error) {
  196 + dev_err(&pdev->dev, "failed to request IRQ\n");
  197 + goto err4;
  198 + }
  199 +
  200 + for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
  201 + k = pdata->keycodes[i];
  202 + if (k)
  203 + input_set_capability(input, EV_KEY, k);
  204 + }
  205 +
  206 + error = input_register_device(input);
  207 + if (error) {
  208 + dev_err(&pdev->dev, "failed to register input device\n");
  209 + goto err5;
  210 + }
  211 +
  212 + iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) |
  213 + pdata->scan_timing, priv->iomem_base + KYCR1_OFFS);
  214 + iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
  215 + iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS);
  216 + return 0;
  217 + err5:
  218 + free_irq(irq, pdev);
  219 + err4:
  220 + input_free_device(input);
  221 + err3:
  222 + iounmap(priv->iomem_base);
  223 + err2:
  224 + release_mem_region(res->start, res_size(res));
  225 + err1:
  226 + platform_set_drvdata(pdev, NULL);
  227 + kfree(priv);
  228 + err0:
  229 + return error;
  230 +}
  231 +
  232 +static int __devexit sh_keysc_remove(struct platform_device *pdev)
  233 +{
  234 + struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
  235 + struct resource *res;
  236 +
  237 + iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS);
  238 +
  239 + input_unregister_device(priv->input);
  240 + free_irq(platform_get_irq(pdev, 0), pdev);
  241 + input_free_device(priv->input);
  242 + iounmap(priv->iomem_base);
  243 +
  244 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  245 + release_mem_region(res->start, res_size(res));
  246 +
  247 + platform_set_drvdata(pdev, NULL);
  248 + kfree(priv);
  249 + return 0;
  250 +}
  251 +
  252 +
  253 +#define sh_keysc_suspend NULL
  254 +#define sh_keysc_resume NULL
  255 +
  256 +struct platform_driver sh_keysc_device_driver = {
  257 + .probe = sh_keysc_probe,
  258 + .remove = __devexit_p(sh_keysc_remove),
  259 + .suspend = sh_keysc_suspend,
  260 + .resume = sh_keysc_resume,
  261 + .driver = {
  262 + .name = "sh_keysc",
  263 + }
  264 +};
  265 +
  266 +static int __init sh_keysc_init(void)
  267 +{
  268 + return platform_driver_register(&sh_keysc_device_driver);
  269 +}
  270 +
  271 +static void __exit sh_keysc_exit(void)
  272 +{
  273 + platform_driver_unregister(&sh_keysc_device_driver);
  274 +}
  275 +
  276 +module_init(sh_keysc_init);
  277 +module_exit(sh_keysc_exit);
  278 +
  279 +MODULE_AUTHOR("Magnus Damm");
  280 +MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
  281 +MODULE_LICENSE("GPL");
include/asm-sh/sh_keysc.h
  1 +#ifndef __ASM_KEYSC_H__
  2 +#define __ASM_KEYSC_H__
  3 +
  4 +#define SH_KEYSC_MAXKEYS 30
  5 +
  6 +struct sh_keysc_info {
  7 + enum { SH_KEYSC_MODE_1, SH_KEYSC_MODE_2, SH_KEYSC_MODE_3 } mode;
  8 + int scan_timing; /* 0 -> 7, see KYCR1, SCN[2:0] */
  9 + int delay;
  10 + int keycodes[SH_KEYSC_MAXKEYS];
  11 +};
  12 +
  13 +#endif /* __ASM_KEYSC_H__ */