xref: /openbmc/linux/drivers/input/misc/pcf8574_keypad.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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  
17  static 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  
37  struct 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  
read_state(struct kp_data * lp)46  static 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  
pcf8574_kp_irq_handler(int irq,void * dev_id)64  static 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  
pcf8574_kp_probe(struct i2c_client * client)83  static 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  
pcf8574_kp_remove(struct i2c_client * client)160  static 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  
pcf8574_kp_resume(struct device * dev)170  static 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  
pcf8574_kp_suspend(struct device * dev)179  static 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  
188  static DEFINE_SIMPLE_DEV_PM_OPS(pcf8574_kp_pm_ops,
189  				pcf8574_kp_suspend, pcf8574_kp_resume);
190  
191  static const struct i2c_device_id pcf8574_kp_id[] = {
192  	{ DRV_NAME, 0 },
193  	{ }
194  };
195  MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
196  
197  static 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  
207  module_i2c_driver(pcf8574_kp_driver);
208  
209  MODULE_AUTHOR("Michael Hennerich");
210  MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
211  MODULE_LICENSE("GPL");
212