blob: 6218b2f024957bcffcac9d953c3e279852cdb34f [file] [log] [blame]
Magnus Damm795e6bf2008-03-04 15:23:45 -08001/*
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>
Magnus Dammfc1d0032009-11-27 07:32:24 +000021#include <linux/input/sh_keysc.h>
Magnus Damm090d9512008-10-31 20:21:23 +090022#include <linux/clk.h>
Magnus Damm795e6bf2008-03-04 15:23:45 -080023#include <linux/io.h>
Magnus Damm795e6bf2008-03-04 15:23:45 -080024
Magnus Damm795e6bf2008-03-04 15:23:45 -080025static const struct {
26 unsigned char kymd, keyout, keyin;
27} sh_keysc_mode[] = {
28 [SH_KEYSC_MODE_1] = { 0, 6, 5 },
29 [SH_KEYSC_MODE_2] = { 1, 5, 6 },
30 [SH_KEYSC_MODE_3] = { 2, 4, 7 },
Magnus Damm3bf12762010-01-21 00:02:36 -080031 [SH_KEYSC_MODE_4] = { 3, 6, 6 },
32 [SH_KEYSC_MODE_5] = { 4, 6, 7 },
Magnus Damm795e6bf2008-03-04 15:23:45 -080033};
34
35struct sh_keysc_priv {
36 void __iomem *iomem_base;
Magnus Damm090d9512008-10-31 20:21:23 +090037 struct clk *clk;
Magnus Damm795e6bf2008-03-04 15:23:45 -080038 unsigned long last_keys;
39 struct input_dev *input;
40 struct sh_keysc_info pdata;
41};
42
Magnus Damm2b14a802010-02-10 22:13:21 -080043#define KYCR1 0
44#define KYCR2 1
45#define KYINDR 2
46#define KYOUTDR 3
47
48#define KYCR2_IRQ_LEVEL 0x10
49#define KYCR2_IRQ_DISABLED 0x00
50
51static unsigned long sh_keysc_read(struct sh_keysc_priv *p, int reg_nr)
52{
53 return ioread16(p->iomem_base + (reg_nr << 2));
54}
55
56static void sh_keysc_write(struct sh_keysc_priv *p, int reg_nr,
57 unsigned long value)
58{
59 iowrite16(value, p->iomem_base + (reg_nr << 2));
60}
61
62static void sh_keysc_level_mode(struct sh_keysc_priv *p,
63 unsigned long keys_set)
64{
65 struct sh_keysc_info *pdata = &p->pdata;
66
67 sh_keysc_write(p, KYOUTDR, 0);
68 sh_keysc_write(p, KYCR2, KYCR2_IRQ_LEVEL | (keys_set << 8));
69
70 if (pdata->kycr2_delay)
71 udelay(pdata->kycr2_delay);
72}
73
Magnus Damm795e6bf2008-03-04 15:23:45 -080074static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
75{
76 struct platform_device *pdev = dev_id;
77 struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
78 struct sh_keysc_info *pdata = &priv->pdata;
79 unsigned long keys, keys1, keys0, mask;
80 unsigned char keyin_set, tmp;
81 int i, k;
82
83 dev_dbg(&pdev->dev, "isr!\n");
84
85 keys1 = ~0;
86 keys0 = 0;
87
88 do {
89 keys = 0;
90 keyin_set = 0;
91
Magnus Damm2b14a802010-02-10 22:13:21 -080092 sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
Magnus Damm795e6bf2008-03-04 15:23:45 -080093
94 for (i = 0; i < sh_keysc_mode[pdata->mode].keyout; i++) {
Magnus Damm2b14a802010-02-10 22:13:21 -080095 sh_keysc_write(priv, KYOUTDR, 0xfff ^ (3 << (i * 2)));
Magnus Damm795e6bf2008-03-04 15:23:45 -080096 udelay(pdata->delay);
Magnus Damm2b14a802010-02-10 22:13:21 -080097 tmp = sh_keysc_read(priv, KYINDR);
98
Magnus Damm795e6bf2008-03-04 15:23:45 -080099 keys |= tmp << (sh_keysc_mode[pdata->mode].keyin * i);
100 tmp ^= (1 << sh_keysc_mode[pdata->mode].keyin) - 1;
101 keyin_set |= tmp;
102 }
103
Magnus Damm2b14a802010-02-10 22:13:21 -0800104 sh_keysc_level_mode(priv, keyin_set);
Kuninori Morimoto1f85d382009-09-15 00:21:34 +0000105
Magnus Damm795e6bf2008-03-04 15:23:45 -0800106 keys ^= ~0;
107 keys &= (1 << (sh_keysc_mode[pdata->mode].keyin *
108 sh_keysc_mode[pdata->mode].keyout)) - 1;
109 keys1 &= keys;
110 keys0 |= keys;
111
112 dev_dbg(&pdev->dev, "keys 0x%08lx\n", keys);
113
Magnus Damm2b14a802010-02-10 22:13:21 -0800114 } while (sh_keysc_read(priv, KYCR2) & 0x01);
Magnus Damm795e6bf2008-03-04 15:23:45 -0800115
116 dev_dbg(&pdev->dev, "last_keys 0x%08lx keys0 0x%08lx keys1 0x%08lx\n",
117 priv->last_keys, keys0, keys1);
118
119 for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
120 k = pdata->keycodes[i];
121 if (!k)
122 continue;
123
124 mask = 1 << i;
125
126 if (!((priv->last_keys ^ keys0) & mask))
127 continue;
128
129 if ((keys1 | keys0) & mask) {
130 input_event(priv->input, EV_KEY, k, 1);
131 priv->last_keys |= mask;
132 }
133
134 if (!(keys1 & mask)) {
135 input_event(priv->input, EV_KEY, k, 0);
136 priv->last_keys &= ~mask;
137 }
138
139 }
140 input_sync(priv->input);
141
142 return IRQ_HANDLED;
143}
144
Magnus Damm795e6bf2008-03-04 15:23:45 -0800145static int __devinit sh_keysc_probe(struct platform_device *pdev)
146{
147 struct sh_keysc_priv *priv;
148 struct sh_keysc_info *pdata;
149 struct resource *res;
150 struct input_dev *input;
Magnus Damm090d9512008-10-31 20:21:23 +0900151 char clk_name[8];
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700152 int i;
Magnus Damm795e6bf2008-03-04 15:23:45 -0800153 int irq, error;
154
155 if (!pdev->dev.platform_data) {
156 dev_err(&pdev->dev, "no platform data defined\n");
157 error = -EINVAL;
158 goto err0;
159 }
160
161 error = -ENXIO;
162 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
163 if (res == NULL) {
164 dev_err(&pdev->dev, "failed to get I/O memory\n");
165 goto err0;
166 }
167
168 irq = platform_get_irq(pdev, 0);
169 if (irq < 0) {
170 dev_err(&pdev->dev, "failed to get irq\n");
171 goto err0;
172 }
173
174 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
175 if (priv == NULL) {
176 dev_err(&pdev->dev, "failed to allocate driver data\n");
177 error = -ENOMEM;
178 goto err0;
179 }
180
181 platform_set_drvdata(pdev, priv);
182 memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata));
183 pdata = &priv->pdata;
184
Magnus Damm3bf12762010-01-21 00:02:36 -0800185 priv->iomem_base = ioremap_nocache(res->start, resource_size(res));
Magnus Damm795e6bf2008-03-04 15:23:45 -0800186 if (priv->iomem_base == NULL) {
187 dev_err(&pdev->dev, "failed to remap I/O memory\n");
188 error = -ENXIO;
Tetsuya Mukawad3aa43a2008-07-19 07:46:53 +0900189 goto err1;
Magnus Damm795e6bf2008-03-04 15:23:45 -0800190 }
191
Magnus Damm090d9512008-10-31 20:21:23 +0900192 snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id);
193 priv->clk = clk_get(&pdev->dev, clk_name);
194 if (IS_ERR(priv->clk)) {
195 dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
196 error = PTR_ERR(priv->clk);
197 goto err2;
198 }
199
Magnus Damm795e6bf2008-03-04 15:23:45 -0800200 priv->input = input_allocate_device();
201 if (!priv->input) {
202 dev_err(&pdev->dev, "failed to allocate input device\n");
203 error = -ENOMEM;
Magnus Damm090d9512008-10-31 20:21:23 +0900204 goto err3;
Magnus Damm795e6bf2008-03-04 15:23:45 -0800205 }
206
207 input = priv->input;
208 input->evbit[0] = BIT_MASK(EV_KEY);
209
210 input->name = pdev->name;
211 input->phys = "sh-keysc-keys/input0";
212 input->dev.parent = &pdev->dev;
213
214 input->id.bustype = BUS_HOST;
215 input->id.vendor = 0x0001;
216 input->id.product = 0x0001;
217 input->id.version = 0x0100;
218
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700219 input->keycode = pdata->keycodes;
220 input->keycodesize = sizeof(pdata->keycodes[0]);
221 input->keycodemax = ARRAY_SIZE(pdata->keycodes);
222
Magnus Damm795e6bf2008-03-04 15:23:45 -0800223 error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
224 if (error) {
225 dev_err(&pdev->dev, "failed to request IRQ\n");
Magnus Damm090d9512008-10-31 20:21:23 +0900226 goto err4;
Magnus Damm795e6bf2008-03-04 15:23:45 -0800227 }
228
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700229 for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
230 __set_bit(pdata->keycodes[i], input->keybit);
231 __clear_bit(KEY_RESERVED, input->keybit);
Magnus Damm795e6bf2008-03-04 15:23:45 -0800232
233 error = input_register_device(input);
234 if (error) {
235 dev_err(&pdev->dev, "failed to register input device\n");
Magnus Damm090d9512008-10-31 20:21:23 +0900236 goto err5;
Magnus Damm795e6bf2008-03-04 15:23:45 -0800237 }
238
Magnus Damm090d9512008-10-31 20:21:23 +0900239 clk_enable(priv->clk);
240
Magnus Damm2b14a802010-02-10 22:13:21 -0800241 sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) |
242 pdata->scan_timing);
243 sh_keysc_level_mode(priv, 0);
Magnus Damma29b99e2009-03-10 06:24:21 +0000244
245 device_init_wakeup(&pdev->dev, 1);
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700246
Magnus Damm795e6bf2008-03-04 15:23:45 -0800247 return 0;
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700248
Magnus Damm090d9512008-10-31 20:21:23 +0900249 err5:
Tetsuya Mukawad3aa43a2008-07-19 07:46:53 +0900250 free_irq(irq, pdev);
Magnus Damm090d9512008-10-31 20:21:23 +0900251 err4:
Tetsuya Mukawad3aa43a2008-07-19 07:46:53 +0900252 input_free_device(input);
Magnus Damm090d9512008-10-31 20:21:23 +0900253 err3:
254 clk_put(priv->clk);
Magnus Damm795e6bf2008-03-04 15:23:45 -0800255 err2:
Tetsuya Mukawad3aa43a2008-07-19 07:46:53 +0900256 iounmap(priv->iomem_base);
Magnus Damm795e6bf2008-03-04 15:23:45 -0800257 err1:
258 platform_set_drvdata(pdev, NULL);
259 kfree(priv);
260 err0:
261 return error;
262}
263
264static int __devexit sh_keysc_remove(struct platform_device *pdev)
265{
266 struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
Magnus Damm795e6bf2008-03-04 15:23:45 -0800267
Magnus Damm2b14a802010-02-10 22:13:21 -0800268 sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
Magnus Damm795e6bf2008-03-04 15:23:45 -0800269
270 input_unregister_device(priv->input);
271 free_irq(platform_get_irq(pdev, 0), pdev);
Magnus Damm795e6bf2008-03-04 15:23:45 -0800272 iounmap(priv->iomem_base);
273
Magnus Damm090d9512008-10-31 20:21:23 +0900274 clk_disable(priv->clk);
275 clk_put(priv->clk);
276
Magnus Damm795e6bf2008-03-04 15:23:45 -0800277 platform_set_drvdata(pdev, NULL);
278 kfree(priv);
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700279
Magnus Damm795e6bf2008-03-04 15:23:45 -0800280 return 0;
281}
282
Magnus Damma29b99e2009-03-10 06:24:21 +0000283static int sh_keysc_suspend(struct device *dev)
284{
Magnus Damm49976922009-03-11 08:04:23 +0000285 struct platform_device *pdev = to_platform_device(dev);
286 struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
Magnus Damm4ba50df2009-04-01 14:39:20 +0000287 int irq = platform_get_irq(pdev, 0);
Magnus Damma29b99e2009-03-10 06:24:21 +0000288 unsigned short value;
Magnus Damm795e6bf2008-03-04 15:23:45 -0800289
Magnus Damm2b14a802010-02-10 22:13:21 -0800290 value = sh_keysc_read(priv, KYCR1);
Magnus Damma29b99e2009-03-10 06:24:21 +0000291
Magnus Damm4ba50df2009-04-01 14:39:20 +0000292 if (device_may_wakeup(dev)) {
Magnus Damma29b99e2009-03-10 06:24:21 +0000293 value |= 0x80;
Magnus Damm4ba50df2009-04-01 14:39:20 +0000294 enable_irq_wake(irq);
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700295 } else {
Magnus Damma29b99e2009-03-10 06:24:21 +0000296 value &= ~0x80;
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700297 }
Magnus Damma29b99e2009-03-10 06:24:21 +0000298
Magnus Damm2b14a802010-02-10 22:13:21 -0800299 sh_keysc_write(priv, KYCR1, value);
Dmitry Torokhov24d01c02009-07-21 01:12:12 -0700300
Magnus Damma29b99e2009-03-10 06:24:21 +0000301 return 0;
302}
303
Magnus Damm4ba50df2009-04-01 14:39:20 +0000304static int sh_keysc_resume(struct device *dev)
305{
306 struct platform_device *pdev = to_platform_device(dev);
307 int irq = platform_get_irq(pdev, 0);
308
309 if (device_may_wakeup(dev))
310 disable_irq_wake(irq);
311
312 return 0;
313}
314
Alexey Dobriyan47145212009-12-14 18:00:08 -0800315static const struct dev_pm_ops sh_keysc_dev_pm_ops = {
Magnus Damma29b99e2009-03-10 06:24:21 +0000316 .suspend = sh_keysc_suspend,
Magnus Damm4ba50df2009-04-01 14:39:20 +0000317 .resume = sh_keysc_resume,
Magnus Damma29b99e2009-03-10 06:24:21 +0000318};
Magnus Damm795e6bf2008-03-04 15:23:45 -0800319
320struct platform_driver sh_keysc_device_driver = {
321 .probe = sh_keysc_probe,
322 .remove = __devexit_p(sh_keysc_remove),
Magnus Damm795e6bf2008-03-04 15:23:45 -0800323 .driver = {
324 .name = "sh_keysc",
Magnus Damma29b99e2009-03-10 06:24:21 +0000325 .pm = &sh_keysc_dev_pm_ops,
Magnus Damm795e6bf2008-03-04 15:23:45 -0800326 }
327};
328
329static int __init sh_keysc_init(void)
330{
331 return platform_driver_register(&sh_keysc_device_driver);
332}
333
334static void __exit sh_keysc_exit(void)
335{
336 platform_driver_unregister(&sh_keysc_device_driver);
337}
338
339module_init(sh_keysc_init);
340module_exit(sh_keysc_exit);
341
342MODULE_AUTHOR("Magnus Damm");
343MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
344MODULE_LICENSE("GPL");