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