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