PageRenderTime 40ms CodeModel.GetById 29ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/input/misc/pcf8574_keypad.c

https://bitbucket.org/ndreys/linux-sunxi
C | 233 lines | 180 code | 46 blank | 7 comment | 10 complexity | 2fcf89e69016b6faf4c81f77f55a0cf7 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  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
219static int __init pcf8574_kp_init(void)
220{
221	return i2c_add_driver(&pcf8574_kp_driver);
222}
223module_init(pcf8574_kp_init);
224
225static void __exit pcf8574_kp_exit(void)
226{
227	i2c_del_driver(&pcf8574_kp_driver);
228}
229module_exit(pcf8574_kp_exit);
230
231MODULE_AUTHOR("Michael Hennerich");
232MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
233MODULE_LICENSE("GPL");