180503b23SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b91c4be7SBryan Wu /*
3b91c4be7SBryan Wu  * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
4b91c4be7SBryan Wu  *
5b91c4be7SBryan Wu  * Copyright 2005-2008 Analog Devices Inc.
6b91c4be7SBryan Wu  */
7b91c4be7SBryan Wu 
8b91c4be7SBryan Wu #include <linux/module.h>
9b91c4be7SBryan Wu #include <linux/input.h>
10b91c4be7SBryan Wu #include <linux/interrupt.h>
11b91c4be7SBryan Wu #include <linux/i2c.h>
12b91c4be7SBryan Wu #include <linux/slab.h>
13b91c4be7SBryan Wu #include <linux/workqueue.h>
14b91c4be7SBryan Wu 
15b91c4be7SBryan Wu #define DRV_NAME "pcf8574_keypad"
16b91c4be7SBryan Wu 
17b91c4be7SBryan Wu static const unsigned char pcf8574_kp_btncode[] = {
18b91c4be7SBryan Wu 	[0] = KEY_RESERVED,
19b91c4be7SBryan Wu 	[1] = KEY_ENTER,
20b91c4be7SBryan Wu 	[2] = KEY_BACKSLASH,
21b91c4be7SBryan Wu 	[3] = KEY_0,
22b91c4be7SBryan Wu 	[4] = KEY_RIGHTBRACE,
23b91c4be7SBryan Wu 	[5] = KEY_C,
24b91c4be7SBryan Wu 	[6] = KEY_9,
25b91c4be7SBryan Wu 	[7] = KEY_8,
26b91c4be7SBryan Wu 	[8] = KEY_7,
27b91c4be7SBryan Wu 	[9] = KEY_B,
28b91c4be7SBryan Wu 	[10] = KEY_6,
29b91c4be7SBryan Wu 	[11] = KEY_5,
30b91c4be7SBryan Wu 	[12] = KEY_4,
31b91c4be7SBryan Wu 	[13] = KEY_A,
32b91c4be7SBryan Wu 	[14] = KEY_3,
33b91c4be7SBryan Wu 	[15] = KEY_2,
34b91c4be7SBryan Wu 	[16] = KEY_1
35b91c4be7SBryan Wu };
36b91c4be7SBryan Wu 
37b91c4be7SBryan Wu struct kp_data {
38b91c4be7SBryan Wu 	unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
39b91c4be7SBryan Wu 	struct input_dev *idev;
40b91c4be7SBryan Wu 	struct i2c_client *client;
41b91c4be7SBryan Wu 	char name[64];
42b91c4be7SBryan Wu 	char phys[32];
43b91c4be7SBryan Wu 	unsigned char laststate;
44b91c4be7SBryan Wu };
45b91c4be7SBryan Wu 
read_state(struct kp_data * lp)46b91c4be7SBryan Wu static short read_state(struct kp_data *lp)
47b91c4be7SBryan Wu {
48b91c4be7SBryan Wu 	unsigned char x, y, a, b;
49b91c4be7SBryan Wu 
50b91c4be7SBryan Wu 	i2c_smbus_write_byte(lp->client, 240);
51b91c4be7SBryan Wu 	x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
52b91c4be7SBryan Wu 
53b91c4be7SBryan Wu 	i2c_smbus_write_byte(lp->client, 15);
54b91c4be7SBryan Wu 	y = 0xF & (~i2c_smbus_read_byte(lp->client));
55b91c4be7SBryan Wu 
56b91c4be7SBryan Wu 	for (a = 0; x > 0; a++)
57b91c4be7SBryan Wu 		x = x >> 1;
58b91c4be7SBryan Wu 	for (b = 0; y > 0; b++)
59b91c4be7SBryan Wu 		y = y >> 1;
60b91c4be7SBryan Wu 
61b91c4be7SBryan Wu 	return ((a - 1) * 4) + b;
62b91c4be7SBryan Wu }
63b91c4be7SBryan Wu 
pcf8574_kp_irq_handler(int irq,void * dev_id)64b91c4be7SBryan Wu static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
65b91c4be7SBryan Wu {
66b91c4be7SBryan Wu 	struct kp_data *lp = dev_id;
67b91c4be7SBryan Wu 	unsigned char nextstate = read_state(lp);
68b91c4be7SBryan Wu 
69b91c4be7SBryan Wu 	if (lp->laststate != nextstate) {
700b75f775SDan Carpenter 		int key_down = nextstate < ARRAY_SIZE(lp->btncode);
71b91c4be7SBryan Wu 		unsigned short keycode = key_down ?
72b91c4be7SBryan Wu 			lp->btncode[nextstate] : lp->btncode[lp->laststate];
73b91c4be7SBryan Wu 
74b91c4be7SBryan Wu 		input_report_key(lp->idev, keycode, key_down);
75b91c4be7SBryan Wu 		input_sync(lp->idev);
76b91c4be7SBryan Wu 
77b91c4be7SBryan Wu 		lp->laststate = nextstate;
78b91c4be7SBryan Wu 	}
79b91c4be7SBryan Wu 
80b91c4be7SBryan Wu 	return IRQ_HANDLED;
81b91c4be7SBryan Wu }
82b91c4be7SBryan Wu 
pcf8574_kp_probe(struct i2c_client * client)83f8630ccdSUwe Kleine-König static int pcf8574_kp_probe(struct i2c_client *client)
84b91c4be7SBryan Wu {
85b91c4be7SBryan Wu 	int i, ret;
86b91c4be7SBryan Wu 	struct input_dev *idev;
87b91c4be7SBryan Wu 	struct kp_data *lp;
88b91c4be7SBryan Wu 
89b91c4be7SBryan Wu 	if (i2c_smbus_write_byte(client, 240) < 0) {
90b91c4be7SBryan Wu 		dev_err(&client->dev, "probe: write fail\n");
91b91c4be7SBryan Wu 		return -ENODEV;
92b91c4be7SBryan Wu 	}
93b91c4be7SBryan Wu 
94b91c4be7SBryan Wu 	lp = kzalloc(sizeof(*lp), GFP_KERNEL);
95b91c4be7SBryan Wu 	if (!lp)
96b91c4be7SBryan Wu 		return -ENOMEM;
97b91c4be7SBryan Wu 
98b91c4be7SBryan Wu 	idev = input_allocate_device();
99b91c4be7SBryan Wu 	if (!idev) {
100b91c4be7SBryan Wu 		dev_err(&client->dev, "Can't allocate input device\n");
101b91c4be7SBryan Wu 		ret = -ENOMEM;
102b91c4be7SBryan Wu 		goto fail_allocate;
103b91c4be7SBryan Wu 	}
104b91c4be7SBryan Wu 
105b91c4be7SBryan Wu 	lp->idev = idev;
106b91c4be7SBryan Wu 	lp->client = client;
107b91c4be7SBryan Wu 
108b91c4be7SBryan Wu 	idev->evbit[0] = BIT_MASK(EV_KEY);
109b91c4be7SBryan Wu 	idev->keycode = lp->btncode;
110b91c4be7SBryan Wu 	idev->keycodesize = sizeof(lp->btncode[0]);
111b91c4be7SBryan Wu 	idev->keycodemax = ARRAY_SIZE(lp->btncode);
112b91c4be7SBryan Wu 
113b91c4be7SBryan Wu 	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
114e4cfb034SAndrew Liu 		if (lp->btncode[i] <= KEY_MAX) {
115b91c4be7SBryan Wu 			lp->btncode[i] = pcf8574_kp_btncode[i];
116e4cfb034SAndrew Liu 			__set_bit(lp->btncode[i], idev->keybit);
117b91c4be7SBryan Wu 		}
118e4cfb034SAndrew Liu 	}
119e4cfb034SAndrew Liu 	__clear_bit(KEY_RESERVED, idev->keybit);
120b91c4be7SBryan Wu 
121b91c4be7SBryan Wu 	sprintf(lp->name, DRV_NAME);
122b91c4be7SBryan Wu 	sprintf(lp->phys, "kp_data/input0");
123b91c4be7SBryan Wu 
124b91c4be7SBryan Wu 	idev->name = lp->name;
125b91c4be7SBryan Wu 	idev->phys = lp->phys;
126b91c4be7SBryan Wu 	idev->id.bustype = BUS_I2C;
127b91c4be7SBryan Wu 	idev->id.vendor = 0x0001;
128b91c4be7SBryan Wu 	idev->id.product = 0x0001;
129b91c4be7SBryan Wu 	idev->id.version = 0x0100;
130b91c4be7SBryan Wu 
131b91c4be7SBryan Wu 	lp->laststate = read_state(lp);
132b91c4be7SBryan Wu 
133b91c4be7SBryan Wu 	ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
134b91c4be7SBryan Wu 				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
135b91c4be7SBryan Wu 				   DRV_NAME, lp);
136b91c4be7SBryan Wu 	if (ret) {
137b91c4be7SBryan Wu 		dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
13817d01f28SDan Carpenter 		goto fail_free_device;
13917d01f28SDan Carpenter 	}
14017d01f28SDan Carpenter 
14117d01f28SDan Carpenter 	ret = input_register_device(idev);
14217d01f28SDan Carpenter 	if (ret) {
14317d01f28SDan Carpenter 		dev_err(&client->dev, "input_register_device() failed\n");
14417d01f28SDan Carpenter 		goto fail_free_irq;
145b91c4be7SBryan Wu 	}
146b91c4be7SBryan Wu 
147b91c4be7SBryan Wu 	i2c_set_clientdata(client, lp);
148b91c4be7SBryan Wu 	return 0;
149b91c4be7SBryan Wu 
15017d01f28SDan Carpenter  fail_free_irq:
15117d01f28SDan Carpenter 	free_irq(client->irq, lp);
15217d01f28SDan Carpenter  fail_free_device:
153b91c4be7SBryan Wu 	input_free_device(idev);
154b91c4be7SBryan Wu  fail_allocate:
155b91c4be7SBryan Wu 	kfree(lp);
156b91c4be7SBryan Wu 
157b91c4be7SBryan Wu 	return ret;
158b91c4be7SBryan Wu }
159b91c4be7SBryan Wu 
pcf8574_kp_remove(struct i2c_client * client)160ed5c2f5fSUwe Kleine-König static void pcf8574_kp_remove(struct i2c_client *client)
161b91c4be7SBryan Wu {
162b91c4be7SBryan Wu 	struct kp_data *lp = i2c_get_clientdata(client);
163b91c4be7SBryan Wu 
164b91c4be7SBryan Wu 	free_irq(client->irq, lp);
165b91c4be7SBryan Wu 
166b91c4be7SBryan Wu 	input_unregister_device(lp->idev);
167b91c4be7SBryan Wu 	kfree(lp);
168b91c4be7SBryan Wu }
169b91c4be7SBryan Wu 
pcf8574_kp_resume(struct device * dev)17010ee2dedSDmitry Torokhov static int pcf8574_kp_resume(struct device *dev)
171b91c4be7SBryan Wu {
17210ee2dedSDmitry Torokhov 	struct i2c_client *client = to_i2c_client(dev);
17310ee2dedSDmitry Torokhov 
174b91c4be7SBryan Wu 	enable_irq(client->irq);
175b91c4be7SBryan Wu 
176b91c4be7SBryan Wu 	return 0;
177b91c4be7SBryan Wu }
178b91c4be7SBryan Wu 
pcf8574_kp_suspend(struct device * dev)17910ee2dedSDmitry Torokhov static int pcf8574_kp_suspend(struct device *dev)
180b91c4be7SBryan Wu {
18110ee2dedSDmitry Torokhov 	struct i2c_client *client = to_i2c_client(dev);
18210ee2dedSDmitry Torokhov 
183b91c4be7SBryan Wu 	disable_irq(client->irq);
184b91c4be7SBryan Wu 
185b91c4be7SBryan Wu 	return 0;
186b91c4be7SBryan Wu }
18710ee2dedSDmitry Torokhov 
18822db998aSJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(pcf8574_kp_pm_ops,
18922db998aSJonathan Cameron 				pcf8574_kp_suspend, pcf8574_kp_resume);
190b91c4be7SBryan Wu 
191b91c4be7SBryan Wu static const struct i2c_device_id pcf8574_kp_id[] = {
192b91c4be7SBryan Wu 	{ DRV_NAME, 0 },
193b91c4be7SBryan Wu 	{ }
194b91c4be7SBryan Wu };
195b91c4be7SBryan Wu MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
196b91c4be7SBryan Wu 
197b91c4be7SBryan Wu static struct i2c_driver pcf8574_kp_driver = {
198b91c4be7SBryan Wu 	.driver = {
199b91c4be7SBryan Wu 		.name  = DRV_NAME,
20022db998aSJonathan Cameron 		.pm = pm_sleep_ptr(&pcf8574_kp_pm_ops),
201b91c4be7SBryan Wu 	},
202*d8bde56dSUwe Kleine-König 	.probe    = pcf8574_kp_probe,
2031cb0aa88SBill Pemberton 	.remove   = pcf8574_kp_remove,
204b91c4be7SBryan Wu 	.id_table = pcf8574_kp_id,
205b91c4be7SBryan Wu };
206b91c4be7SBryan Wu 
2071b92c1cfSAxel Lin module_i2c_driver(pcf8574_kp_driver);
208b91c4be7SBryan Wu 
209b91c4be7SBryan Wu MODULE_AUTHOR("Michael Hennerich");
210b91c4be7SBryan Wu MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
211b91c4be7SBryan Wu MODULE_LICENSE("GPL");
212