pxa27x_keypad.c 6.4 KB
Newer Older
1
/*
2
 * linux/drivers/input/keyboard/pxa27x_keypad.c
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * Driver for the pxa27x matrix keyboard controller.
 *
 * Created:	Feb 22, 2007
 * Author:	Rodolfo Giometti <giometti@linux.it>
 *
 * Based on a previous implementations by Kevin O'Connor
 * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and
 * on some suggestions by Nicolas Pitre <nico@cam.org>.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/platform_device.h>
26 27
#include <linux/clk.h>
#include <linux/err.h>
28 29 30 31 32 33 34 35

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>

#include <asm/arch/hardware.h>
#include <asm/arch/pxa-regs.h>
#include <asm/arch/irqs.h>
36
#include <asm/arch/pxa27x_keypad.h>
37

38
#define DRIVER_NAME		"pxa27x-keypad"
39 40 41 42 43 44

#define KPASMKP(col)		(col/2 == 0 ? KPASMKP0 : \
				 col/2 == 1 ? KPASMKP1 : \
				 col/2 == 2 ? KPASMKP2 : KPASMKP3)
#define KPASMKPx_MKC(row, col)	(1 << (row + 16 * (col % 2)))

45
static struct clk *pxa27x_keypad_clk;
46

47
static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
48 49
{
	struct platform_device *pdev = dev_id;
50
	struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
	struct input_dev *input_dev = platform_get_drvdata(pdev);
	unsigned long kpc = KPC;
	int p, row, col, rel;

	if (kpc & KPC_DI) {
		unsigned long kpdk = KPDK;

		if (!(kpdk & KPDK_DKP)) {
			/* better luck next time */
		} else if (kpc & KPC_REE0) {
			unsigned long kprec = KPREC;
			KPREC = 0x7f;

			if (kprec & KPREC_OF0)
				rel = (kprec & 0xff) + 0x7f;
			else if (kprec & KPREC_UF0)
				rel = (kprec & 0xff) - 0x7f - 0xff;
			else
				rel = (kprec & 0xff) - 0x7f;

			if (rel) {
				input_report_rel(input_dev, REL_WHEEL, rel);
				input_sync(input_dev);
			}
		}
	}

	if (kpc & KPC_MI) {
		/* report the status of every button */
		for (row = 0; row < pdata->nr_rows; row++) {
			for (col = 0; col < pdata->nr_cols; col++) {
				p = KPASMKP(col) & KPASMKPx_MKC(row, col) ?
					1 : 0;
				pr_debug("keycode %x - pressed %x\n",
						pdata->keycodes[row][col], p);
				input_report_key(input_dev,
						pdata->keycodes[row][col], p);
			}
		}
		input_sync(input_dev);
	}

	return IRQ_HANDLED;
}

96
static int pxa27x_keypad_open(struct input_dev *dev)
97 98 99 100 101 102 103 104 105 106 107 108 109 110
{
	/* Set keypad control register */
	KPC |= (KPC_ASACT |
		KPC_MS_ALL |
		(2 << 6) | KPC_REE0 | KPC_DK_DEB_SEL |
		KPC_ME | KPC_MIE | KPC_DE | KPC_DIE);

	KPC &= ~KPC_AS;         /* disable automatic scan */
	KPC &= ~KPC_IMKP;       /* do not ignore multiple keypresses */

	/* Set rotary count to mid-point value */
	KPREC = 0x7F;

	/* Enable unit clock */
111
	clk_enable(pxa27x_keypad_clk);
112 113 114 115

	return 0;
}

116
static void pxa27x_keypad_close(struct input_dev *dev)
117 118
{
	/* Disable clock unit */
119
	clk_disable(pxa27x_keypad_clk);
120 121 122
}

#ifdef CONFIG_PM
123
static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state)
124
{
125
	struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
126 127 128 129 130 131 132 133

	/* Save controller status */
	pdata->reg_kpc = KPC;
	pdata->reg_kprec = KPREC;

	return 0;
}

