1/*
2 * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
3 *
4 * Copyright 2005-2008 Analog Devices Inc.
5 *
6 * Licensed under the GPL-2 or later.
7 */
8
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/input.h>
12#include <linux/interrupt.h>
13#include <linux/i2c.h>
14#include <linux/slab.h>
15#include <linux/workqueue.h>
16
17#define DRV_NAME "pcf8574_keypad"
18
19static const unsigned char pcf8574_kp_btncode[] = {
20	[0] = KEY_RESERVED,
21	[1] = KEY_ENTER,
22	[2] = KEY_BACKSLASH,
23	[3] = KEY_0,
24	[4] = KEY_RIGHTBRACE,
25	[5] = KEY_C,
26	[6] = KEY_9,
27	[7] = KEY_8,
28	[8] = KEY_7,
29	[9] = KEY_B,
30	[10] = KEY_6,
31	[11] = KEY_5,
32	[12] = KEY_4,
33	[13] = KEY_A,
34	[14] = KEY_3,
35	[15] = KEY_2,
36	[16] = KEY_1
37};
38
39struct kp_data {
40	unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
41	struct input_dev *idev;
42	struct i2c_client *client;
43	char name[64];
44	char phys[32];
45	unsigned char laststate;
46};
47
48static short read_state(struct kp_data *lp)
49{
50	unsigned char x, y, a, b;
51
52	i2c_smbus_write_byte(lp->client, 240);
53	x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
54
55	i2c_smbus_write_byte(lp->client, 15);
56	y = 0xF & (~i2c_smbus_read_byte(lp->client));
57
58	for (a = 0; x > 0; a++)
59		x = x >> 1;
60	for (b = 0; y > 0; b++)
61		y = y >> 1;
62
63	return ((a - 1) * 4) + b;
64}
65
66static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
67{
68	struct kp_data *lp = dev_id;
69	unsigned char nextstate = read_state(lp);
70
71	if (lp->laststate != nextstate) {
72		int key_down = nextstate < ARRAY_SIZE(lp->btncode);
73		unsigned short keycode = key_down ?
74			lp->btncode[nextstate] : lp->btncode[lp->laststate];
75
76		input_report_key(lp->idev, keycode, key_down);
77		input_sync(lp->idev);
78
79		lp->laststate = nextstate;
80	}
81
82	return IRQ_HANDLED;
83}
84
85static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)
86{
87	int i, ret;
88	struct input_dev *idev;
89	struct kp_data *lp;
90
91	if (i2c_smbus_write_byte(client, 240) < 0) {
92		dev_err(&client->dev, "probe: write fail\n");
93		return -ENODEV;
94	}
95
96	lp = kzalloc(sizeof(*lp), GFP_KERNEL);
97	if (!lp)
98		return -ENOMEM;
99
100	idev = input_allocate_device();
101	if (!idev) {
102		dev_err(&client->dev, "Can't allocate input device\n");
103		ret = -ENOMEM;
104		goto fail_allocate;
105	}
106
107	lp->idev = idev;
108	lp->client = client;
109
110	idev->evbit[0] = BIT_MASK(EV_KEY);
111	idev->keycode = lp->btncode;
112	idev->keycodesize = sizeof(lp->btncode[0]);
113	idev->keycodemax = ARRAY_SIZE(lp->btncode);
114
115	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
116		lp->btncode[i] = pcf8574_kp_btncode[i];
117		__set_bit(lp->btncode[i] & KEY_MAX, idev->keybit);
118	}
119
120	sprintf(lp->name, DRV_NAME);
121	sprintf(lp->phys, "kp_data/input0");
122
123	idev->name = lp->name;
124	idev->phys = lp->phys;
125	idev->id.bustype = BUS_I2C;
126	idev->id.vendor = 0x0001;
127	idev->id.product = 0x0001;
128	idev->id.version = 0x0100;
129
130	lp->laststate = read_state(lp);
131
132	ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
133				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
134				   DRV_NAME, lp);
135	if (ret) {
136		dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
137		goto fail_free_device;
138	}
139
140	ret = input_register_device(idev);
141	if (ret) {
142		dev_err(&client->dev, "input_register_device() failed\n");
143		goto fail_free_irq;
144	}
145
146	i2c_set_clientdata(client, lp);
147	return 0;
148
149 fail_free_irq:
150	free_irq(client->irq, lp);
151 fail_free_device:
152	input_free_device(idev);
153 fail_allocate:
154	kfree(lp);
155
156	return ret;
157}
158
159static int __devexit pcf8574_kp_remove(struct i2c_client *client)
160{
161	struct kp_data *lp = i2c_get_clientdata(client);
162
163	free_irq(client->irq, lp);
164
165	input_unregister_device(lp->idev);
166	kfree(lp);
167
168	return 0;
169}
170
171#ifdef CONFIG_PM
172static int pcf8574_kp_resume(struct device *dev)
173{
174	struct i2c_client *client = to_i2c_client(dev);
175
176	enable_irq(client->irq);
177
178	return 0;
179}
180
181static int pcf8574_kp_suspend(struct device *dev)
182{
183	struct i2c_client *client = to_i2c_client(dev);
184
185	disable_irq(client->irq);
186
187	return 0;
188}
189
190static const struct dev_pm_ops pcf8574_kp_pm_ops = {
191	.suspend	= pcf8574_kp_suspend,
192	.resume		= pcf8574_kp_resume,
193};
194
195#else
196# define pcf8574_kp_resume  NULL
197# define pcf8574_kp_suspend NULL
198#endif
199
200static const struct i2c_device_id pcf8574_kp_id[] = {
201	{ DRV_NAME, 0 },
202	{ }
203};
204MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
205
206static struct i2c_driver pcf8574_kp_driver = {
207	.driver = {
208		.name  = DRV_NAME,
209		.owner = THIS_MODULE,
210#ifdef CONFIG_PM
211		.pm = &pcf8574_kp_pm_ops,
212#endif
213	},
214	.probe    = pcf8574_kp_probe,
215	.remove   = __devexit_p(pcf8574_kp_remove),
216	.id_table = pcf8574_kp_id,
217};
218
219module_i2c_driver(pcf8574_kp_driver);
220
221MODULE_AUTHOR("Michael Hennerich");
222MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
223MODULE_LICENSE("GPL");
224