]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
30ba3ead SG |
2 | /* |
3 | * Driver for keys on TCA6416 I2C IO expander | |
4 | * | |
5 | * Copyright (C) 2010 Texas Instruments | |
6 | * | |
7 | * Author : Sriramakrishnan.A.G. <srk@ti.com> | |
30ba3ead SG |
8 | */ |
9 | ||
10 | #include <linux/types.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/workqueue.h> | |
17 | #include <linux/gpio.h> | |
18 | #include <linux/i2c.h> | |
19 | #include <linux/input.h> | |
20 | #include <linux/tca6416_keypad.h> | |
21 | ||
22 | #define TCA6416_INPUT 0 | |
23 | #define TCA6416_OUTPUT 1 | |
24 | #define TCA6416_INVERT 2 | |
25 | #define TCA6416_DIRECTION 3 | |
26 | ||
27 | static const struct i2c_device_id tca6416_id[] = { | |
28 | { "tca6416-keys", 16, }, | |
b8a3d6bc | 29 | { "tca6408-keys", 8, }, |
30ba3ead SG |
30 | { } |
31 | }; | |
32 | MODULE_DEVICE_TABLE(i2c, tca6416_id); | |
33 | ||
34 | struct tca6416_drv_data { | |
35 | struct input_dev *input; | |
94bef5d5 | 36 | struct tca6416_button data[]; |
30ba3ead SG |
37 | }; |
38 | ||
39 | struct tca6416_keypad_chip { | |
40 | uint16_t reg_output; | |
41 | uint16_t reg_direction; | |
42 | uint16_t reg_input; | |
43 | ||
44 | struct i2c_client *client; | |
45 | struct input_dev *input; | |
46 | struct delayed_work dwork; | |
b8a3d6bc | 47 | int io_size; |
30ba3ead | 48 | int irqnum; |
b8a3d6bc | 49 | u16 pinmask; |
30ba3ead | 50 | bool use_polling; |
94bef5d5 | 51 | struct tca6416_button buttons[]; |
30ba3ead SG |
52 | }; |
53 | ||
54 | static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) | |
55 | { | |
56 | int error; | |
57 | ||
b8a3d6bc TS |
58 | error = chip->io_size > 8 ? |
59 | i2c_smbus_write_word_data(chip->client, reg << 1, val) : | |
60 | i2c_smbus_write_byte_data(chip->client, reg, val); | |
30ba3ead SG |
61 | if (error < 0) { |
62 | dev_err(&chip->client->dev, | |
63 | "%s failed, reg: %d, val: %d, error: %d\n", | |
64 | __func__, reg, val, error); | |
65 | return error; | |
66 | } | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) | |
72 | { | |
73 | int retval; | |
74 | ||
b8a3d6bc TS |
75 | retval = chip->io_size > 8 ? |
76 | i2c_smbus_read_word_data(chip->client, reg << 1) : | |
77 | i2c_smbus_read_byte_data(chip->client, reg); | |
30ba3ead SG |
78 | if (retval < 0) { |
79 | dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", | |
80 | __func__, reg, retval); | |
81 | return retval; | |
82 | } | |
83 | ||
84 | *val = (u16)retval; | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static void tca6416_keys_scan(struct tca6416_keypad_chip *chip) | |
89 | { | |
90 | struct input_dev *input = chip->input; | |
91 | u16 reg_val, val; | |
92 | int error, i, pin_index; | |
93 | ||
94 | error = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); | |
95 | if (error) | |
96 | return; | |
97 | ||
98 | reg_val &= chip->pinmask; | |
99 | ||
100 | /* Figure out which lines have changed */ | |
101 | val = reg_val ^ chip->reg_input; | |
102 | chip->reg_input = reg_val; | |
103 | ||
104 | for (i = 0, pin_index = 0; i < 16; i++) { | |
105 | if (val & (1 << i)) { | |
106 | struct tca6416_button *button = &chip->buttons[pin_index]; | |
107 | unsigned int type = button->type ?: EV_KEY; | |
108 | int state = ((reg_val & (1 << i)) ? 1 : 0) | |
109 | ^ button->active_low; | |
110 | ||
111 | input_event(input, type, button->code, !!state); | |
112 | input_sync(input); | |
113 | } | |
114 | ||
115 | if (chip->pinmask & (1 << i)) | |
116 | pin_index++; | |
117 | } | |
118 | } | |
119 | ||
120 | /* | |
121 | * This is threaded IRQ handler and this can (and will) sleep. | |
122 | */ | |
123 | static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) | |
124 | { | |
125 | struct tca6416_keypad_chip *chip = dev_id; | |
126 | ||
127 | tca6416_keys_scan(chip); | |
128 | ||
129 | return IRQ_HANDLED; | |
130 | } | |
131 | ||
132 | static void tca6416_keys_work_func(struct work_struct *work) | |
133 | { | |
134 | struct tca6416_keypad_chip *chip = | |
135 | container_of(work, struct tca6416_keypad_chip, dwork.work); | |
136 | ||
137 | tca6416_keys_scan(chip); | |
138 | schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); | |
139 | } | |
140 | ||
141 | static int tca6416_keys_open(struct input_dev *dev) | |
142 | { | |
143 | struct tca6416_keypad_chip *chip = input_get_drvdata(dev); | |
144 | ||
145 | /* Get initial device state in case it has switches */ | |
146 | tca6416_keys_scan(chip); | |
147 | ||
148 | if (chip->use_polling) | |
149 | schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); | |
150 | else | |
151 | enable_irq(chip->irqnum); | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | static void tca6416_keys_close(struct input_dev *dev) | |
157 | { | |
158 | struct tca6416_keypad_chip *chip = input_get_drvdata(dev); | |
159 | ||
160 | if (chip->use_polling) | |
161 | cancel_delayed_work_sync(&chip->dwork); | |
162 | else | |
163 | disable_irq(chip->irqnum); | |
164 | } | |
165 | ||
5298cc4c | 166 | static int tca6416_setup_registers(struct tca6416_keypad_chip *chip) |
30ba3ead SG |
167 | { |
168 | int error; | |
169 | ||
170 | error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); | |
171 | if (error) | |
172 | return error; | |
173 | ||
174 | error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); | |
175 | if (error) | |
176 | return error; | |
177 | ||
178 | /* ensure that keypad pins are set to input */ | |
179 | error = tca6416_write_reg(chip, TCA6416_DIRECTION, | |
180 | chip->reg_direction | chip->pinmask); | |
181 | if (error) | |
182 | return error; | |
183 | ||
184 | error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); | |
185 | if (error) | |
186 | return error; | |
187 | ||
188 | error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); | |
189 | if (error) | |
190 | return error; | |
191 | ||
192 | chip->reg_input &= chip->pinmask; | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
5298cc4c | 197 | static int tca6416_keypad_probe(struct i2c_client *client, |
30ba3ead SG |
198 | const struct i2c_device_id *id) |
199 | { | |
200 | struct tca6416_keys_platform_data *pdata; | |
201 | struct tca6416_keypad_chip *chip; | |
202 | struct input_dev *input; | |
203 | int error; | |
204 | int i; | |
205 | ||
206 | /* Check functionality */ | |
207 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { | |
208 | dev_err(&client->dev, "%s adapter not supported\n", | |
209 | dev_driver_string(&client->adapter->dev)); | |
210 | return -ENODEV; | |
211 | } | |
212 | ||
c838cb3d | 213 | pdata = dev_get_platdata(&client->dev); |
30ba3ead SG |
214 | if (!pdata) { |
215 | dev_dbg(&client->dev, "no platform data\n"); | |
216 | return -EINVAL; | |
217 | } | |
218 | ||
fb5fc09c | 219 | chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL); |
30ba3ead SG |
220 | input = input_allocate_device(); |
221 | if (!chip || !input) { | |
222 | error = -ENOMEM; | |
223 | goto fail1; | |
224 | } | |
225 | ||
226 | chip->client = client; | |
227 | chip->input = input; | |
b8a3d6bc | 228 | chip->io_size = id->driver_data; |
30ba3ead SG |
229 | chip->pinmask = pdata->pinmask; |
230 | chip->use_polling = pdata->use_polling; | |
231 | ||
232 | INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func); | |
233 | ||
234 | input->phys = "tca6416-keys/input0"; | |
235 | input->name = client->name; | |
236 | input->dev.parent = &client->dev; | |
237 | ||
238 | input->open = tca6416_keys_open; | |
239 | input->close = tca6416_keys_close; | |
240 | ||
241 | input->id.bustype = BUS_HOST; | |
242 | input->id.vendor = 0x0001; | |
243 | input->id.product = 0x0001; | |
244 | input->id.version = 0x0100; | |
245 | ||
246 | /* Enable auto repeat feature of Linux input subsystem */ | |
247 | if (pdata->rep) | |
248 | __set_bit(EV_REP, input->evbit); | |
249 | ||
250 | for (i = 0; i < pdata->nbuttons; i++) { | |
251 | unsigned int type; | |
252 | ||
253 | chip->buttons[i] = pdata->buttons[i]; | |
254 | type = (pdata->buttons[i].type) ?: EV_KEY; | |
255 | input_set_capability(input, type, pdata->buttons[i].code); | |
256 | } | |
257 | ||
258 | input_set_drvdata(input, chip); | |
259 | ||
260 | /* | |
261 | * Initialize cached registers from their original values. | |
262 | * we can't share this chip with another i2c master. | |
263 | */ | |
264 | error = tca6416_setup_registers(chip); | |
265 | if (error) | |
266 | goto fail1; | |
267 | ||
268 | if (!chip->use_polling) { | |
269 | if (pdata->irq_is_gpio) | |
270 | chip->irqnum = gpio_to_irq(client->irq); | |
271 | else | |
272 | chip->irqnum = client->irq; | |
273 | ||
274 | error = request_threaded_irq(chip->irqnum, NULL, | |
275 | tca6416_keys_isr, | |
9b7e31bb | 276 | IRQF_TRIGGER_FALLING | |
bcd9730a | 277 | IRQF_ONESHOT | IRQF_NO_AUTOEN, |
30ba3ead SG |
278 | "tca6416-keypad", chip); |
279 | if (error) { | |
280 | dev_dbg(&client->dev, | |
281 | "Unable to claim irq %d; error %d\n", | |
282 | chip->irqnum, error); | |
283 | goto fail1; | |
284 | } | |
30ba3ead SG |
285 | } |
286 | ||
287 | error = input_register_device(input); | |
288 | if (error) { | |
289 | dev_dbg(&client->dev, | |
290 | "Unable to register input device, error: %d\n", error); | |
291 | goto fail2; | |
292 | } | |
293 | ||
294 | i2c_set_clientdata(client, chip); | |
64dcddd8 | 295 | device_init_wakeup(&client->dev, 1); |
30ba3ead SG |
296 | |
297 | return 0; | |
298 | ||
299 | fail2: | |
300 | if (!chip->use_polling) { | |
301 | free_irq(chip->irqnum, chip); | |
302 | enable_irq(chip->irqnum); | |
303 | } | |
304 | fail1: | |
305 | input_free_device(input); | |
306 | kfree(chip); | |
307 | return error; | |
308 | } | |
309 | ||
e2619cf7 | 310 | static int tca6416_keypad_remove(struct i2c_client *client) |
30ba3ead SG |
311 | { |
312 | struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); | |
313 | ||
314 | if (!chip->use_polling) { | |
315 | free_irq(chip->irqnum, chip); | |
316 | enable_irq(chip->irqnum); | |
317 | } | |
318 | ||
319 | input_unregister_device(chip->input); | |
320 | kfree(chip); | |
321 | ||
30ba3ead SG |
322 | return 0; |
323 | } | |
324 | ||
64dcddd8 MD |
325 | #ifdef CONFIG_PM_SLEEP |
326 | static int tca6416_keypad_suspend(struct device *dev) | |
327 | { | |
328 | struct i2c_client *client = to_i2c_client(dev); | |
329 | struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); | |
330 | ||
331 | if (device_may_wakeup(dev)) | |
332 | enable_irq_wake(chip->irqnum); | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | static int tca6416_keypad_resume(struct device *dev) | |
338 | { | |
339 | struct i2c_client *client = to_i2c_client(dev); | |
340 | struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); | |
341 | ||
342 | if (device_may_wakeup(dev)) | |
343 | disable_irq_wake(chip->irqnum); | |
344 | ||
345 | return 0; | |
346 | } | |
347 | #endif | |
348 | ||
349 | static SIMPLE_DEV_PM_OPS(tca6416_keypad_dev_pm_ops, | |
350 | tca6416_keypad_suspend, tca6416_keypad_resume); | |
30ba3ead SG |
351 | |
352 | static struct i2c_driver tca6416_keypad_driver = { | |
353 | .driver = { | |
354 | .name = "tca6416-keypad", | |
64dcddd8 | 355 | .pm = &tca6416_keypad_dev_pm_ops, |
30ba3ead SG |
356 | }, |
357 | .probe = tca6416_keypad_probe, | |
1cb0aa88 | 358 | .remove = tca6416_keypad_remove, |
30ba3ead SG |
359 | .id_table = tca6416_id, |
360 | }; | |
361 | ||
362 | static int __init tca6416_keypad_init(void) | |
363 | { | |
364 | return i2c_add_driver(&tca6416_keypad_driver); | |
365 | } | |
366 | ||
367 | subsys_initcall(tca6416_keypad_init); | |
368 | ||
369 | static void __exit tca6416_keypad_exit(void) | |
370 | { | |
371 | i2c_del_driver(&tca6416_keypad_driver); | |
372 | } | |
373 | module_exit(tca6416_keypad_exit); | |
374 | ||
375 | MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>"); | |
c154703b | 376 | MODULE_DESCRIPTION("Keypad driver over tca6416 IO expander"); |
30ba3ead | 377 | MODULE_LICENSE("GPL"); |