134
static int pxa27x_keypad_resume(struct platform_device *pdev)
135
{
136
	struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
137 138 139 140 141 142 143 144 145 146
	struct input_dev *input_dev = platform_get_drvdata(pdev);

	mutex_lock(&input_dev->mutex);

	if (input_dev->users) {
		/* Restore controller status */
		KPC = pdata->reg_kpc;
		KPREC = pdata->reg_kprec;

		/* Enable unit clock */
147 148
		clk_disable(pxa27x_keypad_clk);
		clk_enable(pxa27x_keypad_clk);
149 150 151 152 153 154 155
	}

	mutex_unlock(&input_dev->mutex);

	return 0;
}
#else
156 157
#define pxa27x_keypad_suspend	NULL
#define pxa27x_keypad_resume	NULL
158 159
#endif

160
static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
161
{
162
	struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
163 164 165
	struct input_dev *input_dev;
	int i, row, col, error;

166 167 168
	pxa27x_keypad_clk = clk_get(&pdev->dev, "KBDCLK");
	if (IS_ERR(pxa27x_keypad_clk)) {
		error = PTR_ERR(pxa27x_keypad_clk);
169 170 171
		goto err_clk;
	}

172 173 174 175
	/* Create and register the input driver. */
	input_dev = input_allocate_device();
	if (!input_dev) {
		printk(KERN_ERR "Cannot request keypad device\n");
176 177
		error = -ENOMEM;
		goto err_alloc;
178 179 180 181
	}

	input_dev->name = DRIVER_NAME;
	input_dev->id.bustype = BUS_HOST;
182 183
	input_dev->open = pxa27x_keypad_open;
	input_dev->close = pxa27x_keypad_close;
184
	input_dev->dev.parent = &pdev->dev;
185

186 187 188
	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
		BIT_MASK(EV_REL);
	input_dev->relbit[BIT_WORD(REL_WHEEL)] = BIT_MASK(REL_WHEEL);
189 190 191 192 193 194 195 196
	for (row = 0; row < pdata->nr_rows; row++) {
		for (col = 0; col < pdata->nr_cols; col++) {
			int code = pdata->keycodes[row][col];
			if (code > 0)
				set_bit(code, input_dev->keybit);
		}
	}

197
	error = request_irq(IRQ_KEYPAD, pxa27x_keypad_irq_handler, IRQF_DISABLED,
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
			    DRIVER_NAME, pdev);
	if (error) {
		printk(KERN_ERR "Cannot request keypad IRQ\n");
		goto err_free_dev;
	}

	platform_set_drvdata(pdev, input_dev);

	/* Register the input device */
	error = input_register_device(input_dev);
	if (error)
		goto err_free_irq;

	/*
	 * Store rows/cols info into keyboard registers.
	 */

	KPC |= (pdata->nr_rows - 1) << 26;
	KPC |= (pdata->nr_cols - 1) << 23;

	for (col = 0; col < pdata->nr_cols; col++)
		KPC |= KPC_MS0 << col;

	return 0;

 err_free_irq:
	platform_set_drvdata(pdev, NULL);
	free_irq(IRQ_KEYPAD, pdev);
 err_free_dev:
	input_free_device(input_dev);
228
 err_alloc:
229
	clk_put(pxa27x_keypad_clk);
230
 err_clk:
231 232 233
	return error;
}

234
static int __devexit pxa27x_keypad_remove(struct platform_device *pdev)
235 236 237 238 239
{
	struct input_dev *input_dev = platform_get_drvdata(pdev);

	input_unregister_device(input_dev);
	free_irq(IRQ_KEYPAD, pdev);
240
	clk_put(pxa27x_keypad_clk);
241 242 243 244 245
	platform_set_drvdata(pdev, NULL);

	return 0;
}

246 247 248 249 250
static struct platform_driver pxa27x_keypad_driver = {
	.probe		= pxa27x_keypad_probe,
	.remove		= __devexit_p(pxa27x_keypad_remove),
	.suspend	= pxa27x_keypad_suspend,
	.resume		= pxa27x_keypad_resume,
251 252 253 254 255
	.driver		= {
		.name	= DRIVER_NAME,
	},
};

256
static int __init pxa27x_keypad_init(void)
257
{
258
	return platform_driver_register(&pxa27x_keypad_driver);
259 260
}

261
static void __exit pxa27x_keypad_exit(void)
262
{
263
	platform_driver_unregister(&pxa27x_keypad_driver);
264 265
}

266 267
module_init(pxa27x_keypad_init);
module_exit(pxa27x_keypad_exit);
268

269
MODULE_DESCRIPTION("PXA27x Keypad Controller Driver");
270
MODULE_LICENSE("GPL");