cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

atmel_captouch.c (7198B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Atmel Atmegaxx Capacitive Touch Button Driver
      4 *
      5 * Copyright (C) 2016 Google, inc.
      6 */
      7
      8/*
      9 * It's irrelevant that the HW used to develop captouch driver is based
     10 * on Atmega88PA part and uses QtouchADC parts for sensing touch.
     11 * Calling this driver "captouch" is an arbitrary way to distinguish
     12 * the protocol this driver supported by other atmel/qtouch drivers.
     13 *
     14 * Captouch driver supports a newer/different version of the I2C
     15 * registers/commands than the qt1070.c driver.
     16 * Don't let the similarity of the general driver structure fool you.
     17 *
     18 * For raw i2c access from userspace, use i2cset/i2cget
     19 * to poke at /dev/i2c-N devices.
     20 */
     21
     22#include <linux/device.h>
     23#include <linux/kernel.h>
     24#include <linux/module.h>
     25#include <linux/init.h>
     26#include <linux/i2c.h>
     27#include <linux/input.h>
     28#include <linux/interrupt.h>
     29#include <linux/slab.h>
     30
     31/* Maximum number of buttons supported */
     32#define MAX_NUM_OF_BUTTONS		8
     33
     34/* Registers */
     35#define REG_KEY1_THRESHOLD		0x02
     36#define REG_KEY2_THRESHOLD		0x03
     37#define REG_KEY3_THRESHOLD		0x04
     38#define REG_KEY4_THRESHOLD		0x05
     39
     40#define REG_KEY1_REF_H			0x20
     41#define REG_KEY1_REF_L			0x21
     42#define REG_KEY2_REF_H			0x22
     43#define REG_KEY2_REF_L			0x23
     44#define REG_KEY3_REF_H			0x24
     45#define REG_KEY3_REF_L			0x25
     46#define REG_KEY4_REF_H			0x26
     47#define REG_KEY4_REF_L			0x27
     48
     49#define REG_KEY1_DLT_H			0x30
     50#define REG_KEY1_DLT_L			0x31
     51#define REG_KEY2_DLT_H			0x32
     52#define REG_KEY2_DLT_L			0x33
     53#define REG_KEY3_DLT_H			0x34
     54#define REG_KEY3_DLT_L			0x35
     55#define REG_KEY4_DLT_H			0x36
     56#define REG_KEY4_DLT_L			0x37
     57
     58#define REG_KEY_STATE			0x3C
     59
     60/*
     61 * @i2c_client: I2C slave device client pointer
     62 * @input: Input device pointer
     63 * @num_btn: Number of buttons
     64 * @keycodes: map of button# to KeyCode
     65 * @prev_btn: Previous key state to detect button "press" or "release"
     66 * @xfer_buf: I2C transfer buffer
     67 */
     68struct atmel_captouch_device {
     69	struct i2c_client *client;
     70	struct input_dev *input;
     71	u32 num_btn;
     72	u32 keycodes[MAX_NUM_OF_BUTTONS];
     73	u8 prev_btn;
     74	u8 xfer_buf[8] ____cacheline_aligned;
     75};
     76
     77/*
     78 * Read from I2C slave device
     79 * The protocol is that the client has to provide both the register address
     80 * and the length, and while reading back the device would prepend the data
     81 * with address and length for verification.
     82 */
     83static int atmel_read(struct atmel_captouch_device *capdev,
     84			 u8 reg, u8 *data, size_t len)
     85{
     86	struct i2c_client *client = capdev->client;
     87	struct device *dev = &client->dev;
     88	struct i2c_msg msg[2];
     89	int err;
     90
     91	if (len > sizeof(capdev->xfer_buf) - 2)
     92		return -EINVAL;
     93
     94	capdev->xfer_buf[0] = reg;
     95	capdev->xfer_buf[1] = len;
     96
     97	msg[0].addr = client->addr;
     98	msg[0].flags = 0;
     99	msg[0].buf = capdev->xfer_buf;
    100	msg[0].len = 2;
    101
    102	msg[1].addr = client->addr;
    103	msg[1].flags = I2C_M_RD;
    104	msg[1].buf = capdev->xfer_buf;
    105	msg[1].len = len + 2;
    106
    107	err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
    108	if (err != ARRAY_SIZE(msg))
    109		return err < 0 ? err : -EIO;
    110
    111	if (capdev->xfer_buf[0] != reg) {
    112		dev_err(dev,
    113			"I2C read error: register address does not match (%#02x vs %02x)\n",
    114			capdev->xfer_buf[0], reg);
    115		return -ECOMM;
    116	}
    117
    118	memcpy(data, &capdev->xfer_buf[2], len);
    119
    120	return 0;
    121}
    122
    123/*
    124 * Handle interrupt and report the key changes to the input system.
    125 * Multi-touch can be supported; however, it really depends on whether
    126 * the device can multi-touch.
    127 */
    128static irqreturn_t atmel_captouch_isr(int irq, void *data)
    129{
    130	struct atmel_captouch_device *capdev = data;
    131	struct device *dev = &capdev->client->dev;
    132	int error;
    133	int i;
    134	u8 new_btn;
    135	u8 changed_btn;
    136
    137	error = atmel_read(capdev, REG_KEY_STATE, &new_btn, 1);
    138	if (error) {
    139		dev_err(dev, "failed to read button state: %d\n", error);
    140		goto out;
    141	}
    142
    143	dev_dbg(dev, "%s: button state %#02x\n", __func__, new_btn);
    144
    145	changed_btn = new_btn ^ capdev->prev_btn;
    146	capdev->prev_btn = new_btn;
    147
    148	for (i = 0; i < capdev->num_btn; i++) {
    149		if (changed_btn & BIT(i))
    150			input_report_key(capdev->input,
    151					 capdev->keycodes[i],
    152					 new_btn & BIT(i));
    153	}
    154
    155	input_sync(capdev->input);
    156
    157out:
    158	return IRQ_HANDLED;
    159}
    160
    161/*
    162 * Probe function to setup the device, input system and interrupt
    163 */
    164static int atmel_captouch_probe(struct i2c_client *client,
    165		const struct i2c_device_id *id)
    166{
    167	struct atmel_captouch_device *capdev;
    168	struct device *dev = &client->dev;
    169	struct device_node *node;
    170	int i;
    171	int err;
    172
    173	if (!i2c_check_functionality(client->adapter,
    174				     I2C_FUNC_SMBUS_BYTE_DATA |
    175					I2C_FUNC_SMBUS_WORD_DATA |
    176					I2C_FUNC_SMBUS_I2C_BLOCK)) {
    177		dev_err(dev, "needed i2c functionality is not supported\n");
    178		return -EINVAL;
    179	}
    180
    181	capdev = devm_kzalloc(dev, sizeof(*capdev), GFP_KERNEL);
    182	if (!capdev)
    183		return -ENOMEM;
    184
    185	capdev->client = client;
    186
    187	err = atmel_read(capdev, REG_KEY_STATE,
    188			    &capdev->prev_btn, sizeof(capdev->prev_btn));
    189	if (err) {
    190		dev_err(dev, "failed to read initial button state: %d\n", err);
    191		return err;
    192	}
    193
    194	capdev->input = devm_input_allocate_device(dev);
    195	if (!capdev->input) {
    196		dev_err(dev, "failed to allocate input device\n");
    197		return -ENOMEM;
    198	}
    199
    200	capdev->input->id.bustype = BUS_I2C;
    201	capdev->input->id.product = 0x880A;
    202	capdev->input->id.version = 0;
    203	capdev->input->name = "ATMegaXX Capacitive Button Controller";
    204	__set_bit(EV_KEY, capdev->input->evbit);
    205
    206	node = dev->of_node;
    207	if (!node) {
    208		dev_err(dev, "failed to find matching node in device tree\n");
    209		return -EINVAL;
    210	}
    211
    212	if (of_property_read_bool(node, "autorepeat"))
    213		__set_bit(EV_REP, capdev->input->evbit);
    214
    215	capdev->num_btn = of_property_count_u32_elems(node, "linux,keymap");
    216	if (capdev->num_btn > MAX_NUM_OF_BUTTONS)
    217		capdev->num_btn = MAX_NUM_OF_BUTTONS;
    218
    219	err = of_property_read_u32_array(node, "linux,keycodes",
    220					 capdev->keycodes,
    221					 capdev->num_btn);
    222	if (err) {
    223		dev_err(dev,
    224			"failed to read linux,keycode property: %d\n", err);
    225		return err;
    226	}
    227
    228	for (i = 0; i < capdev->num_btn; i++)
    229		__set_bit(capdev->keycodes[i], capdev->input->keybit);
    230
    231	capdev->input->keycode = capdev->keycodes;
    232	capdev->input->keycodesize = sizeof(capdev->keycodes[0]);
    233	capdev->input->keycodemax = capdev->num_btn;
    234
    235	err = input_register_device(capdev->input);
    236	if (err)
    237		return err;
    238
    239	err = devm_request_threaded_irq(dev, client->irq,
    240					NULL, atmel_captouch_isr,
    241					IRQF_ONESHOT,
    242					"atmel_captouch", capdev);
    243	if (err) {
    244		dev_err(dev, "failed to request irq %d: %d\n",
    245			client->irq, err);
    246		return err;
    247	}
    248
    249	return 0;
    250}
    251
    252#ifdef CONFIG_OF
    253static const struct of_device_id atmel_captouch_of_id[] = {
    254	{
    255		.compatible = "atmel,captouch",
    256	},
    257	{ /* sentinel */ }
    258};
    259MODULE_DEVICE_TABLE(of, atmel_captouch_of_id);
    260#endif
    261
    262static const struct i2c_device_id atmel_captouch_id[] = {
    263	{ "atmel_captouch", 0 },
    264	{ }
    265};
    266MODULE_DEVICE_TABLE(i2c, atmel_captouch_id);
    267
    268static struct i2c_driver atmel_captouch_driver = {
    269	.probe		= atmel_captouch_probe,
    270	.id_table	= atmel_captouch_id,
    271	.driver		= {
    272		.name	= "atmel_captouch",
    273		.of_match_table = of_match_ptr(atmel_captouch_of_id),
    274	},
    275};
    276module_i2c_driver(atmel_captouch_driver);
    277
    278/* Module information */
    279MODULE_AUTHOR("Hung-yu Wu <hywu@google.com>");
    280MODULE_DESCRIPTION("Atmel ATmegaXX Capacitance Touch Sensor I2C Driver");
    281MODULE_LICENSE("GPL v2");