]>
Commit | Line | Data |
---|---|---|
74ba9207 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
fde11323 RDP |
2 | /* |
3 | * qt2160.c - Atmel AT42QT2160 Touch Sense Controller | |
4 | * | |
5 | * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com> | |
fde11323 RDP |
6 | */ |
7 | ||
8 | #include <linux/kernel.h> | |
0e47e3dc | 9 | #include <linux/leds.h> |
fde11323 RDP |
10 | #include <linux/module.h> |
11 | #include <linux/slab.h> | |
12 | #include <linux/jiffies.h> | |
13 | #include <linux/i2c.h> | |
14 | #include <linux/irq.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/input.h> | |
17 | ||
18 | #define QT2160_VALID_CHIPID 0x11 | |
19 | ||
20 | #define QT2160_CMD_CHIPID 0 | |
21 | #define QT2160_CMD_CODEVER 1 | |
22 | #define QT2160_CMD_GSTAT 2 | |
23 | #define QT2160_CMD_KEYS3 3 | |
24 | #define QT2160_CMD_KEYS4 4 | |
25 | #define QT2160_CMD_SLIDE 5 | |
26 | #define QT2160_CMD_GPIOS 6 | |
27 | #define QT2160_CMD_SUBVER 7 | |
28 | #define QT2160_CMD_CALIBRATE 10 | |
0e47e3dc JM |
29 | #define QT2160_CMD_DRIVE_X 70 |
30 | #define QT2160_CMD_PWMEN_X 74 | |
31 | #define QT2160_CMD_PWM_DUTY 76 | |
32 | ||
33 | #define QT2160_NUM_LEDS_X 8 | |
fde11323 RDP |
34 | |
35 | #define QT2160_CYCLE_INTERVAL (2*HZ) | |
36 | ||
37 | static unsigned char qt2160_key2code[] = { | |
38 | KEY_0, KEY_1, KEY_2, KEY_3, | |
39 | KEY_4, KEY_5, KEY_6, KEY_7, | |
40 | KEY_8, KEY_9, KEY_A, KEY_B, | |
41 | KEY_C, KEY_D, KEY_E, KEY_F, | |
42 | }; | |
43 | ||
0e47e3dc JM |
44 | #ifdef CONFIG_LEDS_CLASS |
45 | struct qt2160_led { | |
46 | struct qt2160_data *qt2160; | |
47 | struct led_classdev cdev; | |
0e47e3dc JM |
48 | char name[32]; |
49 | int id; | |
83cd2030 | 50 | enum led_brightness brightness; |
0e47e3dc JM |
51 | }; |
52 | #endif | |
53 | ||
fde11323 RDP |
54 | struct qt2160_data { |
55 | struct i2c_client *client; | |
56 | struct input_dev *input; | |
57 | struct delayed_work dwork; | |
fde11323 RDP |
58 | unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; |
59 | u16 key_matrix; | |
0e47e3dc JM |
60 | #ifdef CONFIG_LEDS_CLASS |
61 | struct qt2160_led leds[QT2160_NUM_LEDS_X]; | |
0e47e3dc | 62 | #endif |
fde11323 RDP |
63 | }; |
64 | ||
0e47e3dc JM |
65 | static int qt2160_read(struct i2c_client *client, u8 reg); |
66 | static int qt2160_write(struct i2c_client *client, u8 reg, u8 data); | |
67 | ||
68 | #ifdef CONFIG_LEDS_CLASS | |
69 | ||
83cd2030 DT |
70 | static int qt2160_led_set(struct led_classdev *cdev, |
71 | enum led_brightness value) | |
0e47e3dc | 72 | { |
83cd2030 | 73 | struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev); |
0e47e3dc JM |
74 | struct qt2160_data *qt2160 = led->qt2160; |
75 | struct i2c_client *client = qt2160->client; | |
0e47e3dc JM |
76 | u32 drive, pwmen; |
77 | ||
83cd2030 DT |
78 | if (value != led->brightness) { |
79 | drive = qt2160_read(client, QT2160_CMD_DRIVE_X); | |
80 | pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X); | |
81 | if (value != LED_OFF) { | |
82 | drive |= BIT(led->id); | |
83 | pwmen |= BIT(led->id); | |
0e47e3dc | 84 | |
83cd2030 DT |
85 | } else { |
86 | drive &= ~BIT(led->id); | |
87 | pwmen &= ~BIT(led->id); | |
88 | } | |
89 | qt2160_write(client, QT2160_CMD_DRIVE_X, drive); | |
90 | qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen); | |
0e47e3dc | 91 | |
83cd2030 DT |
92 | /* |
93 | * Changing this register will change the brightness | |
94 | * of every LED in the qt2160. It's a HW limitation. | |
95 | */ | |
96 | if (value != LED_OFF) | |
97 | qt2160_write(client, QT2160_CMD_PWM_DUTY, value); | |
0e47e3dc | 98 | |
83cd2030 DT |
99 | led->brightness = value; |
100 | } | |
0e47e3dc | 101 | |
83cd2030 | 102 | return 0; |
0e47e3dc JM |
103 | } |
104 | ||
105 | #endif /* CONFIG_LEDS_CLASS */ | |
106 | ||
fde11323 RDP |
107 | static int qt2160_read_block(struct i2c_client *client, |
108 | u8 inireg, u8 *buffer, unsigned int count) | |
109 | { | |
110 | int error, idx = 0; | |
111 | ||
112 | /* | |
113 | * Can't use SMBus block data read. Check for I2C functionality to speed | |
114 | * things up whenever possible. Otherwise we will be forced to read | |
115 | * sequentially. | |
116 | */ | |
117 | if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | |
118 | ||
119 | error = i2c_smbus_write_byte(client, inireg + idx); | |
120 | if (error) { | |
121 | dev_err(&client->dev, | |
122 | "couldn't send request. Returned %d\n", error); | |
123 | return error; | |
124 | } | |
125 | ||
126 | error = i2c_master_recv(client, buffer, count); | |
127 | if (error != count) { | |
128 | dev_err(&client->dev, | |
129 | "couldn't read registers. Returned %d bytes\n", error); | |
130 | return error; | |
131 | } | |
132 | } else { | |
133 | ||
134 | while (count--) { | |
135 | int data; | |
136 | ||
137 | error = i2c_smbus_write_byte(client, inireg + idx); | |
138 | if (error) { | |
139 | dev_err(&client->dev, | |
140 | "couldn't send request. Returned %d\n", error); | |
141 | return error; | |
142 | } | |
143 | ||
144 | data = i2c_smbus_read_byte(client); | |
145 | if (data < 0) { | |
146 | dev_err(&client->dev, | |
147 | "couldn't read register. Returned %d\n", data); | |
148 | return data; | |
149 | } | |
150 | ||
151 | buffer[idx++] = data; | |
152 | } | |
153 | } | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | static int qt2160_get_key_matrix(struct qt2160_data *qt2160) | |
159 | { | |
160 | struct i2c_client *client = qt2160->client; | |
161 | struct input_dev *input = qt2160->input; | |
162 | u8 regs[6]; | |
163 | u16 old_matrix, new_matrix; | |
164 | int ret, i, mask; | |
165 | ||
166 | dev_dbg(&client->dev, "requesting keys...\n"); | |
167 | ||
168 | /* | |
169 | * Read all registers from General Status Register | |
170 | * to GPIOs register | |
171 | */ | |
172 | ret = qt2160_read_block(client, QT2160_CMD_GSTAT, regs, 6); | |
173 | if (ret) { | |
174 | dev_err(&client->dev, | |
175 | "could not perform chip read.\n"); | |
176 | return ret; | |
177 | } | |
178 | ||
179 | old_matrix = qt2160->key_matrix; | |
180 | qt2160->key_matrix = new_matrix = (regs[2] << 8) | regs[1]; | |
181 | ||
182 | mask = 0x01; | |
183 | for (i = 0; i < 16; ++i, mask <<= 1) { | |
184 | int keyval = new_matrix & mask; | |
185 | ||
186 | if ((old_matrix & mask) != keyval) { | |
187 | input_report_key(input, qt2160->keycodes[i], keyval); | |
188 | dev_dbg(&client->dev, "key %d %s\n", | |
189 | i, keyval ? "pressed" : "released"); | |
190 | } | |
191 | } | |
192 | ||
193 | input_sync(input); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static irqreturn_t qt2160_irq(int irq, void *_qt2160) | |
199 | { | |
200 | struct qt2160_data *qt2160 = _qt2160; | |
fde11323 | 201 | |
e7c2f967 | 202 | mod_delayed_work(system_wq, &qt2160->dwork, 0); |
fde11323 | 203 | |
fde11323 RDP |
204 | return IRQ_HANDLED; |
205 | } | |
206 | ||
207 | static void qt2160_schedule_read(struct qt2160_data *qt2160) | |
208 | { | |
fde11323 | 209 | schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL); |
fde11323 RDP |
210 | } |
211 | ||
212 | static void qt2160_worker(struct work_struct *work) | |
213 | { | |
214 | struct qt2160_data *qt2160 = | |
215 | container_of(work, struct qt2160_data, dwork.work); | |
216 | ||
217 | dev_dbg(&qt2160->client->dev, "worker\n"); | |
218 | ||
219 | qt2160_get_key_matrix(qt2160); | |
220 | ||
221 | /* Avoid device lock up by checking every so often */ | |
222 | qt2160_schedule_read(qt2160); | |
223 | } | |
224 | ||
5298cc4c | 225 | static int qt2160_read(struct i2c_client *client, u8 reg) |
fde11323 RDP |
226 | { |
227 | int ret; | |
228 | ||
229 | ret = i2c_smbus_write_byte(client, reg); | |
230 | if (ret) { | |
231 | dev_err(&client->dev, | |
232 | "couldn't send request. Returned %d\n", ret); | |
233 | return ret; | |
234 | } | |
235 | ||
236 | ret = i2c_smbus_read_byte(client); | |
237 | if (ret < 0) { | |
238 | dev_err(&client->dev, | |
239 | "couldn't read register. Returned %d\n", ret); | |
240 | return ret; | |
241 | } | |
242 | ||
243 | return ret; | |
244 | } | |
245 | ||
5298cc4c | 246 | static int qt2160_write(struct i2c_client *client, u8 reg, u8 data) |
fde11323 | 247 | { |
a6e8c0a2 | 248 | int ret; |
fde11323 | 249 | |
a6e8c0a2 JM |
250 | ret = i2c_smbus_write_byte_data(client, reg, data); |
251 | if (ret < 0) | |
fde11323 | 252 | dev_err(&client->dev, |
a6e8c0a2 | 253 | "couldn't write data. Returned %d\n", ret); |
fde11323 | 254 | |
a6e8c0a2 | 255 | return ret; |
fde11323 RDP |
256 | } |
257 | ||
0e47e3dc JM |
258 | #ifdef CONFIG_LEDS_CLASS |
259 | ||
260 | static int qt2160_register_leds(struct qt2160_data *qt2160) | |
261 | { | |
262 | struct i2c_client *client = qt2160->client; | |
263 | int ret; | |
264 | int i; | |
265 | ||
0e47e3dc JM |
266 | for (i = 0; i < QT2160_NUM_LEDS_X; i++) { |
267 | struct qt2160_led *led = &qt2160->leds[i]; | |
268 | ||
269 | snprintf(led->name, sizeof(led->name), "qt2160:x%d", i); | |
270 | led->cdev.name = led->name; | |
83cd2030 | 271 | led->cdev.brightness_set_blocking = qt2160_led_set; |
0e47e3dc JM |
272 | led->cdev.brightness = LED_OFF; |
273 | led->id = i; | |
274 | led->qt2160 = qt2160; | |
275 | ||
0e47e3dc JM |
276 | ret = led_classdev_register(&client->dev, &led->cdev); |
277 | if (ret < 0) | |
278 | return ret; | |
279 | } | |
280 | ||
281 | /* Tur off LEDs */ | |
282 | qt2160_write(client, QT2160_CMD_DRIVE_X, 0); | |
283 | qt2160_write(client, QT2160_CMD_PWMEN_X, 0); | |
284 | qt2160_write(client, QT2160_CMD_PWM_DUTY, 0); | |
285 | ||
286 | return 0; | |
287 | } | |
288 | ||
289 | static void qt2160_unregister_leds(struct qt2160_data *qt2160) | |
290 | { | |
291 | int i; | |
292 | ||
83cd2030 | 293 | for (i = 0; i < QT2160_NUM_LEDS_X; i++) |
0e47e3dc | 294 | led_classdev_unregister(&qt2160->leds[i].cdev); |
0e47e3dc JM |
295 | } |
296 | ||
297 | #else | |
298 | ||
299 | static inline int qt2160_register_leds(struct qt2160_data *qt2160) | |
300 | { | |
301 | return 0; | |
302 | } | |
303 | ||
304 | static inline void qt2160_unregister_leds(struct qt2160_data *qt2160) | |
305 | { | |
306 | } | |
307 | ||
308 | #endif | |
fde11323 | 309 | |
5298cc4c | 310 | static bool qt2160_identify(struct i2c_client *client) |
fde11323 RDP |
311 | { |
312 | int id, ver, rev; | |
313 | ||
314 | /* Read Chid ID to check if chip is valid */ | |
315 | id = qt2160_read(client, QT2160_CMD_CHIPID); | |
316 | if (id != QT2160_VALID_CHIPID) { | |
317 | dev_err(&client->dev, "ID %d not supported\n", id); | |
318 | return false; | |
319 | } | |
320 | ||
321 | /* Read chip firmware version */ | |
322 | ver = qt2160_read(client, QT2160_CMD_CODEVER); | |
323 | if (ver < 0) { | |
324 | dev_err(&client->dev, "could not get firmware version\n"); | |
325 | return false; | |
326 | } | |
327 | ||
328 | /* Read chip firmware revision */ | |
329 | rev = qt2160_read(client, QT2160_CMD_SUBVER); | |
330 | if (rev < 0) { | |
331 | dev_err(&client->dev, "could not get firmware revision\n"); | |
332 | return false; | |
333 | } | |
334 | ||
335 | dev_info(&client->dev, "AT42QT2160 firmware version %d.%d.%d\n", | |
336 | ver >> 4, ver & 0xf, rev); | |
337 | ||
338 | return true; | |
339 | } | |
340 | ||
5298cc4c | 341 | static int qt2160_probe(struct i2c_client *client, |
0e47e3dc | 342 | const struct i2c_device_id *id) |
fde11323 RDP |
343 | { |
344 | struct qt2160_data *qt2160; | |
345 | struct input_dev *input; | |
346 | int i; | |
347 | int error; | |
348 | ||
349 | /* Check functionality */ | |
350 | error = i2c_check_functionality(client->adapter, | |
351 | I2C_FUNC_SMBUS_BYTE); | |
352 | if (!error) { | |
353 | dev_err(&client->dev, "%s adapter not supported\n", | |
354 | dev_driver_string(&client->adapter->dev)); | |
355 | return -ENODEV; | |
356 | } | |
357 | ||
358 | if (!qt2160_identify(client)) | |
359 | return -ENODEV; | |
360 | ||
361 | /* Chip is valid and active. Allocate structure */ | |
362 | qt2160 = kzalloc(sizeof(struct qt2160_data), GFP_KERNEL); | |
363 | input = input_allocate_device(); | |
364 | if (!qt2160 || !input) { | |
365 | dev_err(&client->dev, "insufficient memory\n"); | |
366 | error = -ENOMEM; | |
367 | goto err_free_mem; | |
368 | } | |
369 | ||
370 | qt2160->client = client; | |
371 | qt2160->input = input; | |
372 | INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker); | |
fde11323 RDP |
373 | |
374 | input->name = "AT42QT2160 Touch Sense Keyboard"; | |
375 | input->id.bustype = BUS_I2C; | |
376 | ||
377 | input->keycode = qt2160->keycodes; | |
378 | input->keycodesize = sizeof(qt2160->keycodes[0]); | |
379 | input->keycodemax = ARRAY_SIZE(qt2160_key2code); | |
380 | ||
381 | __set_bit(EV_KEY, input->evbit); | |
382 | __clear_bit(EV_REP, input->evbit); | |
383 | for (i = 0; i < ARRAY_SIZE(qt2160_key2code); i++) { | |
384 | qt2160->keycodes[i] = qt2160_key2code[i]; | |
385 | __set_bit(qt2160_key2code[i], input->keybit); | |
386 | } | |
387 | __clear_bit(KEY_RESERVED, input->keybit); | |
388 | ||
389 | /* Calibrate device */ | |
390 | error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1); | |
391 | if (error) { | |
392 | dev_err(&client->dev, "failed to calibrate device\n"); | |
393 | goto err_free_mem; | |
394 | } | |
395 | ||
396 | if (client->irq) { | |
397 | error = request_irq(client->irq, qt2160_irq, | |
398 | IRQF_TRIGGER_FALLING, "qt2160", qt2160); | |
399 | if (error) { | |
400 | dev_err(&client->dev, | |
401 | "failed to allocate irq %d\n", client->irq); | |
402 | goto err_free_mem; | |
403 | } | |
404 | } | |
405 | ||
0e47e3dc JM |
406 | error = qt2160_register_leds(qt2160); |
407 | if (error) { | |
408 | dev_err(&client->dev, "Failed to register leds\n"); | |
409 | goto err_free_irq; | |
410 | } | |
411 | ||
fde11323 RDP |
412 | error = input_register_device(qt2160->input); |
413 | if (error) { | |
414 | dev_err(&client->dev, | |
415 | "Failed to register input device\n"); | |
0e47e3dc | 416 | goto err_unregister_leds; |
fde11323 RDP |
417 | } |
418 | ||
419 | i2c_set_clientdata(client, qt2160); | |
420 | qt2160_schedule_read(qt2160); | |
421 | ||
422 | return 0; | |
423 | ||
0e47e3dc JM |
424 | err_unregister_leds: |
425 | qt2160_unregister_leds(qt2160); | |
fde11323 RDP |
426 | err_free_irq: |
427 | if (client->irq) | |
428 | free_irq(client->irq, qt2160); | |
429 | err_free_mem: | |
430 | input_free_device(input); | |
431 | kfree(qt2160); | |
432 | return error; | |
433 | } | |
434 | ||
e2619cf7 | 435 | static int qt2160_remove(struct i2c_client *client) |
fde11323 RDP |
436 | { |
437 | struct qt2160_data *qt2160 = i2c_get_clientdata(client); | |
438 | ||
0e47e3dc JM |
439 | qt2160_unregister_leds(qt2160); |
440 | ||
fde11323 RDP |
441 | /* Release IRQ so no queue will be scheduled */ |
442 | if (client->irq) | |
443 | free_irq(client->irq, qt2160); | |
444 | ||
445 | cancel_delayed_work_sync(&qt2160->dwork); | |
446 | ||
447 | input_unregister_device(qt2160->input); | |
448 | kfree(qt2160); | |
449 | ||
fde11323 RDP |
450 | return 0; |
451 | } | |
452 | ||
ce7b39a1 | 453 | static const struct i2c_device_id qt2160_idtable[] = { |
fde11323 RDP |
454 | { "qt2160", 0, }, |
455 | { } | |
456 | }; | |
457 | ||
458 | MODULE_DEVICE_TABLE(i2c, qt2160_idtable); | |
459 | ||
460 | static struct i2c_driver qt2160_driver = { | |
461 | .driver = { | |
462 | .name = "qt2160", | |
fde11323 RDP |
463 | }, |
464 | ||
465 | .id_table = qt2160_idtable, | |
466 | .probe = qt2160_probe, | |
1cb0aa88 | 467 | .remove = qt2160_remove, |
fde11323 RDP |
468 | }; |
469 | ||
1b92c1cf | 470 | module_i2c_driver(qt2160_driver); |
fde11323 RDP |
471 | |
472 | MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>"); | |
473 | MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor"); | |
474 | MODULE_LICENSE("GPL"); |