aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMagnus Damm <magnus.damm@gmail.com>2008-03-04 18:23:45 -0500
committerPaul Mundt <lethal@linux-sh.org>2008-04-18 12:50:00 -0400
commit795e6bf33561ff03e253a6a756d5eb663b4a56bd (patch)
tree59e4c5ad82e0df7d20c04cd40028de197d0adb58 /drivers
parent253ba4e79edc695b2925bd2ef34de06ff4d4070c (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/keyboard/Kconfig9
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/sh_keysc.c281
3 files changed, 291 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 8ea709be3306..efd70a974591 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -314,4 +314,13 @@ config KEYBOARD_BFIN
314 To compile this driver as a module, choose M here: the 314 To compile this driver as a module, choose M here: the
315 module will be called bf54x-keys. 315 module will be called bf54x-keys.
316 316
317config 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.
317endif 326endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index e741f4031012..0edc8f285d1c 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
26obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o 26obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
27obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o 27obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
28obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o 28obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
29obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
new file mode 100644
index 000000000000..5d0864a9e94b
--- /dev/null
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -0,0 +1,281 @@
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
32static 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
40struct 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
47static 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
122static 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
232static 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
256struct 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
266static int __init sh_keysc_init(void)
267{
268 return platform_driver_register(&sh_keysc_device_driver);
269}
270
271static void __exit sh_keysc_exit(void)
272{
273 platform_driver_unregister(&sh_keysc_device_driver);
274}
275
276module_init(sh_keysc_init);
277module_exit(sh_keysc_exit);
278
279MODULE_AUTHOR("Magnus Damm");
280MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
281MODULE_LICENSE("GPL");