Uwe Kleine-König (The Capable Hub)Input: Use named initializers for arrays of i2c_device_data47ceab26 months agoHistory
278 lines · 7.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Atmel Atmegaxx Capacitive Touch Button Driver * * Copyright (C) 2016 Google, inc. */ /* * It's irrelevant that the HW used to develop captouch driver is based * on Atmega88PA part and uses QtouchADC parts for sensing touch. * Calling this driver "captouch" is an arbitrary way to distinguish * the protocol this driver supported by other atmel/qtouch drivers. * * Captouch driver supports a newer/different version of the I2C * registers/commands than the qt1070.c driver. * Don't let the similarity of the general driver structure fool you. * * For raw i2c access from userspace, use i2cset/i2cget * to poke at /dev/i2c-N devices. */ #include <linux/device.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/i2c.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/slab.h> /* Maximum number of buttons supported */#define MAX_NUM_OF_BUTTONS		8 /* Registers */#define REG_KEY1_THRESHOLD		0x02#define REG_KEY2_THRESHOLD		0x03#define REG_KEY3_THRESHOLD		0x04#define REG_KEY4_THRESHOLD		0x05 #define REG_KEY1_REF_H			0x20#define REG_KEY1_REF_L			0x21#define REG_KEY2_REF_H			0x22#define REG_KEY2_REF_L			0x23#define REG_KEY3_REF_H			0x24#define REG_KEY3_REF_L			0x25#define REG_KEY4_REF_H			0x26#define REG_KEY4_REF_L			0x27 #define REG_KEY1_DLT_H			0x30#define REG_KEY1_DLT_L			0x31#define REG_KEY2_DLT_H			0x32#define REG_KEY2_DLT_L			0x33#define REG_KEY3_DLT_H			0x34#define REG_KEY3_DLT_L			0x35#define REG_KEY4_DLT_H			0x36#define REG_KEY4_DLT_L			0x37 #define REG_KEY_STATE			0x3C /* * @i2c_client: I2C slave device client pointer * @input: Input device pointer * @num_btn: Number of buttons * @keycodes: map of button# to KeyCode * @prev_btn: Previous key state to detect button "press" or "release" * @xfer_buf: I2C transfer buffer */struct atmel_captouch_device {	struct i2c_client *client;	struct input_dev *input;	u32 num_btn;	u32 keycodes[MAX_NUM_OF_BUTTONS];	u8 prev_btn;	u8 xfer_buf[8] ____cacheline_aligned;}; /* * Read from I2C slave device * The protocol is that the client has to provide both the register address * and the length, and while reading back the device would prepend the data * with address and length for verification. */static int atmel_read(struct atmel_captouch_device *capdev,			 u8 reg, u8 *data, size_t len){	struct i2c_client *client = capdev->client;	struct device *dev = &client->dev;	struct i2c_msg msg[2];	int err; 	if (len > sizeof(capdev->xfer_buf) - 2)		return -EINVAL; 	capdev->xfer_buf[0] = reg;	capdev->xfer_buf[1] = len; 	msg[0].addr = client->addr;	msg[0].flags = 0;	msg[0].buf = capdev->xfer_buf;	msg[0].len = 2; 	msg[1].addr = client->addr;	msg[1].flags = I2C_M_RD;	msg[1].buf = capdev->xfer_buf;	msg[1].len = len + 2; 	err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));	if (err != ARRAY_SIZE(msg))		return err < 0 ? err : -EIO; 	if (capdev->xfer_buf[0] != reg) {		dev_err(dev,			"I2C read error: register address does not match (%#02x vs %02x)\n",			capdev->xfer_buf[0], reg);		return -ECOMM;	} 	memcpy(data, &capdev->xfer_buf[2], len); 	return 0;} /* * Handle interrupt and report the key changes to the input system. * Multi-touch can be supported; however, it really depends on whether * the device can multi-touch. */static irqreturn_t atmel_captouch_isr(int irq, void *data){	struct atmel_captouch_device *capdev = data;	struct device *dev = &capdev->client->dev;	int error;	int i;	u8 new_btn;	u8 changed_btn; 	error = atmel_read(capdev, REG_KEY_STATE, &new_btn, 1);	if (error) {		dev_err(dev, "failed to read button state: %d\n", error);		goto out;	} 	dev_dbg(dev, "%s: button state %#02x\n", __func__, new_btn); 	changed_btn = new_btn ^ capdev->prev_btn;	capdev->prev_btn = new_btn; 	for (i = 0; i < capdev->num_btn; i++) {		if (changed_btn & BIT(i))			input_report_key(capdev->input,					 capdev->keycodes[i],					 new_btn & BIT(i));	} 	input_sync(capdev->input); out:	return IRQ_HANDLED;} /* * Probe function to setup the device, input system and interrupt */static int atmel_captouch_probe(struct i2c_client *client){	struct atmel_captouch_device *capdev;	struct device *dev = &client->dev;	struct device_node *node;	int i;	int err; 	if (!i2c_check_functionality(client->adapter,				     I2C_FUNC_SMBUS_BYTE_DATA |					I2C_FUNC_SMBUS_WORD_DATA |					I2C_FUNC_SMBUS_I2C_BLOCK)) {		dev_err(dev, "needed i2c functionality is not supported\n");		return -EINVAL;	} 	capdev = devm_kzalloc(dev, sizeof(*capdev), GFP_KERNEL);	if (!capdev)		return -ENOMEM; 	capdev->client = client; 	err = atmel_read(capdev, REG_KEY_STATE,			    &capdev->prev_btn, sizeof(capdev->prev_btn));	if (err) {		dev_err(dev, "failed to read initial button state: %d\n", err);		return err;	} 	capdev->input = devm_input_allocate_device(dev);	if (!capdev->input) {		dev_err(dev, "failed to allocate input device\n");		return -ENOMEM;	} 	capdev->input->id.bustype = BUS_I2C;	capdev->input->id.product = 0x880A;	capdev->input->id.version = 0;	capdev->input->name = "ATMegaXX Capacitive Button Controller";	__set_bit(EV_KEY, capdev->input->evbit); 	node = dev->of_node;	if (!node) {		dev_err(dev, "failed to find matching node in device tree\n");		return -EINVAL;	} 	if (of_property_read_bool(node, "autorepeat"))		__set_bit(EV_REP, capdev->input->evbit); 	capdev->num_btn = of_property_count_u32_elems(node, "linux,keymap");	if (capdev->num_btn > MAX_NUM_OF_BUTTONS)		capdev->num_btn = MAX_NUM_OF_BUTTONS; 	err = of_property_read_u32_array(node, "linux,keycodes",					 capdev->keycodes,					 capdev->num_btn);	if (err) {		dev_err(dev,			"failed to read linux,keycode property: %d\n", err);		return err;	} 	for (i = 0; i < capdev->num_btn; i++)		__set_bit(capdev->keycodes[i], capdev->input->keybit); 	capdev->input->keycode = capdev->keycodes;	capdev->input->keycodesize = sizeof(capdev->keycodes[0]);	capdev->input->keycodemax = capdev->num_btn; 	err = input_register_device(capdev->input);	if (err)		return err; 	err = devm_request_threaded_irq(dev, client->irq,					NULL, atmel_captouch_isr,					IRQF_ONESHOT,					"atmel_captouch", capdev);	if (err) {		dev_err(dev, "failed to request irq %d: %d\n",			client->irq, err);		return err;	} 	return 0;} static const struct of_device_id atmel_captouch_of_id[] = {	{		.compatible = "atmel,captouch",	},	{ /* sentinel */ }};MODULE_DEVICE_TABLE(of, atmel_captouch_of_id); static const struct i2c_device_id atmel_captouch_id[] = {	{ .name = "atmel_captouch" },	{ }};MODULE_DEVICE_TABLE(i2c, atmel_captouch_id); static struct i2c_driver atmel_captouch_driver = {	.probe		= atmel_captouch_probe,	.id_table	= atmel_captouch_id,	.driver		= {		.name	= "atmel_captouch",		.of_match_table = atmel_captouch_of_id,	},};module_i2c_driver(atmel_captouch_driver); /* Module information */MODULE_AUTHOR("Hung-yu Wu <hywu@google.com>");MODULE_DESCRIPTION("Atmel ATmegaXX Capacitance Touch Sensor I2C Driver");MODULE_LICENSE("GPL v2");