1 // SPDX-License-Identifier: GPL-2.0-only
3 * D-Link DIR-685 router I2C-based Touchkeys input driver
4 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
6 * This is a one-off touchkey controller based on the Cypress Semiconductor
7 * CY8C214 MCU with some firmware in its internal 8KB flash. The circuit
8 * board inside the router is named E119921
11 #include <linux/module.h>
12 #include <linux/i2c.h>
13 #include <linux/interrupt.h>
14 #include <linux/delay.h>
15 #include <linux/input.h>
16 #include <linux/slab.h>
17 #include <linux/bitops.h>
19 struct dir685_touchkeys
{
21 struct i2c_client
*client
;
22 struct input_dev
*input
;
23 unsigned long cur_key
;
27 static irqreturn_t
dir685_tk_irq_thread(int irq
, void *data
)
29 struct dir685_touchkeys
*tk
= data
;
30 const int num_bits
= min_t(int, ARRAY_SIZE(tk
->codes
), 16);
31 unsigned long changed
;
37 memset(buf
, 0, sizeof(buf
));
38 err
= i2c_master_recv(tk
->client
, buf
, sizeof(buf
));
39 if (err
!= sizeof(buf
)) {
40 dev_err(tk
->dev
, "short read %d\n", err
);
44 dev_dbg(tk
->dev
, "IN: %*ph\n", (int)sizeof(buf
), buf
);
45 key
= be16_to_cpup((__be16
*) &buf
[4]);
47 /* Figure out if any bits went high or low since last message */
48 changed
= tk
->cur_key
^ key
;
49 for_each_set_bit(i
, &changed
, num_bits
) {
50 dev_dbg(tk
->dev
, "key %d is %s\n", i
,
51 test_bit(i
, &key
) ? "down" : "up");
52 input_report_key(tk
->input
, tk
->codes
[i
], test_bit(i
, &key
));
55 /* Store currently down keys */
57 input_sync(tk
->input
);
62 static int dir685_tk_probe(struct i2c_client
*client
)
64 static const u8 bl_data
[] = { 0xa7, 0x40 };
65 struct device
*dev
= &client
->dev
;
66 struct dir685_touchkeys
*tk
;
70 tk
= devm_kzalloc(&client
->dev
, sizeof(*tk
), GFP_KERNEL
);
74 tk
->input
= devm_input_allocate_device(dev
);
81 tk
->input
->keycodesize
= sizeof(u16
);
82 tk
->input
->keycodemax
= ARRAY_SIZE(tk
->codes
);
83 tk
->input
->keycode
= tk
->codes
;
84 tk
->codes
[0] = KEY_UP
;
85 tk
->codes
[1] = KEY_DOWN
;
86 tk
->codes
[2] = KEY_LEFT
;
87 tk
->codes
[3] = KEY_RIGHT
;
88 tk
->codes
[4] = KEY_ENTER
;
89 tk
->codes
[5] = KEY_WPS_BUTTON
;
91 * This key appears in the vendor driver, but I have
92 * not been able to activate it.
94 tk
->codes
[6] = KEY_RESERVED
;
96 __set_bit(EV_KEY
, tk
->input
->evbit
);
97 for (i
= 0; i
< ARRAY_SIZE(tk
->codes
); i
++)
98 __set_bit(tk
->codes
[i
], tk
->input
->keybit
);
99 __clear_bit(KEY_RESERVED
, tk
->input
->keybit
);
101 tk
->input
->name
= "D-Link DIR-685 touchkeys";
102 tk
->input
->id
.bustype
= BUS_I2C
;
104 err
= input_register_device(tk
->input
);
108 /* Set the brightness to max level */
109 err
= i2c_master_send(client
, bl_data
, sizeof(bl_data
));
110 if (err
!= sizeof(bl_data
))
111 dev_warn(tk
->dev
, "error setting brightness level\n");
114 dev_err(dev
, "no IRQ on the I2C device\n");
117 err
= devm_request_threaded_irq(dev
, client
->irq
,
118 NULL
, dir685_tk_irq_thread
,
122 dev_err(dev
, "can't request IRQ\n");
129 static const struct i2c_device_id dir685_tk_id
[] = {
133 MODULE_DEVICE_TABLE(i2c
, dir685_tk_id
);
136 static const struct of_device_id dir685_tk_of_match
[] = {
137 { .compatible
= "dlink,dir685-touchkeys" },
140 MODULE_DEVICE_TABLE(of
, dir685_tk_of_match
);
143 static struct i2c_driver dir685_tk_i2c_driver
= {
145 .name
= "dlink-dir685-touchkeys",
146 .of_match_table
= of_match_ptr(dir685_tk_of_match
),
148 .probe
= dir685_tk_probe
,
149 .id_table
= dir685_tk_id
,
151 module_i2c_driver(dir685_tk_i2c_driver
);
153 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
154 MODULE_DESCRIPTION("D-Link DIR-685 touchkeys driver");
155 MODULE_LICENSE("GPL");