Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/keyboard/samsung-keypad.c
15111 views
1
/*
2
* Samsung keypad driver
3
*
4
* Copyright (C) 2010 Samsung Electronics Co.Ltd
5
* Author: Joonyoung Shim <[email protected]>
6
* Author: Donghwa Lee <[email protected]>
7
*
8
* This program is free software; you can redistribute it and/or modify it
9
* under the terms of the GNU General Public License as published by the
10
* Free Software Foundation; either version 2 of the License, or (at your
11
* option) any later version.
12
*/
13
14
#include <linux/clk.h>
15
#include <linux/delay.h>
16
#include <linux/err.h>
17
#include <linux/init.h>
18
#include <linux/input.h>
19
#include <linux/interrupt.h>
20
#include <linux/io.h>
21
#include <linux/module.h>
22
#include <linux/platform_device.h>
23
#include <linux/slab.h>
24
#include <linux/sched.h>
25
#include <plat/keypad.h>
26
27
#define SAMSUNG_KEYIFCON 0x00
28
#define SAMSUNG_KEYIFSTSCLR 0x04
29
#define SAMSUNG_KEYIFCOL 0x08
30
#define SAMSUNG_KEYIFROW 0x0c
31
#define SAMSUNG_KEYIFFC 0x10
32
33
/* SAMSUNG_KEYIFCON */
34
#define SAMSUNG_KEYIFCON_INT_F_EN (1 << 0)
35
#define SAMSUNG_KEYIFCON_INT_R_EN (1 << 1)
36
#define SAMSUNG_KEYIFCON_DF_EN (1 << 2)
37
#define SAMSUNG_KEYIFCON_FC_EN (1 << 3)
38
#define SAMSUNG_KEYIFCON_WAKEUPEN (1 << 4)
39
40
/* SAMSUNG_KEYIFSTSCLR */
41
#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK (0xff << 0)
42
#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK (0xff << 8)
43
#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET 8
44
#define S5PV210_KEYIFSTSCLR_P_INT_MASK (0x3fff << 0)
45
#define S5PV210_KEYIFSTSCLR_R_INT_MASK (0x3fff << 16)
46
#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET 16
47
48
/* SAMSUNG_KEYIFCOL */
49
#define SAMSUNG_KEYIFCOL_MASK (0xff << 0)
50
#define S5PV210_KEYIFCOLEN_MASK (0xff << 8)
51
52
/* SAMSUNG_KEYIFROW */
53
#define SAMSUNG_KEYIFROW_MASK (0xff << 0)
54
#define S5PV210_KEYIFROW_MASK (0x3fff << 0)
55
56
/* SAMSUNG_KEYIFFC */
57
#define SAMSUNG_KEYIFFC_MASK (0x3ff << 0)
58
59
enum samsung_keypad_type {
60
KEYPAD_TYPE_SAMSUNG,
61
KEYPAD_TYPE_S5PV210,
62
};
63
64
struct samsung_keypad {
65
struct input_dev *input_dev;
66
struct clk *clk;
67
void __iomem *base;
68
wait_queue_head_t wait;
69
bool stopped;
70
int irq;
71
unsigned int row_shift;
72
unsigned int rows;
73
unsigned int cols;
74
unsigned int row_state[SAMSUNG_MAX_COLS];
75
unsigned short keycodes[];
76
};
77
78
static int samsung_keypad_is_s5pv210(struct device *dev)
79
{
80
struct platform_device *pdev = to_platform_device(dev);
81
enum samsung_keypad_type type =
82
platform_get_device_id(pdev)->driver_data;
83
84
return type == KEYPAD_TYPE_S5PV210;
85
}
86
87
static void samsung_keypad_scan(struct samsung_keypad *keypad,
88
unsigned int *row_state)
89
{
90
struct device *dev = keypad->input_dev->dev.parent;
91
unsigned int col;
92
unsigned int val;
93
94
for (col = 0; col < keypad->cols; col++) {
95
if (samsung_keypad_is_s5pv210(dev)) {
96
val = S5PV210_KEYIFCOLEN_MASK;
97
val &= ~(1 << col) << 8;
98
} else {
99
val = SAMSUNG_KEYIFCOL_MASK;
100
val &= ~(1 << col);
101
}
102
103
writel(val, keypad->base + SAMSUNG_KEYIFCOL);
104
mdelay(1);
105
106
val = readl(keypad->base + SAMSUNG_KEYIFROW);
107
row_state[col] = ~val & ((1 << keypad->rows) - 1);
108
}
109
110
/* KEYIFCOL reg clear */
111
writel(0, keypad->base + SAMSUNG_KEYIFCOL);
112
}
113
114
static bool samsung_keypad_report(struct samsung_keypad *keypad,
115
unsigned int *row_state)
116
{
117
struct input_dev *input_dev = keypad->input_dev;
118
unsigned int changed;
119
unsigned int pressed;
120
unsigned int key_down = 0;
121
unsigned int val;
122
unsigned int col, row;
123
124
for (col = 0; col < keypad->cols; col++) {
125
changed = row_state[col] ^ keypad->row_state[col];
126
key_down |= row_state[col];
127
if (!changed)
128
continue;
129
130
for (row = 0; row < keypad->rows; row++) {
131
if (!(changed & (1 << row)))
132
continue;
133
134
pressed = row_state[col] & (1 << row);
135
136
dev_dbg(&keypad->input_dev->dev,
137
"key %s, row: %d, col: %d\n",
138
pressed ? "pressed" : "released", row, col);
139
140
val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
141
142
input_event(input_dev, EV_MSC, MSC_SCAN, val);
143
input_report_key(input_dev,
144
keypad->keycodes[val], pressed);
145
}
146
input_sync(keypad->input_dev);
147
}
148
149
memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));
150
151
return key_down;
152
}
153
154
static irqreturn_t samsung_keypad_irq(int irq, void *dev_id)
155
{
156
struct samsung_keypad *keypad = dev_id;
157
unsigned int row_state[SAMSUNG_MAX_COLS];
158
unsigned int val;
159
bool key_down;
160
161
do {
162
val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR);
163
/* Clear interrupt. */
164
writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
165
166
samsung_keypad_scan(keypad, row_state);
167
168
key_down = samsung_keypad_report(keypad, row_state);
169
if (key_down)
170
wait_event_timeout(keypad->wait, keypad->stopped,
171
msecs_to_jiffies(50));
172
173
} while (key_down && !keypad->stopped);
174
175
return IRQ_HANDLED;
176
}
177
178
static void samsung_keypad_start(struct samsung_keypad *keypad)
179
{
180
unsigned int val;
181
182
/* Tell IRQ thread that it may poll the device. */
183
keypad->stopped = false;
184
185
clk_enable(keypad->clk);
186
187
/* Enable interrupt bits. */
188
val = readl(keypad->base + SAMSUNG_KEYIFCON);
189
val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN;
190
writel(val, keypad->base + SAMSUNG_KEYIFCON);
191
192
/* KEYIFCOL reg clear. */
193
writel(0, keypad->base + SAMSUNG_KEYIFCOL);
194
}
195
196
static void samsung_keypad_stop(struct samsung_keypad *keypad)
197
{
198
unsigned int val;
199
200
/* Signal IRQ thread to stop polling and disable the handler. */
201
keypad->stopped = true;
202
wake_up(&keypad->wait);
203
disable_irq(keypad->irq);
204
205
/* Clear interrupt. */
206
writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
207
208
/* Disable interrupt bits. */
209
val = readl(keypad->base + SAMSUNG_KEYIFCON);
210
val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN);
211
writel(val, keypad->base + SAMSUNG_KEYIFCON);
212
213
clk_disable(keypad->clk);
214
215
/*
216
* Now that chip should not generate interrupts we can safely
217
* re-enable the handler.
218
*/
219
enable_irq(keypad->irq);
220
}
221
222
static int samsung_keypad_open(struct input_dev *input_dev)
223
{
224
struct samsung_keypad *keypad = input_get_drvdata(input_dev);
225
226
samsung_keypad_start(keypad);
227
228
return 0;
229
}
230
231
static void samsung_keypad_close(struct input_dev *input_dev)
232
{
233
struct samsung_keypad *keypad = input_get_drvdata(input_dev);
234
235
samsung_keypad_stop(keypad);
236
}
237
238
static int __devinit samsung_keypad_probe(struct platform_device *pdev)
239
{
240
const struct samsung_keypad_platdata *pdata;
241
const struct matrix_keymap_data *keymap_data;
242
struct samsung_keypad *keypad;
243
struct resource *res;
244
struct input_dev *input_dev;
245
unsigned int row_shift;
246
unsigned int keymap_size;
247
int error;
248
249
pdata = pdev->dev.platform_data;
250
if (!pdata) {
251
dev_err(&pdev->dev, "no platform data defined\n");
252
return -EINVAL;
253
}
254
255
keymap_data = pdata->keymap_data;
256
if (!keymap_data) {
257
dev_err(&pdev->dev, "no keymap data defined\n");
258
return -EINVAL;
259
}
260
261
if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS)
262
return -EINVAL;
263
264
if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS)
265
return -EINVAL;
266
267
/* initialize the gpio */
268
if (pdata->cfg_gpio)
269
pdata->cfg_gpio(pdata->rows, pdata->cols);
270
271
row_shift = get_count_order(pdata->cols);
272
keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);
273
274
keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL);
275
input_dev = input_allocate_device();
276
if (!keypad || !input_dev) {
277
error = -ENOMEM;
278
goto err_free_mem;
279
}
280
281
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
282
if (!res) {
283
error = -ENODEV;
284
goto err_free_mem;
285
}
286
287
keypad->base = ioremap(res->start, resource_size(res));
288
if (!keypad->base) {
289
error = -EBUSY;
290
goto err_free_mem;
291
}
292
293
keypad->clk = clk_get(&pdev->dev, "keypad");
294
if (IS_ERR(keypad->clk)) {
295
dev_err(&pdev->dev, "failed to get keypad clk\n");
296
error = PTR_ERR(keypad->clk);
297
goto err_unmap_base;
298
}
299
300
keypad->input_dev = input_dev;
301
keypad->row_shift = row_shift;
302
keypad->rows = pdata->rows;
303
keypad->cols = pdata->cols;
304
init_waitqueue_head(&keypad->wait);
305
306
input_dev->name = pdev->name;
307
input_dev->id.bustype = BUS_HOST;
308
input_dev->dev.parent = &pdev->dev;
309
input_set_drvdata(input_dev, keypad);
310
311
input_dev->open = samsung_keypad_open;
312
input_dev->close = samsung_keypad_close;
313
314
input_dev->evbit[0] = BIT_MASK(EV_KEY);
315
if (!pdata->no_autorepeat)
316
input_dev->evbit[0] |= BIT_MASK(EV_REP);
317
318
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
319
320
input_dev->keycode = keypad->keycodes;
321
input_dev->keycodesize = sizeof(keypad->keycodes[0]);
322
input_dev->keycodemax = pdata->rows << row_shift;
323
324
matrix_keypad_build_keymap(keymap_data, row_shift,
325
input_dev->keycode, input_dev->keybit);
326
327
keypad->irq = platform_get_irq(pdev, 0);
328
if (keypad->irq < 0) {
329
error = keypad->irq;
330
goto err_put_clk;
331
}
332
333
error = request_threaded_irq(keypad->irq, NULL, samsung_keypad_irq,
334
IRQF_ONESHOT, dev_name(&pdev->dev), keypad);
335
if (error) {
336
dev_err(&pdev->dev, "failed to register keypad interrupt\n");
337
goto err_put_clk;
338
}
339
340
error = input_register_device(keypad->input_dev);
341
if (error)
342
goto err_free_irq;
343
344
device_init_wakeup(&pdev->dev, pdata->wakeup);
345
platform_set_drvdata(pdev, keypad);
346
return 0;
347
348
err_free_irq:
349
free_irq(keypad->irq, keypad);
350
err_put_clk:
351
clk_put(keypad->clk);
352
err_unmap_base:
353
iounmap(keypad->base);
354
err_free_mem:
355
input_free_device(input_dev);
356
kfree(keypad);
357
358
return error;
359
}
360
361
static int __devexit samsung_keypad_remove(struct platform_device *pdev)
362
{
363
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
364
365
device_init_wakeup(&pdev->dev, 0);
366
platform_set_drvdata(pdev, NULL);
367
368
input_unregister_device(keypad->input_dev);
369
370
/*
371
* It is safe to free IRQ after unregistering device because
372
* samsung_keypad_close will shut off interrupts.
373
*/
374
free_irq(keypad->irq, keypad);
375
376
clk_put(keypad->clk);
377
378
iounmap(keypad->base);
379
kfree(keypad);
380
381
return 0;
382
}
383
384
#ifdef CONFIG_PM
385
static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad,
386
bool enable)
387
{
388
struct device *dev = keypad->input_dev->dev.parent;
389
unsigned int val;
390
391
clk_enable(keypad->clk);
392
393
val = readl(keypad->base + SAMSUNG_KEYIFCON);
394
if (enable) {
395
val |= SAMSUNG_KEYIFCON_WAKEUPEN;
396
if (device_may_wakeup(dev))
397
enable_irq_wake(keypad->irq);
398
} else {
399
val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
400
if (device_may_wakeup(dev))
401
disable_irq_wake(keypad->irq);
402
}
403
writel(val, keypad->base + SAMSUNG_KEYIFCON);
404
405
clk_disable(keypad->clk);
406
}
407
408
static int samsung_keypad_suspend(struct device *dev)
409
{
410
struct platform_device *pdev = to_platform_device(dev);
411
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
412
struct input_dev *input_dev = keypad->input_dev;
413
414
mutex_lock(&input_dev->mutex);
415
416
if (input_dev->users)
417
samsung_keypad_stop(keypad);
418
419
samsung_keypad_toggle_wakeup(keypad, true);
420
421
mutex_unlock(&input_dev->mutex);
422
423
return 0;
424
}
425
426
static int samsung_keypad_resume(struct device *dev)
427
{
428
struct platform_device *pdev = to_platform_device(dev);
429
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
430
struct input_dev *input_dev = keypad->input_dev;
431
432
mutex_lock(&input_dev->mutex);
433
434
samsung_keypad_toggle_wakeup(keypad, false);
435
436
if (input_dev->users)
437
samsung_keypad_start(keypad);
438
439
mutex_unlock(&input_dev->mutex);
440
441
return 0;
442
}
443
444
static const struct dev_pm_ops samsung_keypad_pm_ops = {
445
.suspend = samsung_keypad_suspend,
446
.resume = samsung_keypad_resume,
447
};
448
#endif
449
450
static struct platform_device_id samsung_keypad_driver_ids[] = {
451
{
452
.name = "samsung-keypad",
453
.driver_data = KEYPAD_TYPE_SAMSUNG,
454
}, {
455
.name = "s5pv210-keypad",
456
.driver_data = KEYPAD_TYPE_S5PV210,
457
},
458
{ },
459
};
460
MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids);
461
462
static struct platform_driver samsung_keypad_driver = {
463
.probe = samsung_keypad_probe,
464
.remove = __devexit_p(samsung_keypad_remove),
465
.driver = {
466
.name = "samsung-keypad",
467
.owner = THIS_MODULE,
468
#ifdef CONFIG_PM
469
.pm = &samsung_keypad_pm_ops,
470
#endif
471
},
472
.id_table = samsung_keypad_driver_ids,
473
};
474
475
static int __init samsung_keypad_init(void)
476
{
477
return platform_driver_register(&samsung_keypad_driver);
478
}
479
module_init(samsung_keypad_init);
480
481
static void __exit samsung_keypad_exit(void)
482
{
483
platform_driver_unregister(&samsung_keypad_driver);
484
}
485
module_exit(samsung_keypad_exit);
486
487
MODULE_DESCRIPTION("Samsung keypad driver");
488
MODULE_AUTHOR("Joonyoung Shim <[email protected]>");
489
MODULE_AUTHOR("Donghwa Lee <[email protected]>");
490
MODULE_LICENSE("GPL");
491
MODULE_ALIAS("platform:samsung-keypad");
492
493