blob: c7275481a74e93ffbcf5a323d0c24ae0d4fe2312 [file] [log] [blame]
Abraham Arcea17f7952010-08-31 17:05:27 -07001/*
2 * OMAP4 Keypad Driver
3 *
4 * Copyright (C) 2010 Texas Instruments
5 *
6 * Author: Abraham Arce <x0066660@ti.com>
7 * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/interrupt.h>
27#include <linux/platform_device.h>
28#include <linux/errno.h>
29#include <linux/io.h>
Sourav Poddar13987432012-07-13 00:10:47 -070030#include <linux/of.h>
Abraham Arcea17f7952010-08-31 17:05:27 -070031#include <linux/input.h>
32#include <linux/slab.h>
Abraham Arce5ad567f2011-02-22 22:25:59 -080033#include <linux/pm_runtime.h>
Abraham Arcea17f7952010-08-31 17:05:27 -070034
Felipe Balbi0f1142a2012-03-16 22:47:48 -070035#include <linux/platform_data/omap4-keypad.h>
Abraham Arcea17f7952010-08-31 17:05:27 -070036
37/* OMAP4 registers */
38#define OMAP4_KBD_REVISION 0x00
39#define OMAP4_KBD_SYSCONFIG 0x10
40#define OMAP4_KBD_SYSSTATUS 0x14
41#define OMAP4_KBD_IRQSTATUS 0x18
42#define OMAP4_KBD_IRQENABLE 0x1C
43#define OMAP4_KBD_WAKEUPENABLE 0x20
44#define OMAP4_KBD_PENDING 0x24
45#define OMAP4_KBD_CTRL 0x28
46#define OMAP4_KBD_DEBOUNCINGTIME 0x2C
47#define OMAP4_KBD_LONGKEYTIME 0x30
48#define OMAP4_KBD_TIMEOUT 0x34
49#define OMAP4_KBD_STATEMACHINE 0x38
50#define OMAP4_KBD_ROWINPUTS 0x3C
51#define OMAP4_KBD_COLUMNOUTPUTS 0x40
52#define OMAP4_KBD_FULLCODE31_0 0x44
53#define OMAP4_KBD_FULLCODE63_32 0x48
54
55/* OMAP4 bit definitions */
Illia Smyrnov875ad692013-08-25 23:25:18 -070056#define OMAP4_DEF_IRQENABLE_EVENTEN BIT(0)
57#define OMAP4_DEF_IRQENABLE_LONGKEY BIT(1)
58#define OMAP4_DEF_WUP_EVENT_ENA BIT(0)
59#define OMAP4_DEF_WUP_LONG_KEY_ENA BIT(1)
60#define OMAP4_DEF_CTRL_NOSOFTMODE BIT(1)
61#define OMAP4_DEF_CTRL_PTV_SHIFT 2
Abraham Arcea17f7952010-08-31 17:05:27 -070062
63/* OMAP4 values */
Illia Smyrnov875ad692013-08-25 23:25:18 -070064#define OMAP4_VAL_IRQDISABLE 0x0
65#define OMAP4_VAL_DEBOUNCINGTIME 0x7
66#define OMAP4_VAL_PVT 0x7
Abraham Arcea17f7952010-08-31 17:05:27 -070067
Poddar, Souravf77621c2012-05-10 22:32:00 -070068enum {
69 KBD_REVISION_OMAP4 = 0,
70 KBD_REVISION_OMAP5,
71};
72
Abraham Arcea17f7952010-08-31 17:05:27 -070073struct omap4_keypad {
74 struct input_dev *input;
75
76 void __iomem *base;
Poddar, Souravf77621c2012-05-10 22:32:00 -070077 unsigned int irq;
Abraham Arcea17f7952010-08-31 17:05:27 -070078
79 unsigned int rows;
80 unsigned int cols;
Poddar, Souravf77621c2012-05-10 22:32:00 -070081 u32 reg_offset;
82 u32 irqreg_offset;
Abraham Arcea17f7952010-08-31 17:05:27 -070083 unsigned int row_shift;
Sourav Poddar13987432012-07-13 00:10:47 -070084 bool no_autorepeat;
Abraham Arcea17f7952010-08-31 17:05:27 -070085 unsigned char key_state[8];
Sourav Poddar13987432012-07-13 00:10:47 -070086 unsigned short *keymap;
Abraham Arcea17f7952010-08-31 17:05:27 -070087};
88
Poddar, Souravf77621c2012-05-10 22:32:00 -070089static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset)
90{
91 return __raw_readl(keypad_data->base +
92 keypad_data->reg_offset + offset);
93}
94
95static void kbd_writel(struct omap4_keypad *keypad_data, u32 offset, u32 value)
96{
97 __raw_writel(value,
98 keypad_data->base + keypad_data->reg_offset + offset);
99}
100
101static int kbd_read_irqreg(struct omap4_keypad *keypad_data, u32 offset)
102{
103 return __raw_readl(keypad_data->base +
104 keypad_data->irqreg_offset + offset);
105}
106
107static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
108 u32 offset, u32 value)
109{
110 __raw_writel(value,
111 keypad_data->base + keypad_data->irqreg_offset + offset);
112}
113
114
Abraham Arcea17f7952010-08-31 17:05:27 -0700115/* Interrupt handler */
116static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
117{
118 struct omap4_keypad *keypad_data = dev_id;
119 struct input_dev *input_dev = keypad_data->input;
120 unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
121 unsigned int col, row, code, changed;
122 u32 *new_state = (u32 *) key_state;
123
124 /* Disable interrupts */
Poddar, Souravf77621c2012-05-10 22:32:00 -0700125 kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
126 OMAP4_VAL_IRQDISABLE);
Abraham Arcea17f7952010-08-31 17:05:27 -0700127
Poddar, Souravf77621c2012-05-10 22:32:00 -0700128 *new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
129 *(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
Abraham Arcea17f7952010-08-31 17:05:27 -0700130
131 for (row = 0; row < keypad_data->rows; row++) {
132 changed = key_state[row] ^ keypad_data->key_state[row];
133 if (!changed)
134 continue;
135
136 for (col = 0; col < keypad_data->cols; col++) {
137 if (changed & (1 << col)) {
138 code = MATRIX_SCAN_CODE(row, col,
139 keypad_data->row_shift);
140 input_event(input_dev, EV_MSC, MSC_SCAN, code);
141 input_report_key(input_dev,
142 keypad_data->keymap[code],
143 key_state[row] & (1 << col));
144 }
145 }
146 }
147
148 input_sync(input_dev);
149
150 memcpy(keypad_data->key_state, key_state,
151 sizeof(keypad_data->key_state));
152
153 /* clear pending interrupts */
Poddar, Souravf77621c2012-05-10 22:32:00 -0700154 kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
155 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
Abraham Arcea17f7952010-08-31 17:05:27 -0700156
157 /* enable interrupts */
Poddar, Souravf77621c2012-05-10 22:32:00 -0700158 kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
159 OMAP4_DEF_IRQENABLE_EVENTEN |
160 OMAP4_DEF_IRQENABLE_LONGKEY);
Abraham Arcea17f7952010-08-31 17:05:27 -0700161
162 return IRQ_HANDLED;
163}
164
Abraham Arce5ad567f2011-02-22 22:25:59 -0800165static int omap4_keypad_open(struct input_dev *input)
166{
167 struct omap4_keypad *keypad_data = input_get_drvdata(input);
168
169 pm_runtime_get_sync(input->dev.parent);
170
171 disable_irq(keypad_data->irq);
172
Poddar, Souravf77621c2012-05-10 22:32:00 -0700173 kbd_writel(keypad_data, OMAP4_KBD_CTRL,
Illia Smyrnov875ad692013-08-25 23:25:18 -0700174 OMAP4_DEF_CTRL_NOSOFTMODE |
175 (OMAP4_VAL_PVT << OMAP4_DEF_CTRL_PTV_SHIFT));
Poddar, Souravf77621c2012-05-10 22:32:00 -0700176 kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME,
177 OMAP4_VAL_DEBOUNCINGTIME);
178 kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
179 OMAP4_VAL_IRQDISABLE);
180 kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
181 OMAP4_DEF_IRQENABLE_EVENTEN |
182 OMAP4_DEF_IRQENABLE_LONGKEY);
183 kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE,
184 OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA);
Abraham Arce5ad567f2011-02-22 22:25:59 -0800185
186 enable_irq(keypad_data->irq);
187
188 return 0;
189}
190
191static void omap4_keypad_close(struct input_dev *input)
192{
193 struct omap4_keypad *keypad_data = input_get_drvdata(input);
194
195 disable_irq(keypad_data->irq);
196
197 /* Disable interrupts */
Poddar, Souravf77621c2012-05-10 22:32:00 -0700198 kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
199 OMAP4_VAL_IRQDISABLE);
Abraham Arce5ad567f2011-02-22 22:25:59 -0800200
201 /* clear pending interrupts */
Poddar, Souravf77621c2012-05-10 22:32:00 -0700202 kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
203 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
Abraham Arce5ad567f2011-02-22 22:25:59 -0800204
205 enable_irq(keypad_data->irq);
206
207 pm_runtime_put_sync(input->dev.parent);
208}
209
Sourav Poddar13987432012-07-13 00:10:47 -0700210#ifdef CONFIG_OF
Bill Pemberton5298cc42012-11-23 21:38:25 -0800211static int omap4_keypad_parse_dt(struct device *dev,
212 struct omap4_keypad *keypad_data)
Sourav Poddar13987432012-07-13 00:10:47 -0700213{
214 struct device_node *np = dev->of_node;
Simon Glass43840412013-02-25 14:08:40 -0800215 int err;
Sourav Poddar13987432012-07-13 00:10:47 -0700216
Simon Glass43840412013-02-25 14:08:40 -0800217 err = matrix_keypad_parse_of_params(dev, &keypad_data->rows,
218 &keypad_data->cols);
219 if (err)
220 return err;
Sourav Poddar13987432012-07-13 00:10:47 -0700221
222 if (of_get_property(np, "linux,input-no-autorepeat", NULL))
223 keypad_data->no_autorepeat = true;
224
225 return 0;
226}
227#else
228static inline int omap4_keypad_parse_dt(struct device *dev,
229 struct omap4_keypad *keypad_data)
230{
231 return -ENOSYS;
232}
233#endif
234
Bill Pemberton5298cc42012-11-23 21:38:25 -0800235static int omap4_keypad_probe(struct platform_device *pdev)
Abraham Arcea17f7952010-08-31 17:05:27 -0700236{
Sourav Poddar13987432012-07-13 00:10:47 -0700237 const struct omap4_keypad_platform_data *pdata =
238 dev_get_platdata(&pdev->dev);
239 const struct matrix_keymap_data *keymap_data =
240 pdata ? pdata->keymap_data : NULL;
Abraham Arcea17f7952010-08-31 17:05:27 -0700241 struct omap4_keypad *keypad_data;
242 struct input_dev *input_dev;
Abraham Arcef3a1ba62010-09-29 23:35:57 -0700243 struct resource *res;
Sourav Poddar13987432012-07-13 00:10:47 -0700244 unsigned int max_keys;
Poddar, Souravf77621c2012-05-10 22:32:00 -0700245 int rev;
Abraham Arcef3a1ba62010-09-29 23:35:57 -0700246 int irq;
Abraham Arcea17f7952010-08-31 17:05:27 -0700247 int error;
248
Abraham Arcef3a1ba62010-09-29 23:35:57 -0700249 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
250 if (!res) {
Abraham Arcea17f7952010-08-31 17:05:27 -0700251 dev_err(&pdev->dev, "no base address specified\n");
252 return -EINVAL;
253 }
254
Abraham Arcef3a1ba62010-09-29 23:35:57 -0700255 irq = platform_get_irq(pdev, 0);
256 if (!irq) {
Abraham Arcea17f7952010-08-31 17:05:27 -0700257 dev_err(&pdev->dev, "no keyboard irq assigned\n");
258 return -EINVAL;
259 }
260
Sourav Poddar13987432012-07-13 00:10:47 -0700261 keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
Abraham Arcea17f7952010-08-31 17:05:27 -0700262 if (!keypad_data) {
263 dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
264 return -ENOMEM;
265 }
266
Sourav Poddar13987432012-07-13 00:10:47 -0700267 keypad_data->irq = irq;
Abraham Arcea17f7952010-08-31 17:05:27 -0700268
Sourav Poddar13987432012-07-13 00:10:47 -0700269 if (pdata) {
270 keypad_data->rows = pdata->rows;
271 keypad_data->cols = pdata->cols;
272 } else {
273 error = omap4_keypad_parse_dt(&pdev->dev, keypad_data);
274 if (error)
275 return error;
276 }
277
278 res = request_mem_region(res->start, resource_size(res), pdev->name);
Abraham Arcef3a1ba62010-09-29 23:35:57 -0700279 if (!res) {
280 dev_err(&pdev->dev, "can't request mem region\n");
281 error = -EBUSY;
282 goto err_free_keypad;
283 }
284
285 keypad_data->base = ioremap(res->start, resource_size(res));
286 if (!keypad_data->base) {
287 dev_err(&pdev->dev, "can't ioremap mem resource\n");
288 error = -ENOMEM;
289 goto err_release_mem;
290 }
291
Abraham Arcea17f7952010-08-31 17:05:27 -0700292
Poddar, Souravf77621c2012-05-10 22:32:00 -0700293 /*
Sourav Poddar13987432012-07-13 00:10:47 -0700294 * Enable clocks for the keypad module so that we can read
295 * revision register.
296 */
Poddar, Souravf77621c2012-05-10 22:32:00 -0700297 pm_runtime_enable(&pdev->dev);
298 error = pm_runtime_get_sync(&pdev->dev);
299 if (error) {
300 dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
301 goto err_unmap;
302 }
303 rev = __raw_readl(keypad_data->base + OMAP4_KBD_REVISION);
304 rev &= 0x03 << 30;
305 rev >>= 30;
306 switch (rev) {
307 case KBD_REVISION_OMAP4:
308 keypad_data->reg_offset = 0x00;
309 keypad_data->irqreg_offset = 0x00;
310 break;
311 case KBD_REVISION_OMAP5:
312 keypad_data->reg_offset = 0x10;
313 keypad_data->irqreg_offset = 0x0c;
314 break;
315 default:
316 dev_err(&pdev->dev,
317 "Keypad reports unsupported revision %d", rev);
318 error = -EINVAL;
319 goto err_pm_put_sync;
320 }
321
Abraham Arcea17f7952010-08-31 17:05:27 -0700322 /* input device allocation */
323 keypad_data->input = input_dev = input_allocate_device();
324 if (!input_dev) {
325 error = -ENOMEM;
Poddar, Souravf77621c2012-05-10 22:32:00 -0700326 goto err_pm_put_sync;
Abraham Arcea17f7952010-08-31 17:05:27 -0700327 }
328
329 input_dev->name = pdev->name;
330 input_dev->dev.parent = &pdev->dev;
331 input_dev->id.bustype = BUS_HOST;
332 input_dev->id.vendor = 0x0001;
333 input_dev->id.product = 0x0001;
334 input_dev->id.version = 0x0001;
335
Abraham Arce5ad567f2011-02-22 22:25:59 -0800336 input_dev->open = omap4_keypad_open;
337 input_dev->close = omap4_keypad_close;
338
Sourav Poddar13987432012-07-13 00:10:47 -0700339 input_set_capability(input_dev, EV_MSC, MSC_SCAN);
340 if (!keypad_data->no_autorepeat)
341 __set_bit(EV_REP, input_dev->evbit);
342
343 input_set_drvdata(input_dev, keypad_data);
344
345 keypad_data->row_shift = get_count_order(keypad_data->cols);
346 max_keys = keypad_data->rows << keypad_data->row_shift;
347 keypad_data->keymap = kzalloc(max_keys * sizeof(keypad_data->keymap[0]),
348 GFP_KERNEL);
349 if (!keypad_data->keymap) {
350 dev_err(&pdev->dev, "Not enough memory for keymap\n");
351 error = -ENOMEM;
Dmitry Torokhov19328112012-05-10 22:37:08 -0700352 goto err_free_input;
353 }
Abraham Arcea17f7952010-08-31 17:05:27 -0700354
Sourav Poddar13987432012-07-13 00:10:47 -0700355 error = matrix_keypad_build_keymap(keymap_data, NULL,
356 keypad_data->rows, keypad_data->cols,
357 keypad_data->keymap, input_dev);
358 if (error) {
359 dev_err(&pdev->dev, "failed to build keymap\n");
360 goto err_free_keymap;
361 }
Abraham Arcea17f7952010-08-31 17:05:27 -0700362
Abraham Arcea17f7952010-08-31 17:05:27 -0700363 error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
Abraham Arcef8038c42010-09-29 23:37:04 -0700364 IRQF_TRIGGER_RISING,
Abraham Arcea17f7952010-08-31 17:05:27 -0700365 "omap4-keypad", keypad_data);
366 if (error) {
367 dev_err(&pdev->dev, "failed to register interrupt\n");
368 goto err_free_input;
369 }
370
Poddar, Souravf77621c2012-05-10 22:32:00 -0700371 pm_runtime_put_sync(&pdev->dev);
Abraham Arce5ad567f2011-02-22 22:25:59 -0800372
Abraham Arcea17f7952010-08-31 17:05:27 -0700373 error = input_register_device(keypad_data->input);
374 if (error < 0) {
375 dev_err(&pdev->dev, "failed to register input device\n");
Abraham Arce5ad567f2011-02-22 22:25:59 -0800376 goto err_pm_disable;
Abraham Arcea17f7952010-08-31 17:05:27 -0700377 }
378
Abraham Arcea17f7952010-08-31 17:05:27 -0700379 platform_set_drvdata(pdev, keypad_data);
380 return 0;
381
Abraham Arce5ad567f2011-02-22 22:25:59 -0800382err_pm_disable:
383 pm_runtime_disable(&pdev->dev);
Abraham Arcea17f7952010-08-31 17:05:27 -0700384 free_irq(keypad_data->irq, keypad_data);
Sourav Poddar13987432012-07-13 00:10:47 -0700385err_free_keymap:
386 kfree(keypad_data->keymap);
Abraham Arcea17f7952010-08-31 17:05:27 -0700387err_free_input:
388 input_free_device(input_dev);
Poddar, Souravf77621c2012-05-10 22:32:00 -0700389err_pm_put_sync:
390 pm_runtime_put_sync(&pdev->dev);
Abraham Arcef3a1ba62010-09-29 23:35:57 -0700391err_unmap:
392 iounmap(keypad_data->base);
393err_release_mem:
Sourav Poddar13987432012-07-13 00:10:47 -0700394 release_mem_region(res->start, resource_size(res));
Abraham Arcea17f7952010-08-31 17:05:27 -0700395err_free_keypad:
396 kfree(keypad_data);
397 return error;
398}
399
Bill Pembertone2619cf2012-11-23 21:50:47 -0800400static int omap4_keypad_remove(struct platform_device *pdev)
Abraham Arcea17f7952010-08-31 17:05:27 -0700401{
402 struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
Abraham Arcef3a1ba62010-09-29 23:35:57 -0700403 struct resource *res;
Abraham Arcea17f7952010-08-31 17:05:27 -0700404
405 free_irq(keypad_data->irq, keypad_data);
Abraham Arce5ad567f2011-02-22 22:25:59 -0800406
407 pm_runtime_disable(&pdev->dev);
408
Abraham Arcea17f7952010-08-31 17:05:27 -0700409 input_unregister_device(keypad_data->input);
Abraham Arcef3a1ba62010-09-29 23:35:57 -0700410
411 iounmap(keypad_data->base);
412
413 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
414 release_mem_region(res->start, resource_size(res));
415
Sourav Poddar13987432012-07-13 00:10:47 -0700416 kfree(keypad_data->keymap);
Abraham Arcea17f7952010-08-31 17:05:27 -0700417 kfree(keypad_data);
Sourav Poddar13987432012-07-13 00:10:47 -0700418
Abraham Arcea17f7952010-08-31 17:05:27 -0700419 return 0;
420}
421
Sourav Poddar13987432012-07-13 00:10:47 -0700422#ifdef CONFIG_OF
423static const struct of_device_id omap_keypad_dt_match[] = {
424 { .compatible = "ti,omap4-keypad" },
425 {},
426};
427MODULE_DEVICE_TABLE(of, omap_keypad_dt_match);
428#endif
429
Abraham Arcea17f7952010-08-31 17:05:27 -0700430static struct platform_driver omap4_keypad_driver = {
431 .probe = omap4_keypad_probe,
Bill Pemberton1cb0aa82012-11-23 21:27:39 -0800432 .remove = omap4_keypad_remove,
Abraham Arcea17f7952010-08-31 17:05:27 -0700433 .driver = {
434 .name = "omap4-keypad",
435 .owner = THIS_MODULE,
Sourav Poddar13987432012-07-13 00:10:47 -0700436 .of_match_table = of_match_ptr(omap_keypad_dt_match),
Abraham Arcea17f7952010-08-31 17:05:27 -0700437 },
438};
JJ Ding5146c842011-11-29 11:08:39 -0800439module_platform_driver(omap4_keypad_driver);
Abraham Arcea17f7952010-08-31 17:05:27 -0700440
441MODULE_AUTHOR("Texas Instruments");
442MODULE_DESCRIPTION("OMAP4 Keypad Driver");
443MODULE_LICENSE("GPL");
444MODULE_ALIAS("platform:omap4-keypad");