xref: /linux/drivers/input/keyboard/pxa27x_keypad.c (revision 0e5f11aa80bd01d048f374cc64ef0819ad7d86f2)
1*0e5f11aaSEric Miao /*
2*0e5f11aaSEric Miao  * linux/drivers/input/keyboard/pxa27x_keypad.c
3*0e5f11aaSEric Miao  *
4*0e5f11aaSEric Miao  * Driver for the pxa27x matrix keyboard controller.
5*0e5f11aaSEric Miao  *
6*0e5f11aaSEric Miao  * Created:	Feb 22, 2007
7*0e5f11aaSEric Miao  * Author:	Rodolfo Giometti <giometti@linux.it>
8*0e5f11aaSEric Miao  *
9*0e5f11aaSEric Miao  * Based on a previous implementations by Kevin O'Connor
10*0e5f11aaSEric Miao  * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and
11*0e5f11aaSEric Miao  * on some suggestions by Nicolas Pitre <nico@cam.org>.
12*0e5f11aaSEric Miao  *
13*0e5f11aaSEric Miao  * This program is free software; you can redistribute it and/or modify
14*0e5f11aaSEric Miao  * it under the terms of the GNU General Public License version 2 as
15*0e5f11aaSEric Miao  * published by the Free Software Foundation.
16*0e5f11aaSEric Miao  */
17*0e5f11aaSEric Miao 
18*0e5f11aaSEric Miao 
19*0e5f11aaSEric Miao #include <linux/kernel.h>
20*0e5f11aaSEric Miao #include <linux/module.h>
21*0e5f11aaSEric Miao #include <linux/init.h>
22*0e5f11aaSEric Miao #include <linux/interrupt.h>
23*0e5f11aaSEric Miao #include <linux/input.h>
24*0e5f11aaSEric Miao #include <linux/device.h>
25*0e5f11aaSEric Miao #include <linux/platform_device.h>
26*0e5f11aaSEric Miao #include <linux/clk.h>
27*0e5f11aaSEric Miao #include <linux/err.h>
28*0e5f11aaSEric Miao 
29*0e5f11aaSEric Miao #include <asm/mach-types.h>
30*0e5f11aaSEric Miao #include <asm/mach/arch.h>
31*0e5f11aaSEric Miao #include <asm/mach/map.h>
32*0e5f11aaSEric Miao 
33*0e5f11aaSEric Miao #include <asm/arch/hardware.h>
34*0e5f11aaSEric Miao #include <asm/arch/pxa-regs.h>
35*0e5f11aaSEric Miao #include <asm/arch/irqs.h>
36*0e5f11aaSEric Miao #include <asm/arch/pxa27x_keypad.h>
37*0e5f11aaSEric Miao 
38*0e5f11aaSEric Miao #define DRIVER_NAME		"pxa27x-keypad"
39*0e5f11aaSEric Miao 
40*0e5f11aaSEric Miao #define KPASMKP(col)		(col/2 == 0 ? KPASMKP0 : \
41*0e5f11aaSEric Miao 				 col/2 == 1 ? KPASMKP1 : \
42*0e5f11aaSEric Miao 				 col/2 == 2 ? KPASMKP2 : KPASMKP3)
43*0e5f11aaSEric Miao #define KPASMKPx_MKC(row, col)	(1 << (row + 16 * (col % 2)))
44*0e5f11aaSEric Miao 
45*0e5f11aaSEric Miao static struct clk *pxa27x_keypad_clk;
46*0e5f11aaSEric Miao 
47*0e5f11aaSEric Miao static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
48*0e5f11aaSEric Miao {
49*0e5f11aaSEric Miao 	struct platform_device *pdev = dev_id;
50*0e5f11aaSEric Miao 	struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
51*0e5f11aaSEric Miao 	struct input_dev *input_dev = platform_get_drvdata(pdev);
52*0e5f11aaSEric Miao 	unsigned long kpc = KPC;
53*0e5f11aaSEric Miao 	int p, row, col, rel;
54*0e5f11aaSEric Miao 
55*0e5f11aaSEric Miao 	if (kpc & KPC_DI) {
56*0e5f11aaSEric Miao 		unsigned long kpdk = KPDK;
57*0e5f11aaSEric Miao 
58*0e5f11aaSEric Miao 		if (!(kpdk & KPDK_DKP)) {
59*0e5f11aaSEric Miao 			/* better luck next time */
60*0e5f11aaSEric Miao 		} else if (kpc & KPC_REE0) {
61*0e5f11aaSEric Miao 			unsigned long kprec = KPREC;
62*0e5f11aaSEric Miao 			KPREC = 0x7f;
63*0e5f11aaSEric Miao 
64*0e5f11aaSEric Miao 			if (kprec & KPREC_OF0)
65*0e5f11aaSEric Miao 				rel = (kprec & 0xff) + 0x7f;
66*0e5f11aaSEric Miao 			else if (kprec & KPREC_UF0)
67*0e5f11aaSEric Miao 				rel = (kprec & 0xff) - 0x7f - 0xff;
68*0e5f11aaSEric Miao 			else
69*0e5f11aaSEric Miao 				rel = (kprec & 0xff) - 0x7f;
70*0e5f11aaSEric Miao 
71*0e5f11aaSEric Miao 			if (rel) {
72*0e5f11aaSEric Miao 				input_report_rel(input_dev, REL_WHEEL, rel);
73*0e5f11aaSEric Miao 				input_sync(input_dev);
74*0e5f11aaSEric Miao 			}
75*0e5f11aaSEric Miao 		}
76*0e5f11aaSEric Miao 	}
77*0e5f11aaSEric Miao 
78*0e5f11aaSEric Miao 	if (kpc & KPC_MI) {
79*0e5f11aaSEric Miao 		/* report the status of every button */
80*0e5f11aaSEric Miao 		for (row = 0; row < pdata->nr_rows; row++) {
81*0e5f11aaSEric Miao 			for (col = 0; col < pdata->nr_cols; col++) {
82*0e5f11aaSEric Miao 				p = KPASMKP(col) & KPASMKPx_MKC(row, col) ?
83*0e5f11aaSEric Miao 					1 : 0;
84*0e5f11aaSEric Miao 				pr_debug("keycode %x - pressed %x\n",
85*0e5f11aaSEric Miao 						pdata->keycodes[row][col], p);
86*0e5f11aaSEric Miao 				input_report_key(input_dev,
87*0e5f11aaSEric Miao 						pdata->keycodes[row][col], p);
88*0e5f11aaSEric Miao 			}
89*0e5f11aaSEric Miao 		}
90*0e5f11aaSEric Miao 		input_sync(input_dev);
91*0e5f11aaSEric Miao 	}
92*0e5f11aaSEric Miao 
93*0e5f11aaSEric Miao 	return IRQ_HANDLED;
94*0e5f11aaSEric Miao }
95*0e5f11aaSEric Miao 
96*0e5f11aaSEric Miao static int pxa27x_keypad_open(struct input_dev *dev)
97*0e5f11aaSEric Miao {
98*0e5f11aaSEric Miao 	/* Set keypad control register */
99*0e5f11aaSEric Miao 	KPC |= (KPC_ASACT |
100*0e5f11aaSEric Miao 		KPC_MS_ALL |
101*0e5f11aaSEric Miao 		(2 << 6) | KPC_REE0 | KPC_DK_DEB_SEL |
102*0e5f11aaSEric Miao 		KPC_ME | KPC_MIE | KPC_DE | KPC_DIE);
103*0e5f11aaSEric Miao 
104*0e5f11aaSEric Miao 	KPC &= ~KPC_AS;         /* disable automatic scan */
105*0e5f11aaSEric Miao 	KPC &= ~KPC_IMKP;       /* do not ignore multiple keypresses */
106*0e5f11aaSEric Miao 
107*0e5f11aaSEric Miao 	/* Set rotary count to mid-point value */
108*0e5f11aaSEric Miao 	KPREC = 0x7F;
109*0e5f11aaSEric Miao 
110*0e5f11aaSEric Miao 	/* Enable unit clock */
111*0e5f11aaSEric Miao 	clk_enable(pxa27x_keypad_clk);
112*0e5f11aaSEric Miao 
113*0e5f11aaSEric Miao 	return 0;
114*0e5f11aaSEric Miao }
115*0e5f11aaSEric Miao 
116*0e5f11aaSEric Miao static void pxa27x_keypad_close(struct input_dev *dev)
117*0e5f11aaSEric Miao {
118*0e5f11aaSEric Miao 	/* Disable clock unit */
119*0e5f11aaSEric Miao 	clk_disable(pxa27x_keypad_clk);
120*0e5f11aaSEric Miao }
121*0e5f11aaSEric Miao 
122*0e5f11aaSEric Miao #ifdef CONFIG_PM
123*0e5f11aaSEric Miao static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state)
124*0e5f11aaSEric Miao {
125*0e5f11aaSEric Miao 	struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
126*0e5f11aaSEric Miao 
127*0e5f11aaSEric Miao 	/* Save controller status */
128*0e5f11aaSEric Miao 	pdata->reg_kpc = KPC;
129*0e5f11aaSEric Miao 	pdata->reg_kprec = KPREC;
130*0e5f11aaSEric Miao 
131*0e5f11aaSEric Miao 	return 0;
132*0e5f11aaSEric Miao }
133*0e5f11aaSEric Miao 
134*0e5f11aaSEric Miao static int pxa27x_keypad_resume(struct platform_device *pdev)
135*0e5f11aaSEric Miao {
136*0e5f11aaSEric Miao 	struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
137*0e5f11aaSEric Miao 	struct input_dev *input_dev = platform_get_drvdata(pdev);
138*0e5f11aaSEric Miao 
139*0e5f11aaSEric Miao 	mutex_lock(&input_dev->mutex);
140*0e5f11aaSEric Miao 
141*0e5f11aaSEric Miao 	if (input_dev->users) {
142*0e5f11aaSEric Miao 		/* Restore controller status */
143*0e5f11aaSEric Miao 		KPC = pdata->reg_kpc;
144*0e5f11aaSEric Miao 		KPREC = pdata->reg_kprec;
145*0e5f11aaSEric Miao 
146*0e5f11aaSEric Miao 		/* Enable unit clock */
147*0e5f11aaSEric Miao 		clk_disable(pxa27x_keypad_clk);
148*0e5f11aaSEric Miao 		clk_enable(pxa27x_keypad_clk);
149*0e5f11aaSEric Miao 	}
150*0e5f11aaSEric Miao 
151*0e5f11aaSEric Miao 	mutex_unlock(&input_dev->mutex);
152*0e5f11aaSEric Miao 
153*0e5f11aaSEric Miao 	return 0;
154*0e5f11aaSEric Miao }
155*0e5f11aaSEric Miao #else
156*0e5f11aaSEric Miao #define pxa27x_keypad_suspend	NULL
157*0e5f11aaSEric Miao #define pxa27x_keypad_resume	NULL
158*0e5f11aaSEric Miao #endif
159*0e5f11aaSEric Miao 
160*0e5f11aaSEric Miao static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
161*0e5f11aaSEric Miao {
162*0e5f11aaSEric Miao 	struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
163*0e5f11aaSEric Miao 	struct input_dev *input_dev;
164*0e5f11aaSEric Miao 	int i, row, col, error;
165*0e5f11aaSEric Miao 
166*0e5f11aaSEric Miao 	pxa27x_keypad_clk = clk_get(&pdev->dev, "KBDCLK");
167*0e5f11aaSEric Miao 	if (IS_ERR(pxa27x_keypad_clk)) {
168*0e5f11aaSEric Miao 		error = PTR_ERR(pxa27x_keypad_clk);
169*0e5f11aaSEric Miao 		goto err_clk;
170*0e5f11aaSEric Miao 	}
171*0e5f11aaSEric Miao 
172*0e5f11aaSEric Miao 	/* Create and register the input driver. */
173*0e5f11aaSEric Miao 	input_dev = input_allocate_device();
174*0e5f11aaSEric Miao 	if (!input_dev) {
175*0e5f11aaSEric Miao 		printk(KERN_ERR "Cannot request keypad device\n");
176*0e5f11aaSEric Miao 		error = -ENOMEM;
177*0e5f11aaSEric Miao 		goto err_alloc;
178*0e5f11aaSEric Miao 	}
179*0e5f11aaSEric Miao 
180*0e5f11aaSEric Miao 	input_dev->name = DRIVER_NAME;
181*0e5f11aaSEric Miao 	input_dev->id.bustype = BUS_HOST;
182*0e5f11aaSEric Miao 	input_dev->open = pxa27x_keypad_open;
183*0e5f11aaSEric Miao 	input_dev->close = pxa27x_keypad_close;
184*0e5f11aaSEric Miao 	input_dev->dev.parent = &pdev->dev;
185*0e5f11aaSEric Miao 
186*0e5f11aaSEric Miao 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
187*0e5f11aaSEric Miao 		BIT_MASK(EV_REL);
188*0e5f11aaSEric Miao 	input_dev->relbit[BIT_WORD(REL_WHEEL)] = BIT_MASK(REL_WHEEL);
189*0e5f11aaSEric Miao 	for (row = 0; row < pdata->nr_rows; row++) {
190*0e5f11aaSEric Miao 		for (col = 0; col < pdata->nr_cols; col++) {
191*0e5f11aaSEric Miao 			int code = pdata->keycodes[row][col];
192*0e5f11aaSEric Miao 			if (code > 0)
193*0e5f11aaSEric Miao 				set_bit(code, input_dev->keybit);
194*0e5f11aaSEric Miao 		}
195*0e5f11aaSEric Miao 	}
196*0e5f11aaSEric Miao 
197*0e5f11aaSEric Miao 	error = request_irq(IRQ_KEYPAD, pxa27x_keypad_irq_handler, IRQF_DISABLED,
198*0e5f11aaSEric Miao 			    DRIVER_NAME, pdev);
199*0e5f11aaSEric Miao 	if (error) {
200*0e5f11aaSEric Miao 		printk(KERN_ERR "Cannot request keypad IRQ\n");
201*0e5f11aaSEric Miao 		goto err_free_dev;
202*0e5f11aaSEric Miao 	}
203*0e5f11aaSEric Miao 
204*0e5f11aaSEric Miao 	platform_set_drvdata(pdev, input_dev);
205*0e5f11aaSEric Miao 
206*0e5f11aaSEric Miao 	/* Register the input device */
207*0e5f11aaSEric Miao 	error = input_register_device(input_dev);
208*0e5f11aaSEric Miao 	if (error)
209*0e5f11aaSEric Miao 		goto err_free_irq;
210*0e5f11aaSEric Miao 
211*0e5f11aaSEric Miao 	/* Setup GPIOs. */
212*0e5f11aaSEric Miao 	for (i = 0; i < pdata->nr_rows + pdata->nr_cols; i++)
213*0e5f11aaSEric Miao 		pxa_gpio_mode(pdata->gpio_modes[i]);
214*0e5f11aaSEric Miao 
215*0e5f11aaSEric Miao 	/*
216*0e5f11aaSEric Miao 	 * Store rows/cols info into keyboard registers.
217*0e5f11aaSEric Miao 	 */
218*0e5f11aaSEric Miao 
219*0e5f11aaSEric Miao 	KPC |= (pdata->nr_rows - 1) << 26;
220*0e5f11aaSEric Miao 	KPC |= (pdata->nr_cols - 1) << 23;
221*0e5f11aaSEric Miao 
222*0e5f11aaSEric Miao 	for (col = 0; col < pdata->nr_cols; col++)
223*0e5f11aaSEric Miao 		KPC |= KPC_MS0 << col;
224*0e5f11aaSEric Miao 
225*0e5f11aaSEric Miao 	return 0;
226*0e5f11aaSEric Miao 
227*0e5f11aaSEric Miao  err_free_irq:
228*0e5f11aaSEric Miao 	platform_set_drvdata(pdev, NULL);
229*0e5f11aaSEric Miao 	free_irq(IRQ_KEYPAD, pdev);
230*0e5f11aaSEric Miao  err_free_dev:
231*0e5f11aaSEric Miao 	input_free_device(input_dev);
232*0e5f11aaSEric Miao  err_alloc:
233*0e5f11aaSEric Miao 	clk_put(pxa27x_keypad_clk);
234*0e5f11aaSEric Miao  err_clk:
235*0e5f11aaSEric Miao 	return error;
236*0e5f11aaSEric Miao }
237*0e5f11aaSEric Miao 
238*0e5f11aaSEric Miao static int __devexit pxa27x_keypad_remove(struct platform_device *pdev)
239*0e5f11aaSEric Miao {
240*0e5f11aaSEric Miao 	struct input_dev *input_dev = platform_get_drvdata(pdev);
241*0e5f11aaSEric Miao 
242*0e5f11aaSEric Miao 	input_unregister_device(input_dev);
243*0e5f11aaSEric Miao 	free_irq(IRQ_KEYPAD, pdev);
244*0e5f11aaSEric Miao 	clk_put(pxa27x_keypad_clk);
245*0e5f11aaSEric Miao 	platform_set_drvdata(pdev, NULL);
246*0e5f11aaSEric Miao 
247*0e5f11aaSEric Miao 	return 0;
248*0e5f11aaSEric Miao }
249*0e5f11aaSEric Miao 
250*0e5f11aaSEric Miao static struct platform_driver pxa27x_keypad_driver = {
251*0e5f11aaSEric Miao 	.probe		= pxa27x_keypad_probe,
252*0e5f11aaSEric Miao 	.remove		= __devexit_p(pxa27x_keypad_remove),
253*0e5f11aaSEric Miao 	.suspend	= pxa27x_keypad_suspend,
254*0e5f11aaSEric Miao 	.resume		= pxa27x_keypad_resume,
255*0e5f11aaSEric Miao 	.driver		= {
256*0e5f11aaSEric Miao 		.name	= DRIVER_NAME,
257*0e5f11aaSEric Miao 	},
258*0e5f11aaSEric Miao };
259*0e5f11aaSEric Miao 
260*0e5f11aaSEric Miao static int __init pxa27x_keypad_init(void)
261*0e5f11aaSEric Miao {
262*0e5f11aaSEric Miao 	return platform_driver_register(&pxa27x_keypad_driver);
263*0e5f11aaSEric Miao }
264*0e5f11aaSEric Miao 
265*0e5f11aaSEric Miao static void __exit pxa27x_keypad_exit(void)
266*0e5f11aaSEric Miao {
267*0e5f11aaSEric Miao 	platform_driver_unregister(&pxa27x_keypad_driver);
268*0e5f11aaSEric Miao }
269*0e5f11aaSEric Miao 
270*0e5f11aaSEric Miao module_init(pxa27x_keypad_init);
271*0e5f11aaSEric Miao module_exit(pxa27x_keypad_exit);
272*0e5f11aaSEric Miao 
273*0e5f11aaSEric Miao MODULE_DESCRIPTION("PXA27x Keypad Controller Driver");
274*0e5f11aaSEric Miao MODULE_LICENSE("GPL");
275