aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc
diff options
context:
space:
mode:
authorDaniel Hung-yu Wu <hywu@google.com>2016-05-18 17:55:12 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2016-05-18 18:08:19 -0400
commitb06d43f7a3db3ea4d62b3e0a299855c2a29ea18d (patch)
tree1bbc3d310dfe4c87785b7554820ddcaebb8f76b3 /drivers/input/misc
parentdb36ae8516ac70dd3f889f2448b6191f06f15f20 (diff)
Input: add Atmel Captouch Button driver
Add I2C driver for Atmel Capacitive Touch Button device. Signed-off-by: Hung-yu Wu <hywu@google.com> Signed-off-by: Grant Grundler <grundler@chromium.org> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/misc')
-rw-r--r--drivers/input/misc/Kconfig13
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/atmel_captouch.c290
3 files changed, 304 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 1f2337abcf2f..9c3dfe79d4d4 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -82,6 +82,19 @@ config INPUT_ARIZONA_HAPTICS
82 To compile this driver as a module, choose M here: the 82 To compile this driver as a module, choose M here: the
83 module will be called arizona-haptics. 83 module will be called arizona-haptics.
84 84
85config INPUT_ATMEL_CAPTOUCH
86 tristate "Atmel Capacitive Touch Button Driver"
87 depends on OF || COMPILE_TEST
88 help
89 Say Y here if an Atmel Capacitive Touch Button device which
90 implements "captouch" protocol is connected to I2C bus. Typically
91 this device consists of Atmel Touch sensor controlled by AtMegaXX
92 MCU running firmware based on Qtouch library.
93 One should find "atmel,captouch" node in the board specific DTS.
94
95 To compile this driver as a module, choose M here: the
96 module will be called atmel_captouch.
97
85config INPUT_BMA150 98config INPUT_BMA150
86 tristate "BMA150/SMB380 acceleration sensor support" 99 tristate "BMA150/SMB380 acceleration sensor support"
87 depends on I2C 100 depends on I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 0357a088c6a9..5373f493f370 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_INPUT_APANEL) += apanel.o
17obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o 17obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
18obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o 18obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
19obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o 19obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
20obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o
20obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o 21obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
21obj-$(CONFIG_INPUT_BMA150) += bma150.o 22obj-$(CONFIG_INPUT_BMA150) += bma150.o
22obj-$(CONFIG_INPUT_CM109) += cm109.o 23obj-$(CONFIG_INPUT_CM109) += cm109.o
diff --git a/drivers/input/misc/atmel_captouch.c b/drivers/input/misc/atmel_captouch.c
new file mode 100644
index 000000000000..941265415a89
--- /dev/null
+++ b/drivers/input/misc/atmel_captouch.c
@@ -0,0 +1,290 @@
1/*
2 * Atmel Atmegaxx Capacitive Touch Button Driver
3 *
4 * Copyright (C) 2016 Google, inc.
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16/*
17 * It's irrelevant that the HW used to develop captouch driver is based
18 * on Atmega88PA part and uses QtouchADC parts for sensing touch.
19 * Calling this driver "captouch" is an arbitrary way to distinguish
20 * the protocol this driver supported by other atmel/qtouch drivers.
21 *
22 * Captouch driver supports a newer/different version of the I2C
23 * registers/commands than the qt1070.c driver.
24 * Don't let the similarity of the general driver structure fool you.
25 *
26 * For raw i2c access from userspace, use i2cset/i2cget
27 * to poke at /dev/i2c-N devices.
28 */
29
30#include <linux/device.h>
31#include <linux/kernel.h>
32#include <linux/module.h>
33#include <linux/init.h>
34#include <linux/i2c.h>
35#include <linux/input.h>
36#include <linux/interrupt.h>
37#include <linux/slab.h>
38
39/* Maximum number of buttons supported */
40#define MAX_NUM_OF_BUTTONS 8
41
42/* Registers */
43#define REG_KEY1_THRESHOLD 0x02
44#define REG_KEY2_THRESHOLD 0x03
45#define REG_KEY3_THRESHOLD 0x04
46#define REG_KEY4_THRESHOLD 0x05
47
48#define REG_KEY1_REF_H 0x20
49#define REG_KEY1_REF_L 0x21
50#define REG_KEY2_REF_H 0x22
51#define REG_KEY2_REF_L 0x23
52#define REG_KEY3_REF_H 0x24
53#define REG_KEY3_REF_L 0x25
54#define REG_KEY4_REF_H 0x26
55#define REG_KEY4_REF_L 0x27
56
57#define REG_KEY1_DLT_H 0x30
58#define REG_KEY1_DLT_L 0x31
59#define REG_KEY2_DLT_H 0x32
60#define REG_KEY2_DLT_L 0x33
61#define REG_KEY3_DLT_H 0x34
62#define REG_KEY3_DLT_L 0x35
63#define REG_KEY4_DLT_H 0x36
64#define REG_KEY4_DLT_L 0x37
65
66#define REG_KEY_STATE 0x3C
67
68/*
69 * @i2c_client: I2C slave device client pointer
70 * @input: Input device pointer
71 * @num_btn: Number of buttons
72 * @keycodes: map of button# to KeyCode
73 * @prev_btn: Previous key state to detect button "press" or "release"
74 * @xfer_buf: I2C transfer buffer
75 */
76struct atmel_captouch_device {
77 struct i2c_client *client;
78 struct input_dev *input;
79 u32 num_btn;
80 u32 keycodes[MAX_NUM_OF_BUTTONS];
81 u8 prev_btn;
82 u8 xfer_buf[8] ____cacheline_aligned;
83};
84
85/*
86 * Read from I2C slave device
87 * The protocol is that the client has to provide both the register address
88 * and the length, and while reading back the device would prepend the data
89 * with address and length for verification.
90 */
91static int atmel_read(struct atmel_captouch_device *capdev,
92 u8 reg, u8 *data, size_t len)
93{
94 struct i2c_client *client = capdev->client;
95 struct device *dev = &client->dev;
96 struct i2c_msg msg[2];
97 int err;
98
99 if (len > sizeof(capdev->xfer_buf) - 2)
100 return -EINVAL;
101
102 capdev->xfer_buf[0] = reg;
103 capdev->xfer_buf[1] = len;
104
105 msg[0].addr = client->addr;
106 msg[0].flags = 0;
107 msg[0].buf = capdev->xfer_buf;
108 msg[0].len = 2;
109
110 msg[1].addr = client->addr;
111 msg[1].flags = I2C_M_RD;
112 msg[1].buf = capdev->xfer_buf;
113 msg[1].len = len + 2;
114
115 err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
116 if (err != ARRAY_SIZE(msg))
117 return err < 0 ? err : -EIO;
118
119 if (capdev->xfer_buf[0] != reg) {
120 dev_err(dev,
121 "I2C read error: register address does not match (%#02x vs %02x)\n",
122 capdev->xfer_buf[0], reg);
123 return -ECOMM;
124 }
125
126 memcpy(data, &capdev->xfer_buf[2], len);
127
128 return 0;
129}
130
131/*
132 * Handle interrupt and report the key changes to the input system.
133 * Multi-touch can be supported; however, it really depends on whether
134 * the device can multi-touch.
135 */
136static irqreturn_t atmel_captouch_isr(int irq, void *data)
137{
138 struct atmel_captouch_device *capdev = data;
139 struct device *dev = &capdev->client->dev;
140 int error;
141 int i;
142 u8 new_btn;
143 u8 changed_btn;
144
145 error = atmel_read(capdev, REG_KEY_STATE, &new_btn, 1);
146 if (error) {
147 dev_err(dev, "failed to read button state: %d\n", error);
148 goto out;
149 }
150
151 dev_dbg(dev, "%s: button state %#02x\n", __func__, new_btn);
152
153 changed_btn = new_btn ^ capdev->prev_btn;
154 capdev->prev_btn = new_btn;
155
156 for (i = 0; i < capdev->num_btn; i++) {
157 if (changed_btn & BIT(i))
158 input_report_key(capdev->input,
159 capdev->keycodes[i],
160 new_btn & BIT(i));
161 }
162
163 input_sync(capdev->input);
164
165out:
166 return IRQ_HANDLED;
167}
168
169/*
170 * Probe function to setup the device, input system and interrupt
171 */
172static int atmel_captouch_probe(struct i2c_client *client,
173 const struct i2c_device_id *id)
174{
175 struct atmel_captouch_device *capdev;
176 struct device *dev = &client->dev;
177 struct device_node *node;
178 int i;
179 int err;
180
181 if (!i2c_check_functionality(client->adapter,
182 I2C_FUNC_SMBUS_BYTE_DATA |
183 I2C_FUNC_SMBUS_WORD_DATA |
184 I2C_FUNC_SMBUS_I2C_BLOCK)) {
185 dev_err(dev, "needed i2c functionality is not supported\n");
186 return -EINVAL;
187 }
188
189 capdev = devm_kzalloc(dev, sizeof(*capdev), GFP_KERNEL);
190 if (!capdev)
191 return -ENOMEM;
192
193 capdev->client = client;
194 i2c_set_clientdata(client, capdev);
195
196 err = atmel_read(capdev, REG_KEY_STATE,
197 &capdev->prev_btn, sizeof(capdev->prev_btn));
198 if (err) {
199 dev_err(dev, "failed to read initial button state: %d\n", err);
200 return err;
201 }
202
203 capdev->input = devm_input_allocate_device(dev);
204 if (!capdev->input) {
205 dev_err(dev, "failed to allocate input device\n");
206 return -ENOMEM;
207 }
208
209 capdev->input->id.bustype = BUS_I2C;
210 capdev->input->id.product = 0x880A;
211 capdev->input->id.version = 0;
212 capdev->input->name = "ATMegaXX Capacitive Button Controller";
213 __set_bit(EV_KEY, capdev->input->evbit);
214
215 node = dev->of_node;
216 if (!node) {
217 dev_err(dev, "failed to find matching node in device tree\n");
218 return -EINVAL;
219 }
220
221 if (of_property_read_bool(node, "autorepeat"))
222 __set_bit(EV_REP, capdev->input->evbit);
223
224 capdev->num_btn = of_property_count_u32_elems(node, "linux,keymap");
225 if (capdev->num_btn > MAX_NUM_OF_BUTTONS)
226 capdev->num_btn = MAX_NUM_OF_BUTTONS;
227
228 err = of_property_read_u32_array(node, "linux,keycodes",
229 capdev->keycodes,
230 capdev->num_btn);
231 if (err) {
232 dev_err(dev,
233 "failed to read linux,keycode property: %d\n", err);
234 return err;
235 }
236
237 for (i = 0; i < capdev->num_btn; i++)
238 __set_bit(capdev->keycodes[i], capdev->input->keybit);
239
240 capdev->input->keycode = capdev->keycodes;
241 capdev->input->keycodesize = sizeof(capdev->keycodes[0]);
242 capdev->input->keycodemax = capdev->num_btn;
243
244 err = input_register_device(capdev->input);
245 if (err)
246 return err;
247
248 err = devm_request_threaded_irq(dev, client->irq,
249 NULL, atmel_captouch_isr,
250 IRQF_ONESHOT,
251 "atmel_captouch", capdev);
252 if (err) {
253 dev_err(dev, "failed to request irq %d: %d\n",
254 client->irq, err);
255 return err;
256 }
257
258 return 0;
259}
260
261#ifdef CONFIG_OF
262static const struct of_device_id atmel_captouch_of_id[] = {
263 {
264 .compatible = "atmel,captouch",
265 },
266 { /* sentinel */ }
267};
268MODULE_DEVICE_TABLE(of, atmel_captouch_of_id);
269#endif
270
271static const struct i2c_device_id atmel_captouch_id[] = {
272 { "atmel_captouch", 0 },
273 { }
274};
275MODULE_DEVICE_TABLE(i2c, atmel_captouch_id);
276
277static struct i2c_driver atmel_captouch_driver = {
278 .probe = atmel_captouch_probe,
279 .id_table = atmel_captouch_id,
280 .driver = {
281 .name = "atmel_captouch",
282 .of_match_table = of_match_ptr(atmel_captouch_of_id),
283 },
284};
285module_i2c_driver(atmel_captouch_driver);
286
287/* Module information */
288MODULE_AUTHOR("Hung-yu Wu <hywu@google.com>");
289MODULE_DESCRIPTION("Atmel ATmegaXX Capacitance Touch Sensor I2C Driver");
290MODULE_LICENSE("GPL v2");