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> | |
30ba3ead SG |
17 | #include <linux/i2c.h> |
18 | #include <linux/input.h> | |
19 | #include <linux/tca6416_keypad.h> | |
20 | ||
21 | #define TCA6416_INPUT 0 | |
22 | #define TCA6416_OUTPUT 1 | |
23 | #define TCA6416_INVERT 2 | |
24 | #define TCA6416_DIRECTION 3 | |
25 | ||
57b0c96f DT |
26 | #define TCA6416_POLL_INTERVAL 100 /* msec */ |
27 | ||
30ba3ead SG |
28 | static const struct i2c_device_id tca6416_id[] = { |
29 | { "tca6416-keys", 16, }, | |
b8a3d6bc | 30 | { "tca6408-keys", 8, }, |
30ba3ead SG |
31 | { } |
32 | }; | |
33 | MODULE_DEVICE_TABLE(i2c, tca6416_id); | |
34 | ||
30ba3ead SG |
35 | struct tca6416_keypad_chip { |
36 | uint16_t reg_output; | |
37 | uint16_t reg_direction; | |
38 | uint16_t reg_input; | |
39 | ||
40 | struct i2c_client *client; | |
41 | struct input_dev *input; | |
b8a3d6bc | 42 | int io_size; |
b8a3d6bc | 43 | u16 pinmask; |
30ba3ead | 44 | bool use_polling; |
94bef5d5 | 45 | struct tca6416_button buttons[]; |
30ba3ead SG |
46 | }; |
47 | ||
48 | static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) | |
49 | { | |
50 | int error; | |
51 | ||
b8a3d6bc TS |
52 | error = chip->io_size > 8 ? |
53 | i2c_smbus_write_word_data(chip->client, reg << 1, val) : | |
54 | i2c_smbus_write_byte_data(chip->client, reg, val); | |
30ba3ead SG |
55 | if (error < 0) { |
56 | dev_err(&chip->client->dev, | |
57 | "%s failed, reg: %d, val: %d, error: %d\n", | |
58 | __func__, reg, val, error); | |
59 | return error; | |
60 | } | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
65 | static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) | |
66 | { | |
67 | int retval; | |
68 | ||
b8a3d6bc TS |
69 | retval = chip->io_size > 8 ? |
70 | i2c_smbus_read_word_data(chip->client, reg << 1) : | |
71 | i2c_smbus_read_byte_data(chip->client, reg); | |
30ba3ead SG |
72 | if (retval < 0) { |
73 | dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", | |
74 | __func__, reg, retval); | |
75 | return retval; | |
76 | } | |
77 | ||
78 | *val = (u16)retval; | |
79 | return 0; | |
80 | } | |
81 | ||
57b0c96f | 82 | static void tca6416_keys_scan(struct input_dev *input) |
30ba3ead | 83 | { |
57b0c96f | 84 | struct tca6416_keypad_chip *chip = input_get_drvdata(input); |
30ba3ead SG |
85 | u16 reg_val, val; |
86 | int error, i, pin_index; | |
87 | ||
88 | error = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); | |
89 | if (error) | |
90 | return; | |
91 | ||
92 | reg_val &= chip->pinmask; | |
93 | ||
94 | /* Figure out which lines have changed */ | |
95 | val = reg_val ^ chip->reg_input; | |
96 | chip->reg_input = reg_val; | |
97 | ||
98 | for (i = 0, pin_index = 0; i < 16; i++) { | |
99 | if (val & (1 << i)) { | |
100 | struct tca6416_button *button = &chip->buttons[pin_index]; | |
101 | unsigned int type = button->type ?: EV_KEY; | |
102 | int state = ((reg_val & (1 << i)) ? 1 : 0) | |
103 | ^ button->active_low; | |
104 | ||
105 | input_event(input, type, button->code, !!state); | |
106 | input_sync(input); | |
107 | } | |
108 | ||
109 | if (chip->pinmask & (1 << i)) | |
110 | pin_index++; | |
111 | } | |
112 | } | |
113 | ||
114 | /* | |
115 | * This is threaded IRQ handler and this can (and will) sleep. | |
116 | */ | |
117 | static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) | |
118 | { | |
57b0c96f | 119 | tca6416_keys_scan(dev_id); |
30ba3ead SG |
120 | |
121 | return IRQ_HANDLED; | |
122 | } | |
123 | ||
30ba3ead SG |
124 | static int tca6416_keys_open(struct input_dev *dev) |
125 | { | |
126 | struct tca6416_keypad_chip *chip = input_get_drvdata(dev); | |
127 | ||
57b0c96f DT |
128 | if (!chip->use_polling) { |
129 | /* Get initial device state in case it has switches */ | |
130 | tca6416_keys_scan(dev); | |
687fe7df | 131 | enable_irq(chip->client->irq); |
57b0c96f | 132 | } |
30ba3ead SG |
133 | |
134 | return 0; | |
135 | } | |
136 | ||
137 | static void tca6416_keys_close(struct input_dev *dev) | |
138 | { | |
139 | struct tca6416_keypad_chip *chip = input_get_drvdata(dev); | |
140 | ||
57b0c96f | 141 | if (!chip->use_polling) |
687fe7df | 142 | disable_irq(chip->client->irq); |
30ba3ead SG |
143 | } |
144 | ||
5298cc4c | 145 | static int tca6416_setup_registers(struct tca6416_keypad_chip *chip) |
30ba3ead SG |
146 | { |
147 | int error; | |
148 | ||
149 | error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); | |
150 | if (error) | |
151 | return error; | |
152 | ||
153 | error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); | |
154 | if (error) | |
155 | return error; | |
156 | ||
157 | /* ensure that keypad pins are set to input */ | |
158 | error = tca6416_write_reg(chip, TCA6416_DIRECTION, | |
159 | chip->reg_direction | chip->pinmask); | |
160 | if (error) | |
161 | return error; | |
162 | ||
163 | error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); | |
164 | if (error) | |
165 | return error; | |
166 | ||
167 | error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); | |
168 | if (error) | |
169 | return error; | |
170 | ||
171 | chip->reg_input &= chip->pinmask; | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
3da11976 | 176 | static int tca6416_keypad_probe(struct i2c_client *client) |
30ba3ead | 177 | { |
3da11976 | 178 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
30ba3ead SG |
179 | struct tca6416_keys_platform_data *pdata; |
180 | struct tca6416_keypad_chip *chip; | |
181 | struct input_dev *input; | |
182 | int error; | |
183 | int i; | |
184 | ||
185 | /* Check functionality */ | |
186 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { | |
187 | dev_err(&client->dev, "%s adapter not supported\n", | |
188 | dev_driver_string(&client->adapter->dev)); | |
189 | return -ENODEV; | |
190 | } | |
191 | ||
c838cb3d | 192 | pdata = dev_get_platdata(&client->dev); |
30ba3ead SG |
193 | if (!pdata) { |
194 | dev_dbg(&client->dev, "no platform data\n"); | |
195 | return -EINVAL; | |
196 | } | |
197 | ||
91a4c690 YL |
198 | chip = devm_kzalloc(&client->dev, |
199 | struct_size(chip, buttons, pdata->nbuttons), | |
200 | GFP_KERNEL); | |
201 | if (!chip) | |
202 | return -ENOMEM; | |
203 | ||
204 | input = devm_input_allocate_device(&client->dev); | |
205 | if (!input) | |
206 | return -ENOMEM; | |
30ba3ead SG |
207 | |
208 | chip->client = client; | |
209 | chip->input = input; | |
b8a3d6bc | 210 | chip->io_size = id->driver_data; |
30ba3ead SG |
211 | chip->pinmask = pdata->pinmask; |
212 | chip->use_polling = pdata->use_polling; | |
213 | ||
30ba3ead SG |
214 | input->phys = "tca6416-keys/input0"; |
215 | input->name = client->name; | |
30ba3ead SG |
216 | |
217 | input->open = tca6416_keys_open; | |
218 | input->close = tca6416_keys_close; | |
219 | ||
220 | input->id.bustype = BUS_HOST; | |
221 | input->id.vendor = 0x0001; | |
222 | input->id.product = 0x0001; | |
223 | input->id.version = 0x0100; | |
224 | ||
225 | /* Enable auto repeat feature of Linux input subsystem */ | |
226 | if (pdata->rep) | |
227 | __set_bit(EV_REP, input->evbit); | |
228 | ||
229 | for (i = 0; i < pdata->nbuttons; i++) { | |
230 | unsigned int type; | |
231 | ||
232 | chip->buttons[i] = pdata->buttons[i]; | |
233 | type = (pdata->buttons[i].type) ?: EV_KEY; | |
234 | input_set_capability(input, type, pdata->buttons[i].code); | |
235 | } | |
236 | ||
237 | input_set_drvdata(input, chip); | |
238 | ||
239 | /* | |
240 | * Initialize cached registers from their original values. | |
241 | * we can't share this chip with another i2c master. | |
242 | */ | |
243 | error = tca6416_setup_registers(chip); | |
244 | if (error) | |
91a4c690 | 245 | return error; |
30ba3ead | 246 | |
57b0c96f DT |
247 | if (chip->use_polling) { |
248 | error = input_setup_polling(input, tca6416_keys_scan); | |
249 | if (error) { | |
250 | dev_err(&client->dev, "Failed to setup polling\n"); | |
251 | return error; | |
252 | } | |
253 | ||
254 | input_set_poll_interval(input, TCA6416_POLL_INTERVAL); | |
255 | } else { | |
91a4c690 YL |
256 | error = devm_request_threaded_irq(&client->dev, client->irq, |
257 | NULL, tca6416_keys_isr, | |
258 | IRQF_TRIGGER_FALLING | | |
259 | IRQF_ONESHOT | | |
260 | IRQF_NO_AUTOEN, | |
57b0c96f | 261 | "tca6416-keypad", input); |
30ba3ead SG |
262 | if (error) { |
263 | dev_dbg(&client->dev, | |
264 | "Unable to claim irq %d; error %d\n", | |
687fe7df | 265 | client->irq, error); |
91a4c690 | 266 | return error; |
30ba3ead | 267 | } |
30ba3ead SG |
268 | } |
269 | ||
270 | error = input_register_device(input); | |
271 | if (error) { | |
272 | dev_dbg(&client->dev, | |
273 | "Unable to register input device, error: %d\n", error); | |
91a4c690 | 274 | return error; |
30ba3ead SG |
275 | } |
276 | ||
277 | i2c_set_clientdata(client, chip); | |
278 | ||
279 | return 0; | |
30ba3ead SG |
280 | } |
281 | ||
30ba3ead SG |
282 | static struct i2c_driver tca6416_keypad_driver = { |
283 | .driver = { | |
284 | .name = "tca6416-keypad", | |
285 | }, | |
d8bde56d | 286 | .probe = tca6416_keypad_probe, |
30ba3ead SG |
287 | .id_table = tca6416_id, |
288 | }; | |
289 | ||
290 | static int __init tca6416_keypad_init(void) | |
291 | { | |
292 | return i2c_add_driver(&tca6416_keypad_driver); | |
293 | } | |
294 | ||
295 | subsys_initcall(tca6416_keypad_init); | |
296 | ||
297 | static void __exit tca6416_keypad_exit(void) | |
298 | { | |
299 | i2c_del_driver(&tca6416_keypad_driver); | |
300 | } | |
301 | module_exit(tca6416_keypad_exit); | |
302 | ||
303 | MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>"); | |
c154703b | 304 | MODULE_DESCRIPTION("Keypad driver over tca6416 IO expander"); |
30ba3ead | 305 | MODULE_LICENSE("GPL"); |