Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/keyboard/bf54x-keys.c
15111 views
1
/*
2
* File: drivers/input/keyboard/bf54x-keys.c
3
* Based on:
4
* Author: Michael Hennerich <[email protected]>
5
*
6
* Created:
7
* Description: keypad driver for Analog Devices Blackfin BF54x Processors
8
*
9
*
10
* Modified:
11
* Copyright 2007-2008 Analog Devices Inc.
12
*
13
* Bugs: Enter bugs at http://blackfin.uclinux.org/
14
*
15
* This program is free software; you can redistribute it and/or modify
16
* it under the terms of the GNU General Public License as published by
17
* the Free Software Foundation; either version 2 of the License, or
18
* (at your option) any later version.
19
*
20
* This program is distributed in the hope that it will be useful,
21
* but WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
* GNU General Public License for more details.
24
*
25
* You should have received a copy of the GNU General Public License
26
* along with this program; if not, see the file COPYING, or write
27
* to the Free Software Foundation, Inc.,
28
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29
*/
30
31
#include <linux/module.h>
32
33
#include <linux/init.h>
34
#include <linux/fs.h>
35
#include <linux/interrupt.h>
36
#include <linux/irq.h>
37
#include <linux/slab.h>
38
#include <linux/sched.h>
39
#include <linux/pm.h>
40
#include <linux/sysctl.h>
41
#include <linux/proc_fs.h>
42
#include <linux/delay.h>
43
#include <linux/platform_device.h>
44
#include <linux/input.h>
45
46
#include <asm/portmux.h>
47
#include <mach/bf54x_keys.h>
48
49
#define DRV_NAME "bf54x-keys"
50
#define TIME_SCALE 100 /* 100 ns */
51
#define MAX_MULT (0xFF * TIME_SCALE)
52
#define MAX_RC 8 /* Max Row/Col */
53
54
static const u16 per_rows[] = {
55
P_KEY_ROW7,
56
P_KEY_ROW6,
57
P_KEY_ROW5,
58
P_KEY_ROW4,
59
P_KEY_ROW3,
60
P_KEY_ROW2,
61
P_KEY_ROW1,
62
P_KEY_ROW0,
63
0
64
};
65
66
static const u16 per_cols[] = {
67
P_KEY_COL7,
68
P_KEY_COL6,
69
P_KEY_COL5,
70
P_KEY_COL4,
71
P_KEY_COL3,
72
P_KEY_COL2,
73
P_KEY_COL1,
74
P_KEY_COL0,
75
0
76
};
77
78
struct bf54x_kpad {
79
struct input_dev *input;
80
int irq;
81
unsigned short lastkey;
82
unsigned short *keycode;
83
struct timer_list timer;
84
unsigned int keyup_test_jiffies;
85
unsigned short kpad_msel;
86
unsigned short kpad_prescale;
87
unsigned short kpad_ctl;
88
};
89
90
static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
91
struct input_dev *input, u16 keyident)
92
{
93
u16 i;
94
95
for (i = 0; i < input->keycodemax; i++)
96
if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
97
return bf54x_kpad->keycode[i];
98
return -1;
99
}
100
101
static inline void bfin_keycodecpy(unsigned short *keycode,
102
const unsigned int *pdata_kc,
103
unsigned short keymapsize)
104
{
105
unsigned int i;
106
107
for (i = 0; i < keymapsize; i++) {
108
keycode[i] = pdata_kc[i] & 0xffff;
109
keycode[i + keymapsize] = pdata_kc[i] >> 16;
110
}
111
}
112
113
static inline u16 bfin_kpad_get_prescale(u32 timescale)
114
{
115
u32 sclk = get_sclk();
116
117
return ((((sclk / 1000) * timescale) / 1024) - 1);
118
}
119
120
static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
121
{
122
return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
123
}
124
125
static inline void bfin_kpad_clear_irq(void)
126
{
127
bfin_write_KPAD_STAT(0xFFFF);
128
bfin_write_KPAD_ROWCOL(0xFFFF);
129
}
130
131
static void bfin_kpad_timer(unsigned long data)
132
{
133
struct platform_device *pdev = (struct platform_device *) data;
134
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
135
136
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
137
/* Try again later */
138
mod_timer(&bf54x_kpad->timer,
139
jiffies + bf54x_kpad->keyup_test_jiffies);
140
return;
141
}
142
143
input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
144
input_sync(bf54x_kpad->input);
145
146
/* Clear IRQ Status */
147
148
bfin_kpad_clear_irq();
149
enable_irq(bf54x_kpad->irq);
150
}
151
152
static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
153
{
154
struct platform_device *pdev = dev_id;
155
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
156
struct input_dev *input = bf54x_kpad->input;
157
int key;
158
u16 rowcol = bfin_read_KPAD_ROWCOL();
159
160
key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
161
162
input_report_key(input, key, 1);
163
input_sync(input);
164
165
if (bfin_kpad_get_keypressed(bf54x_kpad)) {
166
disable_irq_nosync(bf54x_kpad->irq);
167
bf54x_kpad->lastkey = key;
168
mod_timer(&bf54x_kpad->timer,
169
jiffies + bf54x_kpad->keyup_test_jiffies);
170
} else {
171
input_report_key(input, key, 0);
172
input_sync(input);
173
174
bfin_kpad_clear_irq();
175
}
176
177
return IRQ_HANDLED;
178
}
179
180
static int __devinit bfin_kpad_probe(struct platform_device *pdev)
181
{
182
struct bf54x_kpad *bf54x_kpad;
183
struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
184
struct input_dev *input;
185
int i, error;
186
187
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
188
dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n");
189
return -EINVAL;
190
}
191
192
if (!pdata->keymapsize ||
193
pdata->keymapsize > (pdata->rows * pdata->cols)) {
194
dev_err(&pdev->dev, "invalid keymapsize\n");
195
return -EINVAL;
196
}
197
198
bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
199
if (!bf54x_kpad)
200
return -ENOMEM;
201
202
platform_set_drvdata(pdev, bf54x_kpad);
203
204
/* Allocate memory for keymap followed by private LUT */
205
bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
206
sizeof(unsigned short) * 2, GFP_KERNEL);
207
if (!bf54x_kpad->keycode) {
208
error = -ENOMEM;
209
goto out;
210
}
211
212
if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
213
!pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
214
dev_warn(&pdev->dev,
215
"invalid platform debounce/columndrive time\n");
216
bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
217
} else {
218
bfin_write_KPAD_MSEL(
219
((pdata->debounce_time / TIME_SCALE)
220
& DBON_SCALE) |
221
(((pdata->coldrive_time / TIME_SCALE) << 8)
222
& COLDRV_SCALE));
223
224
}
225
226
if (!pdata->keyup_test_interval)
227
bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
228
else
229
bf54x_kpad->keyup_test_jiffies =
230
msecs_to_jiffies(pdata->keyup_test_interval);
231
232
if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
233
DRV_NAME)) {
234
dev_err(&pdev->dev, "requesting peripherals failed\n");
235
error = -EFAULT;
236
goto out0;
237
}
238
239
if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
240
DRV_NAME)) {
241
dev_err(&pdev->dev, "requesting peripherals failed\n");
242
error = -EFAULT;
243
goto out1;
244
}
245
246
bf54x_kpad->irq = platform_get_irq(pdev, 0);
247
if (bf54x_kpad->irq < 0) {
248
error = -ENODEV;
249
goto out2;
250
}
251
252
error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
253
0, DRV_NAME, pdev);
254
if (error) {
255
dev_err(&pdev->dev, "unable to claim irq %d\n",
256
bf54x_kpad->irq);
257
goto out2;
258
}
259
260
input = input_allocate_device();
261
if (!input) {
262
error = -ENOMEM;
263
goto out3;
264
}
265
266
bf54x_kpad->input = input;
267
268
input->name = pdev->name;
269
input->phys = "bf54x-keys/input0";
270
input->dev.parent = &pdev->dev;
271
272
input_set_drvdata(input, bf54x_kpad);
273
274
input->id.bustype = BUS_HOST;
275
input->id.vendor = 0x0001;
276
input->id.product = 0x0001;
277
input->id.version = 0x0100;
278
279
input->keycodesize = sizeof(unsigned short);
280
input->keycodemax = pdata->keymapsize;
281
input->keycode = bf54x_kpad->keycode;
282
283
bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
284
285
/* setup input device */
286
__set_bit(EV_KEY, input->evbit);
287
288
if (pdata->repeat)
289
__set_bit(EV_REP, input->evbit);
290
291
for (i = 0; i < input->keycodemax; i++)
292
__set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit);
293
__clear_bit(KEY_RESERVED, input->keybit);
294
295
error = input_register_device(input);
296
if (error) {
297
dev_err(&pdev->dev, "unable to register input device\n");
298
goto out4;
299
}
300
301
/* Init Keypad Key Up/Release test timer */
302
303
setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev);
304
305
bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
306
307
bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
308
(((pdata->rows - 1) << 10) & KPAD_ROWEN) |
309
(2 & KPAD_IRQMODE));
310
311
bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
312
313
device_init_wakeup(&pdev->dev, 1);
314
315
return 0;
316
317
out4:
318
input_free_device(input);
319
out3:
320
free_irq(bf54x_kpad->irq, pdev);
321
out2:
322
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
323
out1:
324
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
325
out0:
326
kfree(bf54x_kpad->keycode);
327
out:
328
kfree(bf54x_kpad);
329
platform_set_drvdata(pdev, NULL);
330
331
return error;
332
}
333
334
static int __devexit bfin_kpad_remove(struct platform_device *pdev)
335
{
336
struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
337
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
338
339
del_timer_sync(&bf54x_kpad->timer);
340
free_irq(bf54x_kpad->irq, pdev);
341
342
input_unregister_device(bf54x_kpad->input);
343
344
peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
345
peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
346
347
kfree(bf54x_kpad->keycode);
348
kfree(bf54x_kpad);
349
platform_set_drvdata(pdev, NULL);
350
351
return 0;
352
}
353
354
#ifdef CONFIG_PM
355
static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
356
{
357
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
358
359
bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();
360
bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();
361
bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();
362
363
if (device_may_wakeup(&pdev->dev))
364
enable_irq_wake(bf54x_kpad->irq);
365
366
return 0;
367
}
368
369
static int bfin_kpad_resume(struct platform_device *pdev)
370
{
371
struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
372
373
bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);
374
bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);
375
bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);
376
377
if (device_may_wakeup(&pdev->dev))
378
disable_irq_wake(bf54x_kpad->irq);
379
380
return 0;
381
}
382
#else
383
# define bfin_kpad_suspend NULL
384
# define bfin_kpad_resume NULL
385
#endif
386
387
struct platform_driver bfin_kpad_device_driver = {
388
.driver = {
389
.name = DRV_NAME,
390
.owner = THIS_MODULE,
391
},
392
.probe = bfin_kpad_probe,
393
.remove = __devexit_p(bfin_kpad_remove),
394
.suspend = bfin_kpad_suspend,
395
.resume = bfin_kpad_resume,
396
};
397
398
static int __init bfin_kpad_init(void)
399
{
400
return platform_driver_register(&bfin_kpad_device_driver);
401
}
402
403
static void __exit bfin_kpad_exit(void)
404
{
405
platform_driver_unregister(&bfin_kpad_device_driver);
406
}
407
408
module_init(bfin_kpad_init);
409
module_exit(bfin_kpad_exit);
410
411
MODULE_LICENSE("GPL");
412
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
413
MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
414
MODULE_ALIAS("platform:bf54x-keys");
415
416