linux/drivers/input/misc/iqs7222.c
Jeff LaBundy 992cf65674 Input: iqs7222 - add support for IQS7222D v1.1 and v1.2
The vendor has introduced two new revisions with slightly different
memory maps; update the driver to support them.

Fixes: dd24e202ac ("Input: iqs7222 - add support for Azoteq IQS7222D")
Signed-off-by: Jeff LaBundy <jeff@labundy.com>
Link: https://lore.kernel.org/r/ZelTRYX3fenMQuhF@nixie71
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2024-03-07 10:10:50 -08:00

3165 lines
75 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Azoteq IQS7222A/B/C/D Capacitive Touch Controller
*
* Copyright (C) 2022 Jeff LaBundy <jeff@labundy.com>
*/
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#define IQS7222_PROD_NUM 0x00
#define IQS7222_PROD_NUM_A 840
#define IQS7222_PROD_NUM_B 698
#define IQS7222_PROD_NUM_C 863
#define IQS7222_PROD_NUM_D 1046
#define IQS7222_SYS_STATUS 0x10
#define IQS7222_SYS_STATUS_RESET BIT(3)
#define IQS7222_SYS_STATUS_ATI_ERROR BIT(1)
#define IQS7222_SYS_STATUS_ATI_ACTIVE BIT(0)
#define IQS7222_CHAN_SETUP_0_REF_MODE_MASK GENMASK(15, 14)
#define IQS7222_CHAN_SETUP_0_REF_MODE_FOLLOW BIT(15)
#define IQS7222_CHAN_SETUP_0_REF_MODE_REF BIT(14)
#define IQS7222_CHAN_SETUP_0_CHAN_EN BIT(8)
#define IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK GENMASK(2, 0)
#define IQS7222_SLDR_SETUP_2_RES_MASK GENMASK(15, 8)
#define IQS7222_SLDR_SETUP_2_RES_SHIFT 8
#define IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK GENMASK(7, 0)
#define IQS7222_GPIO_SETUP_0_GPIO_EN BIT(0)
#define IQS7222_SYS_SETUP 0xD0
#define IQS7222_SYS_SETUP_INTF_MODE_MASK GENMASK(7, 6)
#define IQS7222_SYS_SETUP_INTF_MODE_TOUCH BIT(7)
#define IQS7222_SYS_SETUP_INTF_MODE_EVENT BIT(6)
#define IQS7222_SYS_SETUP_PWR_MODE_MASK GENMASK(5, 4)
#define IQS7222_SYS_SETUP_PWR_MODE_AUTO IQS7222_SYS_SETUP_PWR_MODE_MASK
#define IQS7222_SYS_SETUP_REDO_ATI BIT(2)
#define IQS7222_SYS_SETUP_ACK_RESET BIT(0)
#define IQS7222_EVENT_MASK_ATI BIT(12)
#define IQS7222_EVENT_MASK_SLDR BIT(10)
#define IQS7222_EVENT_MASK_TPAD IQS7222_EVENT_MASK_SLDR
#define IQS7222_EVENT_MASK_TOUCH BIT(1)
#define IQS7222_EVENT_MASK_PROX BIT(0)
#define IQS7222_COMMS_HOLD BIT(0)
#define IQS7222_COMMS_ERROR 0xEEEE
#define IQS7222_COMMS_RETRY_MS 50
#define IQS7222_COMMS_TIMEOUT_MS 100
#define IQS7222_RESET_TIMEOUT_MS 250
#define IQS7222_ATI_TIMEOUT_MS 2000
#define IQS7222_MAX_COLS_STAT 8
#define IQS7222_MAX_COLS_CYCLE 3
#define IQS7222_MAX_COLS_GLBL 3
#define IQS7222_MAX_COLS_BTN 3
#define IQS7222_MAX_COLS_CHAN 6
#define IQS7222_MAX_COLS_FILT 2
#define IQS7222_MAX_COLS_SLDR 11
#define IQS7222_MAX_COLS_TPAD 24
#define IQS7222_MAX_COLS_GPIO 3
#define IQS7222_MAX_COLS_SYS 13
#define IQS7222_MAX_CHAN 20
#define IQS7222_MAX_SLDR 2
#define IQS7222_NUM_RETRIES 5
#define IQS7222_REG_OFFSET 0x100
enum iqs7222_reg_key_id {
IQS7222_REG_KEY_NONE,
IQS7222_REG_KEY_PROX,
IQS7222_REG_KEY_TOUCH,
IQS7222_REG_KEY_DEBOUNCE,
IQS7222_REG_KEY_TAP,
IQS7222_REG_KEY_TAP_LEGACY,
IQS7222_REG_KEY_AXIAL,
IQS7222_REG_KEY_AXIAL_LEGACY,
IQS7222_REG_KEY_WHEEL,
IQS7222_REG_KEY_NO_WHEEL,
IQS7222_REG_KEY_RESERVED
};
enum iqs7222_reg_grp_id {
IQS7222_REG_GRP_STAT,
IQS7222_REG_GRP_FILT,
IQS7222_REG_GRP_CYCLE,
IQS7222_REG_GRP_GLBL,
IQS7222_REG_GRP_BTN,
IQS7222_REG_GRP_CHAN,
IQS7222_REG_GRP_SLDR,
IQS7222_REG_GRP_TPAD,
IQS7222_REG_GRP_GPIO,
IQS7222_REG_GRP_SYS,
IQS7222_NUM_REG_GRPS
};
static const char * const iqs7222_reg_grp_names[IQS7222_NUM_REG_GRPS] = {
[IQS7222_REG_GRP_CYCLE] = "cycle-%d",
[IQS7222_REG_GRP_CHAN] = "channel-%d",
[IQS7222_REG_GRP_SLDR] = "slider-%d",
[IQS7222_REG_GRP_TPAD] = "trackpad",
[IQS7222_REG_GRP_GPIO] = "gpio-%d",
};
static const unsigned int iqs7222_max_cols[IQS7222_NUM_REG_GRPS] = {
[IQS7222_REG_GRP_STAT] = IQS7222_MAX_COLS_STAT,
[IQS7222_REG_GRP_CYCLE] = IQS7222_MAX_COLS_CYCLE,
[IQS7222_REG_GRP_GLBL] = IQS7222_MAX_COLS_GLBL,
[IQS7222_REG_GRP_BTN] = IQS7222_MAX_COLS_BTN,
[IQS7222_REG_GRP_CHAN] = IQS7222_MAX_COLS_CHAN,
[IQS7222_REG_GRP_FILT] = IQS7222_MAX_COLS_FILT,
[IQS7222_REG_GRP_SLDR] = IQS7222_MAX_COLS_SLDR,
[IQS7222_REG_GRP_TPAD] = IQS7222_MAX_COLS_TPAD,
[IQS7222_REG_GRP_GPIO] = IQS7222_MAX_COLS_GPIO,
[IQS7222_REG_GRP_SYS] = IQS7222_MAX_COLS_SYS,
};
static const unsigned int iqs7222_gpio_links[] = { 2, 5, 6, };
struct iqs7222_event_desc {
const char *name;
u16 link;
u16 mask;
u16 val;
u16 strict;
u16 enable;
enum iqs7222_reg_key_id reg_key;
};
static const struct iqs7222_event_desc iqs7222_kp_events[] = {
{
.name = "event-prox",
.enable = IQS7222_EVENT_MASK_PROX,
.reg_key = IQS7222_REG_KEY_PROX,
},
{
.name = "event-touch",
.enable = IQS7222_EVENT_MASK_TOUCH,
.reg_key = IQS7222_REG_KEY_TOUCH,
},
};
static const struct iqs7222_event_desc iqs7222_sl_events[] = {
{ .name = "event-press", },
{
.name = "event-tap",
.mask = BIT(0),
.val = BIT(0),
.enable = BIT(0),
.reg_key = IQS7222_REG_KEY_TAP,
},
{
.name = "event-swipe-pos",
.mask = BIT(5) | BIT(1),
.val = BIT(1),
.enable = BIT(1),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-swipe-neg",
.mask = BIT(5) | BIT(1),
.val = BIT(5) | BIT(1),
.enable = BIT(1),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-flick-pos",
.mask = BIT(5) | BIT(2),
.val = BIT(2),
.enable = BIT(2),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-flick-neg",
.mask = BIT(5) | BIT(2),
.val = BIT(5) | BIT(2),
.enable = BIT(2),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
};
static const struct iqs7222_event_desc iqs7222_tp_events[] = {
{
.name = "event-press",
.link = BIT(7),
},
{
.name = "event-tap",
.link = BIT(0),
.mask = BIT(0),
.val = BIT(0),
.enable = BIT(0),
.reg_key = IQS7222_REG_KEY_TAP,
},
{
.name = "event-swipe-x-pos",
.link = BIT(2),
.mask = BIT(2) | BIT(1),
.val = BIT(2),
.strict = BIT(4),
.enable = BIT(1),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-swipe-y-pos",
.link = BIT(3),
.mask = BIT(3) | BIT(1),
.val = BIT(3),
.strict = BIT(3),
.enable = BIT(1),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-swipe-x-neg",
.link = BIT(4),
.mask = BIT(4) | BIT(1),
.val = BIT(4),
.strict = BIT(4),
.enable = BIT(1),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-swipe-y-neg",
.link = BIT(5),
.mask = BIT(5) | BIT(1),
.val = BIT(5),
.strict = BIT(3),
.enable = BIT(1),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-flick-x-pos",
.link = BIT(2),
.mask = BIT(2) | BIT(1),
.val = BIT(2) | BIT(1),
.strict = BIT(4),
.enable = BIT(2),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-flick-y-pos",
.link = BIT(3),
.mask = BIT(3) | BIT(1),
.val = BIT(3) | BIT(1),
.strict = BIT(3),
.enable = BIT(2),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-flick-x-neg",
.link = BIT(4),
.mask = BIT(4) | BIT(1),
.val = BIT(4) | BIT(1),
.strict = BIT(4),
.enable = BIT(2),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
{
.name = "event-flick-y-neg",
.link = BIT(5),
.mask = BIT(5) | BIT(1),
.val = BIT(5) | BIT(1),
.strict = BIT(3),
.enable = BIT(2),
.reg_key = IQS7222_REG_KEY_AXIAL,
},
};
struct iqs7222_reg_grp_desc {
u16 base;
int num_row;
int num_col;
};
struct iqs7222_dev_desc {
u16 prod_num;
u16 fw_major;
u16 fw_minor;
u16 sldr_res;
u16 touch_link;
u16 wheel_enable;
int allow_offset;
int event_offset;
int comms_offset;
bool legacy_gesture;
struct iqs7222_reg_grp_desc reg_grps[IQS7222_NUM_REG_GRPS];
};
static const struct iqs7222_dev_desc iqs7222_devs[] = {
{
.prod_num = IQS7222_PROD_NUM_A,
.fw_major = 1,
.fw_minor = 13,
.sldr_res = U8_MAX * 16,
.touch_link = 1768,
.allow_offset = 9,
.event_offset = 10,
.comms_offset = 12,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 8,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 7,
.num_col = 3,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8700,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 12,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 12,
.num_col = 6,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAC00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_SLDR] = {
.base = 0xB000,
.num_row = 2,
.num_col = 11,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 13,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_A,
.fw_major = 1,
.fw_minor = 12,
.sldr_res = U8_MAX * 16,
.touch_link = 1768,
.allow_offset = 9,
.event_offset = 10,
.comms_offset = 12,
.legacy_gesture = true,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 8,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 7,
.num_col = 3,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8700,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 12,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 12,
.num_col = 6,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAC00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_SLDR] = {
.base = 0xB000,
.num_row = 2,
.num_col = 11,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 13,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_B,
.fw_major = 1,
.fw_minor = 43,
.event_offset = 10,
.comms_offset = 11,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 6,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 10,
.num_col = 2,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8A00,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 20,
.num_col = 2,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xB000,
.num_row = 20,
.num_col = 4,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xC400,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 13,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_B,
.fw_major = 1,
.fw_minor = 27,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 6,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 10,
.num_col = 2,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8A00,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 20,
.num_col = 2,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xB000,
.num_row = 20,
.num_col = 4,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xC400,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 10,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_C,
.fw_major = 2,
.fw_minor = 6,
.sldr_res = U16_MAX,
.touch_link = 1686,
.wheel_enable = BIT(3),
.event_offset = 9,
.comms_offset = 10,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 6,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 5,
.num_col = 3,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8500,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 10,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 10,
.num_col = 6,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAA00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_SLDR] = {
.base = 0xB000,
.num_row = 2,
.num_col = 10,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 3,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 12,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_C,
.fw_major = 1,
.fw_minor = 13,
.sldr_res = U16_MAX,
.touch_link = 1674,
.wheel_enable = BIT(3),
.event_offset = 9,
.comms_offset = 10,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 6,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 5,
.num_col = 3,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8500,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 10,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 10,
.num_col = 6,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAA00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_SLDR] = {
.base = 0xB000,
.num_row = 2,
.num_col = 10,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 11,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_D,
.fw_major = 1,
.fw_minor = 2,
.touch_link = 1770,
.allow_offset = 9,
.event_offset = 10,
.comms_offset = 11,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 7,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 7,
.num_col = 2,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8700,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 14,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 14,
.num_col = 4,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAE00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_TPAD] = {
.base = 0xB000,
.num_row = 1,
.num_col = 24,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 3,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 12,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_D,
.fw_major = 1,
.fw_minor = 1,
.touch_link = 1774,
.allow_offset = 9,
.event_offset = 10,
.comms_offset = 11,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 7,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 7,
.num_col = 2,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8700,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 14,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 14,
.num_col = 4,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAE00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_TPAD] = {
.base = 0xB000,
.num_row = 1,
.num_col = 24,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 3,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 12,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_D,
.fw_major = 0,
.fw_minor = 37,
.touch_link = 1770,
.allow_offset = 9,
.event_offset = 10,
.comms_offset = 11,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 7,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 7,
.num_col = 2,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8700,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 14,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 14,
.num_col = 4,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAE00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_TPAD] = {
.base = 0xB000,
.num_row = 1,
.num_col = 24,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 3,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 12,
},
},
},
};
struct iqs7222_prop_desc {
const char *name;
enum iqs7222_reg_grp_id reg_grp;
enum iqs7222_reg_key_id reg_key;
int reg_offset;
int reg_shift;
int reg_width;
int val_pitch;
int val_min;
int val_max;
bool invert;
const char *label;
};
static const struct iqs7222_prop_desc iqs7222_props[] = {
{
.name = "azoteq,conv-period",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 0,
.reg_shift = 8,
.reg_width = 8,
.label = "conversion period",
},
{
.name = "azoteq,conv-frac",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 0,
.reg_shift = 0,
.reg_width = 8,
.label = "conversion frequency fractional divider",
},
{
.name = "azoteq,rx-float-inactive",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 1,
.reg_shift = 6,
.reg_width = 1,
.invert = true,
},
{
.name = "azoteq,dead-time-enable",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 1,
.reg_shift = 5,
.reg_width = 1,
},
{
.name = "azoteq,tx-freq-fosc",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 1,
.reg_shift = 4,
.reg_width = 1,
},
{
.name = "azoteq,vbias-enable",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 1,
.reg_shift = 3,
.reg_width = 1,
},
{
.name = "azoteq,sense-mode",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 1,
.reg_shift = 0,
.reg_width = 3,
.val_max = 3,
.label = "sensing mode",
},
{
.name = "azoteq,iref-enable",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 2,
.reg_shift = 10,
.reg_width = 1,
},
{
.name = "azoteq,iref-level",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 2,
.reg_shift = 4,
.reg_width = 4,
.label = "current reference level",
},
{
.name = "azoteq,iref-trim",
.reg_grp = IQS7222_REG_GRP_CYCLE,
.reg_offset = 2,
.reg_shift = 0,
.reg_width = 4,
.label = "current reference trim",
},
{
.name = "azoteq,max-counts",
.reg_grp = IQS7222_REG_GRP_GLBL,
.reg_offset = 0,
.reg_shift = 13,
.reg_width = 2,
.label = "maximum counts",
},
{
.name = "azoteq,auto-mode",
.reg_grp = IQS7222_REG_GRP_GLBL,
.reg_offset = 0,
.reg_shift = 2,
.reg_width = 2,
.label = "number of conversions",
},
{
.name = "azoteq,ati-frac-div-fine",
.reg_grp = IQS7222_REG_GRP_GLBL,
.reg_offset = 1,
.reg_shift = 9,
.reg_width = 5,
.label = "ATI fine fractional divider",
},
{
.name = "azoteq,ati-frac-div-coarse",
.reg_grp = IQS7222_REG_GRP_GLBL,
.reg_offset = 1,
.reg_shift = 0,
.reg_width = 5,
.label = "ATI coarse fractional divider",
},
{
.name = "azoteq,ati-comp-select",
.reg_grp = IQS7222_REG_GRP_GLBL,
.reg_offset = 2,
.reg_shift = 0,
.reg_width = 10,
.label = "ATI compensation selection",
},
{
.name = "azoteq,ati-band",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 0,
.reg_shift = 12,
.reg_width = 2,
.label = "ATI band",
},
{
.name = "azoteq,global-halt",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 0,
.reg_shift = 11,
.reg_width = 1,
},
{
.name = "azoteq,invert-enable",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 0,
.reg_shift = 10,
.reg_width = 1,
},
{
.name = "azoteq,dual-direction",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 0,
.reg_shift = 9,
.reg_width = 1,
},
{
.name = "azoteq,samp-cap-double",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 0,
.reg_shift = 3,
.reg_width = 1,
},
{
.name = "azoteq,vref-half",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 0,
.reg_shift = 2,
.reg_width = 1,
},
{
.name = "azoteq,proj-bias",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 0,
.reg_shift = 0,
.reg_width = 2,
.label = "projected bias current",
},
{
.name = "azoteq,ati-target",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 1,
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 8,
.label = "ATI target",
},
{
.name = "azoteq,ati-base",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 1,
.reg_shift = 3,
.reg_width = 5,
.val_pitch = 16,
.label = "ATI base",
},
{
.name = "azoteq,ati-mode",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 1,
.reg_shift = 0,
.reg_width = 3,
.val_max = 5,
.label = "ATI mode",
},
{
.name = "azoteq,ati-frac-div-fine",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 2,
.reg_shift = 9,
.reg_width = 5,
.label = "ATI fine fractional divider",
},
{
.name = "azoteq,ati-frac-mult-coarse",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 2,
.reg_shift = 5,
.reg_width = 4,
.label = "ATI coarse fractional multiplier",
},
{
.name = "azoteq,ati-frac-div-coarse",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 2,
.reg_shift = 0,
.reg_width = 5,
.label = "ATI coarse fractional divider",
},
{
.name = "azoteq,ati-comp-div",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 3,
.reg_shift = 11,
.reg_width = 5,
.label = "ATI compensation divider",
},
{
.name = "azoteq,ati-comp-select",
.reg_grp = IQS7222_REG_GRP_CHAN,
.reg_offset = 3,
.reg_shift = 0,
.reg_width = 10,
.label = "ATI compensation selection",
},
{
.name = "azoteq,debounce-exit",
.reg_grp = IQS7222_REG_GRP_BTN,
.reg_key = IQS7222_REG_KEY_DEBOUNCE,
.reg_offset = 0,
.reg_shift = 12,
.reg_width = 4,
.label = "debounce exit factor",
},
{
.name = "azoteq,debounce-enter",
.reg_grp = IQS7222_REG_GRP_BTN,
.reg_key = IQS7222_REG_KEY_DEBOUNCE,
.reg_offset = 0,
.reg_shift = 8,
.reg_width = 4,
.label = "debounce entrance factor",
},
{
.name = "azoteq,thresh",
.reg_grp = IQS7222_REG_GRP_BTN,
.reg_key = IQS7222_REG_KEY_PROX,
.reg_offset = 0,
.reg_shift = 0,
.reg_width = 8,
.val_max = 127,
.label = "threshold",
},
{
.name = "azoteq,thresh",
.reg_grp = IQS7222_REG_GRP_BTN,
.reg_key = IQS7222_REG_KEY_TOUCH,
.reg_offset = 1,
.reg_shift = 0,
.reg_width = 8,
.label = "threshold",
},
{
.name = "azoteq,hyst",
.reg_grp = IQS7222_REG_GRP_BTN,
.reg_key = IQS7222_REG_KEY_TOUCH,
.reg_offset = 1,
.reg_shift = 8,
.reg_width = 8,
.label = "hysteresis",
},
{
.name = "azoteq,lta-beta-lp",
.reg_grp = IQS7222_REG_GRP_FILT,
.reg_offset = 0,
.reg_shift = 12,
.reg_width = 4,
.label = "low-power mode long-term average beta",
},
{
.name = "azoteq,lta-beta-np",
.reg_grp = IQS7222_REG_GRP_FILT,
.reg_offset = 0,
.reg_shift = 8,
.reg_width = 4,
.label = "normal-power mode long-term average beta",
},
{
.name = "azoteq,counts-beta-lp",
.reg_grp = IQS7222_REG_GRP_FILT,
.reg_offset = 0,
.reg_shift = 4,
.reg_width = 4,
.label = "low-power mode counts beta",
},
{
.name = "azoteq,counts-beta-np",
.reg_grp = IQS7222_REG_GRP_FILT,
.reg_offset = 0,
.reg_shift = 0,
.reg_width = 4,
.label = "normal-power mode counts beta",
},
{
.name = "azoteq,lta-fast-beta-lp",
.reg_grp = IQS7222_REG_GRP_FILT,
.reg_offset = 1,
.reg_shift = 4,
.reg_width = 4,
.label = "low-power mode long-term average fast beta",
},
{
.name = "azoteq,lta-fast-beta-np",
.reg_grp = IQS7222_REG_GRP_FILT,
.reg_offset = 1,
.reg_shift = 0,
.reg_width = 4,
.label = "normal-power mode long-term average fast beta",
},
{
.name = "azoteq,lower-cal",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_offset = 0,
.reg_shift = 8,
.reg_width = 8,
.label = "lower calibration",
},
{
.name = "azoteq,static-beta",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_NO_WHEEL,
.reg_offset = 0,
.reg_shift = 6,
.reg_width = 1,
},
{
.name = "azoteq,bottom-beta",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_NO_WHEEL,
.reg_offset = 0,
.reg_shift = 3,
.reg_width = 3,
.label = "bottom beta",
},
{
.name = "azoteq,static-beta",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_WHEEL,
.reg_offset = 0,
.reg_shift = 7,
.reg_width = 1,
},
{
.name = "azoteq,bottom-beta",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_WHEEL,
.reg_offset = 0,
.reg_shift = 4,
.reg_width = 3,
.label = "bottom beta",
},
{
.name = "azoteq,bottom-speed",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_offset = 1,
.reg_shift = 8,
.reg_width = 8,
.label = "bottom speed",
},
{
.name = "azoteq,upper-cal",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_offset = 1,
.reg_shift = 0,
.reg_width = 8,
.label = "upper calibration",
},
{
.name = "azoteq,gesture-max-ms",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_TAP,
.reg_offset = 9,
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 16,
.label = "maximum gesture time",
},
{
.name = "azoteq,gesture-max-ms",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_TAP_LEGACY,
.reg_offset = 9,
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 4,
.label = "maximum gesture time",
},
{
.name = "azoteq,gesture-min-ms",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_TAP,
.reg_offset = 9,
.reg_shift = 3,
.reg_width = 5,
.val_pitch = 16,
.label = "minimum gesture time",
},
{
.name = "azoteq,gesture-min-ms",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_TAP_LEGACY,
.reg_offset = 9,
.reg_shift = 3,
.reg_width = 5,
.val_pitch = 4,
.label = "minimum gesture time",
},
{
.name = "azoteq,gesture-dist",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_AXIAL,
.reg_offset = 10,
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 16,
.label = "gesture distance",
},
{
.name = "azoteq,gesture-dist",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_AXIAL_LEGACY,
.reg_offset = 10,
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 16,
.label = "gesture distance",
},
{
.name = "azoteq,gesture-max-ms",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_AXIAL,
.reg_offset = 10,
.reg_shift = 0,
.reg_width = 8,
.val_pitch = 16,
.label = "maximum gesture time",
},
{
.name = "azoteq,gesture-max-ms",
.reg_grp = IQS7222_REG_GRP_SLDR,
.reg_key = IQS7222_REG_KEY_AXIAL_LEGACY,
.reg_offset = 10,
.reg_shift = 0,
.reg_width = 8,
.val_pitch = 4,
.label = "maximum gesture time",
},
{
.name = "azoteq,num-rows",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_offset = 0,
.reg_shift = 4,
.reg_width = 4,
.val_min = 1,
.val_max = 12,
.label = "number of rows",
},
{
.name = "azoteq,num-cols",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_offset = 0,
.reg_shift = 0,
.reg_width = 4,
.val_min = 1,
.val_max = 12,
.label = "number of columns",
},
{
.name = "azoteq,lower-cal-y",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_offset = 1,
.reg_shift = 8,
.reg_width = 8,
.label = "lower vertical calibration",
},
{
.name = "azoteq,lower-cal-x",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_offset = 1,
.reg_shift = 0,
.reg_width = 8,
.label = "lower horizontal calibration",
},
{
.name = "azoteq,upper-cal-y",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_offset = 2,
.reg_shift = 8,
.reg_width = 8,
.label = "upper vertical calibration",
},
{
.name = "azoteq,upper-cal-x",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_offset = 2,
.reg_shift = 0,
.reg_width = 8,
.label = "upper horizontal calibration",
},
{
.name = "azoteq,top-speed",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_offset = 3,
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 4,
.label = "top speed",
},
{
.name = "azoteq,bottom-speed",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_offset = 3,
.reg_shift = 0,
.reg_width = 8,
.label = "bottom speed",
},
{
.name = "azoteq,gesture-min-ms",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_key = IQS7222_REG_KEY_TAP,
.reg_offset = 20,
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 16,
.label = "minimum gesture time",
},
{
.name = "azoteq,gesture-max-ms",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_key = IQS7222_REG_KEY_AXIAL,
.reg_offset = 21,
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 16,
.label = "maximum gesture time",
},
{
.name = "azoteq,gesture-max-ms",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_key = IQS7222_REG_KEY_TAP,
.reg_offset = 21,
.reg_shift = 0,
.reg_width = 8,
.val_pitch = 16,
.label = "maximum gesture time",
},
{
.name = "azoteq,gesture-dist",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_key = IQS7222_REG_KEY_TAP,
.reg_offset = 22,
.reg_shift = 0,
.reg_width = 16,
.label = "gesture distance",
},
{
.name = "azoteq,gesture-dist",
.reg_grp = IQS7222_REG_GRP_TPAD,
.reg_key = IQS7222_REG_KEY_AXIAL,
.reg_offset = 23,
.reg_shift = 0,
.reg_width = 16,
.label = "gesture distance",
},
{
.name = "drive-open-drain",
.reg_grp = IQS7222_REG_GRP_GPIO,
.reg_offset = 0,
.reg_shift = 1,
.reg_width = 1,
},
{
.name = "azoteq,timeout-ati-ms",
.reg_grp = IQS7222_REG_GRP_SYS,
.reg_offset = 1,
.reg_shift = 0,
.reg_width = 16,
.val_pitch = 500,
.label = "ATI error timeout",
},
{
.name = "azoteq,rate-ati-ms",
.reg_grp = IQS7222_REG_GRP_SYS,
.reg_offset = 2,
.reg_shift = 0,
.reg_width = 16,
.label = "ATI report rate",
},
{
.name = "azoteq,timeout-np-ms",
.reg_grp = IQS7222_REG_GRP_SYS,
.reg_offset = 3,
.reg_shift = 0,
.reg_width = 16,
.label = "normal-power mode timeout",
},
{
.name = "azoteq,rate-np-ms",
.reg_grp = IQS7222_REG_GRP_SYS,
.reg_offset = 4,
.reg_shift = 0,
.reg_width = 16,
.val_max = 3000,
.label = "normal-power mode report rate",
},
{
.name = "azoteq,timeout-lp-ms",
.reg_grp = IQS7222_REG_GRP_SYS,
.reg_offset = 5,
.reg_shift = 0,
.reg_width = 16,
.label = "low-power mode timeout",
},
{
.name = "azoteq,rate-lp-ms",
.reg_grp = IQS7222_REG_GRP_SYS,
.reg_offset = 6,
.reg_shift = 0,
.reg_width = 16,
.val_max = 3000,
.label = "low-power mode report rate",
},
{
.name = "azoteq,timeout-ulp-ms",
.reg_grp = IQS7222_REG_GRP_SYS,
.reg_offset = 7,
.reg_shift = 0,
.reg_width = 16,
.label = "ultra-low-power mode timeout",
},
{
.name = "azoteq,rate-ulp-ms",
.reg_grp = IQS7222_REG_GRP_SYS,
.reg_offset = 8,
.reg_shift = 0,
.reg_width = 16,
.val_max = 3000,
.label = "ultra-low-power mode report rate",
},
};
struct iqs7222_private {
const struct iqs7222_dev_desc *dev_desc;
struct gpio_desc *reset_gpio;
struct gpio_desc *irq_gpio;
struct i2c_client *client;
struct input_dev *keypad;
struct touchscreen_properties prop;
unsigned int kp_type[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)];
unsigned int kp_code[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)];
unsigned int sl_code[IQS7222_MAX_SLDR][ARRAY_SIZE(iqs7222_sl_events)];
unsigned int sl_axis[IQS7222_MAX_SLDR];
unsigned int tp_code[ARRAY_SIZE(iqs7222_tp_events)];
u16 cycle_setup[IQS7222_MAX_CHAN / 2][IQS7222_MAX_COLS_CYCLE];
u16 glbl_setup[IQS7222_MAX_COLS_GLBL];
u16 btn_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_BTN];
u16 chan_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_CHAN];
u16 filt_setup[IQS7222_MAX_COLS_FILT];
u16 sldr_setup[IQS7222_MAX_SLDR][IQS7222_MAX_COLS_SLDR];
u16 tpad_setup[IQS7222_MAX_COLS_TPAD];
u16 gpio_setup[ARRAY_SIZE(iqs7222_gpio_links)][IQS7222_MAX_COLS_GPIO];
u16 sys_setup[IQS7222_MAX_COLS_SYS];
};
static u16 *iqs7222_setup(struct iqs7222_private *iqs7222,
enum iqs7222_reg_grp_id reg_grp, int row)
{
switch (reg_grp) {
case IQS7222_REG_GRP_CYCLE:
return iqs7222->cycle_setup[row];
case IQS7222_REG_GRP_GLBL:
return iqs7222->glbl_setup;
case IQS7222_REG_GRP_BTN:
return iqs7222->btn_setup[row];
case IQS7222_REG_GRP_CHAN:
return iqs7222->chan_setup[row];
case IQS7222_REG_GRP_FILT:
return iqs7222->filt_setup;
case IQS7222_REG_GRP_SLDR:
return iqs7222->sldr_setup[row];
case IQS7222_REG_GRP_TPAD:
return iqs7222->tpad_setup;
case IQS7222_REG_GRP_GPIO:
return iqs7222->gpio_setup[row];
case IQS7222_REG_GRP_SYS:
return iqs7222->sys_setup;
default:
return NULL;
}
}
static int iqs7222_irq_poll(struct iqs7222_private *iqs7222, u16 timeout_ms)
{
ktime_t irq_timeout = ktime_add_ms(ktime_get(), timeout_ms);
int ret;
do {
usleep_range(1000, 1100);
ret = gpiod_get_value_cansleep(iqs7222->irq_gpio);
if (ret < 0)
return ret;
else if (ret > 0)
return 0;
} while (ktime_compare(ktime_get(), irq_timeout) < 0);
return -EBUSY;
}
static int iqs7222_hard_reset(struct iqs7222_private *iqs7222)
{
struct i2c_client *client = iqs7222->client;
int error;
if (!iqs7222->reset_gpio)
return 0;
gpiod_set_value_cansleep(iqs7222->reset_gpio, 1);
usleep_range(1000, 1100);
gpiod_set_value_cansleep(iqs7222->reset_gpio, 0);
error = iqs7222_irq_poll(iqs7222, IQS7222_RESET_TIMEOUT_MS);
if (error)
dev_err(&client->dev, "Failed to reset device: %d\n", error);
return error;
}
static int iqs7222_force_comms(struct iqs7222_private *iqs7222)
{
u8 msg_buf[] = { 0xFF, };
int ret;
/*
* The device cannot communicate until it asserts its interrupt (RDY)
* pin. Attempts to do so while RDY is deasserted return an ACK; how-
* ever all write data is ignored, and all read data returns 0xEE.
*
* Unsolicited communication must be preceded by a special force com-
* munication command, after which the device eventually asserts its
* RDY pin and agrees to communicate.
*
* Regardless of whether communication is forced or the result of an
* interrupt, the device automatically deasserts its RDY pin once it
* detects an I2C stop condition, or a timeout expires.
*/
ret = gpiod_get_value_cansleep(iqs7222->irq_gpio);
if (ret < 0)
return ret;
else if (ret > 0)
return 0;
ret = i2c_master_send(iqs7222->client, msg_buf, sizeof(msg_buf));
if (ret < (int)sizeof(msg_buf)) {
if (ret >= 0)
ret = -EIO;
/*
* The datasheet states that the host must wait to retry any
* failed attempt to communicate over I2C.
*/
msleep(IQS7222_COMMS_RETRY_MS);
return ret;
}
return iqs7222_irq_poll(iqs7222, IQS7222_COMMS_TIMEOUT_MS);
}
static int iqs7222_read_burst(struct iqs7222_private *iqs7222,
u16 reg, void *val, u16 num_val)
{
u8 reg_buf[sizeof(__be16)];
int ret, i;
struct i2c_client *client = iqs7222->client;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = reg > U8_MAX ? sizeof(reg) : sizeof(u8),
.buf = reg_buf,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = num_val * sizeof(__le16),
.buf = (u8 *)val,
},
};
if (reg > U8_MAX)
put_unaligned_be16(reg, reg_buf);
else
*reg_buf = (u8)reg;
/*
* The following loop protects against an edge case in which the RDY
* pin is automatically deasserted just as the read is initiated. In
* that case, the read must be retried using forced communication.
*/
for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
ret = iqs7222_force_comms(iqs7222);
if (ret < 0)
continue;
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ret < (int)ARRAY_SIZE(msg)) {
if (ret >= 0)
ret = -EIO;
msleep(IQS7222_COMMS_RETRY_MS);
continue;
}
if (get_unaligned_le16(msg[1].buf) == IQS7222_COMMS_ERROR) {
ret = -ENODATA;
continue;
}
ret = 0;
break;
}
/*
* The following delay ensures the device has deasserted the RDY pin
* following the I2C stop condition.
*/
usleep_range(50, 100);
if (ret < 0)
dev_err(&client->dev,
"Failed to read from address 0x%04X: %d\n", reg, ret);
return ret;
}
static int iqs7222_read_word(struct iqs7222_private *iqs7222, u16 reg, u16 *val)
{
__le16 val_buf;
int error;
error = iqs7222_read_burst(iqs7222, reg, &val_buf, 1);
if (error)
return error;
*val = le16_to_cpu(val_buf);
return 0;
}
static int iqs7222_write_burst(struct iqs7222_private *iqs7222,
u16 reg, const void *val, u16 num_val)
{
int reg_len = reg > U8_MAX ? sizeof(reg) : sizeof(u8);
int val_len = num_val * sizeof(__le16);
int msg_len = reg_len + val_len;
int ret, i;
struct i2c_client *client = iqs7222->client;
u8 *msg_buf;
msg_buf = kzalloc(msg_len, GFP_KERNEL);
if (!msg_buf)
return -ENOMEM;
if (reg > U8_MAX)
put_unaligned_be16(reg, msg_buf);
else
*msg_buf = (u8)reg;
memcpy(msg_buf + reg_len, val, val_len);
/*
* The following loop protects against an edge case in which the RDY
* pin is automatically asserted just before the force communication
* command is sent.
*
* In that case, the subsequent I2C stop condition tricks the device
* into preemptively deasserting the RDY pin and the command must be
* sent again.
*/
for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
ret = iqs7222_force_comms(iqs7222);
if (ret < 0)
continue;
ret = i2c_master_send(client, msg_buf, msg_len);
if (ret < msg_len) {
if (ret >= 0)
ret = -EIO;
msleep(IQS7222_COMMS_RETRY_MS);
continue;
}
ret = 0;
break;
}
kfree(msg_buf);
usleep_range(50, 100);
if (ret < 0)
dev_err(&client->dev,
"Failed to write to address 0x%04X: %d\n", reg, ret);
return ret;
}
static int iqs7222_write_word(struct iqs7222_private *iqs7222, u16 reg, u16 val)
{
__le16 val_buf = cpu_to_le16(val);
return iqs7222_write_burst(iqs7222, reg, &val_buf, 1);
}
static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222)
{
struct i2c_client *client = iqs7222->client;
ktime_t ati_timeout;
u16 sys_status = 0;
u16 sys_setup;
int error, i;
/*
* The reserved fields of the system setup register may have changed
* as a result of other registers having been written. As such, read
* the register's latest value to avoid unexpected behavior when the
* register is written in the loop that follows.
*/
error = iqs7222_read_word(iqs7222, IQS7222_SYS_SETUP, &sys_setup);
if (error)
return error;
for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
/*
* Trigger ATI from streaming and normal-power modes so that
* the RDY pin continues to be asserted during ATI.
*/
error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
sys_setup |
IQS7222_SYS_SETUP_REDO_ATI);
if (error)
return error;
ati_timeout = ktime_add_ms(ktime_get(), IQS7222_ATI_TIMEOUT_MS);
do {
error = iqs7222_irq_poll(iqs7222,
IQS7222_COMMS_TIMEOUT_MS);
if (error)
continue;
error = iqs7222_read_word(iqs7222, IQS7222_SYS_STATUS,
&sys_status);
if (error)
return error;
if (sys_status & IQS7222_SYS_STATUS_RESET)
return 0;
if (sys_status & IQS7222_SYS_STATUS_ATI_ERROR)
break;
if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE)
continue;
/*
* Use stream-in-touch mode if either slider reports
* absolute position.
*/
sys_setup |= test_bit(EV_ABS, iqs7222->keypad->evbit)
? IQS7222_SYS_SETUP_INTF_MODE_TOUCH
: IQS7222_SYS_SETUP_INTF_MODE_EVENT;
sys_setup |= IQS7222_SYS_SETUP_PWR_MODE_AUTO;
return iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
sys_setup);
} while (ktime_compare(ktime_get(), ati_timeout) < 0);
dev_err(&client->dev,
"ATI attempt %d of %d failed with status 0x%02X, %s\n",
i + 1, IQS7222_NUM_RETRIES, (u8)sys_status,
i + 1 < IQS7222_NUM_RETRIES ? "retrying" : "stopping");
}
return -ETIMEDOUT;
}
static int iqs7222_dev_init(struct iqs7222_private *iqs7222, int dir)
{
const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
int comms_offset = dev_desc->comms_offset;
int error, i, j, k;
/*
* Acknowledge reset before writing any registers in case the device
* suffers a spurious reset during initialization. Because this step
* may change the reserved fields of the second filter beta register,
* its cache must be updated.
*
* Writing the second filter beta register, in turn, may clobber the
* system status register. As such, the filter beta register pair is
* written first to protect against this hazard.
*/
if (dir == WRITE) {
u16 reg = dev_desc->reg_grps[IQS7222_REG_GRP_FILT].base + 1;
u16 filt_setup;
error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
iqs7222->sys_setup[0] |
IQS7222_SYS_SETUP_ACK_RESET);
if (error)
return error;
error = iqs7222_read_word(iqs7222, reg, &filt_setup);
if (error)
return error;
iqs7222->filt_setup[1] &= GENMASK(7, 0);
iqs7222->filt_setup[1] |= (filt_setup & ~GENMASK(7, 0));
}
/*
* Take advantage of the stop-bit disable function, if available, to
* save the trouble of having to reopen a communication window after
* each burst read or write.
*/
if (comms_offset) {
u16 comms_setup;
error = iqs7222_read_word(iqs7222,
IQS7222_SYS_SETUP + comms_offset,
&comms_setup);
if (error)
return error;
error = iqs7222_write_word(iqs7222,
IQS7222_SYS_SETUP + comms_offset,
comms_setup | IQS7222_COMMS_HOLD);
if (error)
return error;
}
for (i = 0; i < IQS7222_NUM_REG_GRPS; i++) {
int num_row = dev_desc->reg_grps[i].num_row;
int num_col = dev_desc->reg_grps[i].num_col;
u16 reg = dev_desc->reg_grps[i].base;
__le16 *val_buf;
u16 *val;
if (!num_col)
continue;
val = iqs7222_setup(iqs7222, i, 0);
if (!val)
continue;
val_buf = kcalloc(num_col, sizeof(__le16), GFP_KERNEL);
if (!val_buf)
return -ENOMEM;
for (j = 0; j < num_row; j++) {
switch (dir) {
case READ:
error = iqs7222_read_burst(iqs7222, reg,
val_buf, num_col);
for (k = 0; k < num_col; k++)
val[k] = le16_to_cpu(val_buf[k]);
break;
case WRITE:
for (k = 0; k < num_col; k++)
val_buf[k] = cpu_to_le16(val[k]);
error = iqs7222_write_burst(iqs7222, reg,
val_buf, num_col);
break;
default:
error = -EINVAL;
}
if (error)
break;
reg += IQS7222_REG_OFFSET;
val += iqs7222_max_cols[i];
}
kfree(val_buf);
if (error)
return error;
}
if (comms_offset) {
u16 comms_setup;
error = iqs7222_read_word(iqs7222,
IQS7222_SYS_SETUP + comms_offset,
&comms_setup);
if (error)
return error;
error = iqs7222_write_word(iqs7222,
IQS7222_SYS_SETUP + comms_offset,
comms_setup & ~IQS7222_COMMS_HOLD);
if (error)
return error;
}
if (dir == READ) {
iqs7222->sys_setup[0] &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK;
iqs7222->sys_setup[0] &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK;
return 0;
}
return iqs7222_ati_trigger(iqs7222);
}
static int iqs7222_dev_info(struct iqs7222_private *iqs7222)
{
struct i2c_client *client = iqs7222->client;
bool prod_num_valid = false;
__le16 dev_id[3];
int error, i;
error = iqs7222_read_burst(iqs7222, IQS7222_PROD_NUM, dev_id,
ARRAY_SIZE(dev_id));
if (error)
return error;
for (i = 0; i < ARRAY_SIZE(iqs7222_devs); i++) {
if (le16_to_cpu(dev_id[0]) != iqs7222_devs[i].prod_num)
continue;
prod_num_valid = true;
if (le16_to_cpu(dev_id[1]) < iqs7222_devs[i].fw_major)
continue;
if (le16_to_cpu(dev_id[2]) < iqs7222_devs[i].fw_minor)
continue;
iqs7222->dev_desc = &iqs7222_devs[i];
return 0;
}
if (prod_num_valid)
dev_err(&client->dev, "Unsupported firmware revision: %u.%u\n",
le16_to_cpu(dev_id[1]), le16_to_cpu(dev_id[2]));
else
dev_err(&client->dev, "Unrecognized product number: %u\n",
le16_to_cpu(dev_id[0]));
return -EINVAL;
}
static int iqs7222_gpio_select(struct iqs7222_private *iqs7222,
struct fwnode_handle *child_node,
int child_enable, u16 child_link)
{
const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
struct i2c_client *client = iqs7222->client;
int num_gpio = dev_desc->reg_grps[IQS7222_REG_GRP_GPIO].num_row;
int error, count, i;
unsigned int gpio_sel[ARRAY_SIZE(iqs7222_gpio_links)];
if (!num_gpio)
return 0;
if (!fwnode_property_present(child_node, "azoteq,gpio-select"))
return 0;
count = fwnode_property_count_u32(child_node, "azoteq,gpio-select");
if (count > num_gpio) {
dev_err(&client->dev, "Invalid number of %s GPIOs\n",
fwnode_get_name(child_node));
return -EINVAL;
} else if (count < 0) {
dev_err(&client->dev, "Failed to count %s GPIOs: %d\n",
fwnode_get_name(child_node), count);
return count;
}
error = fwnode_property_read_u32_array(child_node,
"azoteq,gpio-select",
gpio_sel, count);
if (error) {
dev_err(&client->dev, "Failed to read %s GPIOs: %d\n",
fwnode_get_name(child_node), error);
return error;
}
for (i = 0; i < count; i++) {
u16 *gpio_setup;
if (gpio_sel[i] >= num_gpio) {
dev_err(&client->dev, "Invalid %s GPIO: %u\n",
fwnode_get_name(child_node), gpio_sel[i]);
return -EINVAL;
}
gpio_setup = iqs7222->gpio_setup[gpio_sel[i]];
if (gpio_setup[2] && child_link != gpio_setup[2]) {
dev_err(&client->dev,
"Conflicting GPIO %u event types\n",
gpio_sel[i]);
return -EINVAL;
}
gpio_setup[0] |= IQS7222_GPIO_SETUP_0_GPIO_EN;
gpio_setup[1] |= child_enable;
gpio_setup[2] = child_link;
}
return 0;
}
static int iqs7222_parse_props(struct iqs7222_private *iqs7222,
struct fwnode_handle *reg_grp_node,
int reg_grp_index,
enum iqs7222_reg_grp_id reg_grp,
enum iqs7222_reg_key_id reg_key)
{
u16 *setup = iqs7222_setup(iqs7222, reg_grp, reg_grp_index);
struct i2c_client *client = iqs7222->client;
int i;
if (!setup)
return 0;
for (i = 0; i < ARRAY_SIZE(iqs7222_props); i++) {
const char *name = iqs7222_props[i].name;
int reg_offset = iqs7222_props[i].reg_offset;
int reg_shift = iqs7222_props[i].reg_shift;
int reg_width = iqs7222_props[i].reg_width;
int val_pitch = iqs7222_props[i].val_pitch ? : 1;
int val_min = iqs7222_props[i].val_min;
int val_max = iqs7222_props[i].val_max;
bool invert = iqs7222_props[i].invert;
const char *label = iqs7222_props[i].label ? : name;
unsigned int val;
int error;
if (iqs7222_props[i].reg_grp != reg_grp ||
iqs7222_props[i].reg_key != reg_key)
continue;
/*
* Boolean register fields are one bit wide; they are forcibly
* reset to provide a means to undo changes by a bootloader if
* necessary.
*
* Scalar fields, on the other hand, are left untouched unless
* their corresponding properties are present.
*/
if (reg_width == 1) {
if (invert)
setup[reg_offset] |= BIT(reg_shift);
else
setup[reg_offset] &= ~BIT(reg_shift);
}
if (!fwnode_property_present(reg_grp_node, name))
continue;
if (reg_width == 1) {
if (invert)
setup[reg_offset] &= ~BIT(reg_shift);
else
setup[reg_offset] |= BIT(reg_shift);
continue;
}
error = fwnode_property_read_u32(reg_grp_node, name, &val);
if (error) {
dev_err(&client->dev, "Failed to read %s %s: %d\n",
fwnode_get_name(reg_grp_node), label, error);
return error;
}
if (!val_max)
val_max = GENMASK(reg_width - 1, 0) * val_pitch;
if (val < val_min || val > val_max) {
dev_err(&client->dev, "Invalid %s %s: %u\n",
fwnode_get_name(reg_grp_node), label, val);
return -EINVAL;
}
setup[reg_offset] &= ~GENMASK(reg_shift + reg_width - 1,
reg_shift);
setup[reg_offset] |= (val / val_pitch << reg_shift);
}
return 0;
}
static int iqs7222_parse_event(struct iqs7222_private *iqs7222,
struct fwnode_handle *event_node,
int reg_grp_index,
enum iqs7222_reg_grp_id reg_grp,
enum iqs7222_reg_key_id reg_key,
u16 event_enable, u16 event_link,
unsigned int *event_type,
unsigned int *event_code)
{
struct i2c_client *client = iqs7222->client;
int error;
error = iqs7222_parse_props(iqs7222, event_node, reg_grp_index,
reg_grp, reg_key);
if (error)
return error;
error = iqs7222_gpio_select(iqs7222, event_node, event_enable,
event_link);
if (error)
return error;
error = fwnode_property_read_u32(event_node, "linux,code", event_code);
if (error == -EINVAL) {
return 0;
} else if (error) {
dev_err(&client->dev, "Failed to read %s code: %d\n",
fwnode_get_name(event_node), error);
return error;
}
if (!event_type) {
input_set_capability(iqs7222->keypad, EV_KEY, *event_code);
return 0;
}
error = fwnode_property_read_u32(event_node, "linux,input-type",
event_type);
if (error == -EINVAL) {
*event_type = EV_KEY;
} else if (error) {
dev_err(&client->dev, "Failed to read %s input type: %d\n",
fwnode_get_name(event_node), error);
return error;
} else if (*event_type != EV_KEY && *event_type != EV_SW) {
dev_err(&client->dev, "Invalid %s input type: %d\n",
fwnode_get_name(event_node), *event_type);
return -EINVAL;
}
input_set_capability(iqs7222->keypad, *event_type, *event_code);
return 0;
}
static int iqs7222_parse_cycle(struct iqs7222_private *iqs7222,
struct fwnode_handle *cycle_node, int cycle_index)
{
u16 *cycle_setup = iqs7222->cycle_setup[cycle_index];
struct i2c_client *client = iqs7222->client;
unsigned int pins[9];
int error, count, i;
/*
* Each channel shares a cycle with one other channel; the mapping of
* channels to cycles is fixed. Properties defined for a cycle impact
* both channels tied to the cycle.
*
* Unlike channels which are restricted to a select range of CRx pins
* based on channel number, any cycle can claim any of the device's 9
* CTx pins (CTx0-8).
*/
if (!fwnode_property_present(cycle_node, "azoteq,tx-enable"))
return 0;
count = fwnode_property_count_u32(cycle_node, "azoteq,tx-enable");
if (count < 0) {
dev_err(&client->dev, "Failed to count %s CTx pins: %d\n",
fwnode_get_name(cycle_node), count);
return count;
} else if (count > ARRAY_SIZE(pins)) {
dev_err(&client->dev, "Invalid number of %s CTx pins\n",
fwnode_get_name(cycle_node));
return -EINVAL;
}
error = fwnode_property_read_u32_array(cycle_node, "azoteq,tx-enable",
pins, count);
if (error) {
dev_err(&client->dev, "Failed to read %s CTx pins: %d\n",
fwnode_get_name(cycle_node), error);
return error;
}
cycle_setup[1] &= ~GENMASK(7 + ARRAY_SIZE(pins) - 1, 7);
for (i = 0; i < count; i++) {
if (pins[i] > 8) {
dev_err(&client->dev, "Invalid %s CTx pin: %u\n",
fwnode_get_name(cycle_node), pins[i]);
return -EINVAL;
}
cycle_setup[1] |= BIT(pins[i] + 7);
}
return 0;
}
static int iqs7222_parse_chan(struct iqs7222_private *iqs7222,
struct fwnode_handle *chan_node, int chan_index)
{
const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
struct i2c_client *client = iqs7222->client;
int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
int ext_chan = rounddown(num_chan, 10);
int error, i;
u16 *chan_setup = iqs7222->chan_setup[chan_index];
u16 *sys_setup = iqs7222->sys_setup;
unsigned int val;
if (dev_desc->allow_offset &&
fwnode_property_present(chan_node, "azoteq,ulp-allow"))
sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
chan_setup[0] |= IQS7222_CHAN_SETUP_0_CHAN_EN;
/*
* The reference channel function allows for differential measurements
* and is only available in the case of IQS7222A or IQS7222C.
*/
if (dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_col > 4 &&
fwnode_property_present(chan_node, "azoteq,ref-select")) {
u16 *ref_setup;
error = fwnode_property_read_u32(chan_node, "azoteq,ref-select",
&val);
if (error) {
dev_err(&client->dev,
"Failed to read %s reference channel: %d\n",
fwnode_get_name(chan_node), error);
return error;
}
if (val >= ext_chan) {
dev_err(&client->dev,
"Invalid %s reference channel: %u\n",
fwnode_get_name(chan_node), val);
return -EINVAL;
}
ref_setup = iqs7222->chan_setup[val];
/*
* Configure the current channel as a follower of the selected
* reference channel.
*/
chan_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_FOLLOW;
chan_setup[4] = val * 42 + 1048;
error = fwnode_property_read_u32(chan_node, "azoteq,ref-weight",
&val);
if (!error) {
if (val > U16_MAX) {
dev_err(&client->dev,
"Invalid %s reference weight: %u\n",
fwnode_get_name(chan_node), val);
return -EINVAL;
}
chan_setup[5] = val;
} else if (error != -EINVAL) {
dev_err(&client->dev,
"Failed to read %s reference weight: %d\n",
fwnode_get_name(chan_node), error);
return error;
}
/*
* Configure the selected channel as a reference channel which
* serves the current channel.
*/
ref_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_REF;
ref_setup[5] |= BIT(chan_index);
ref_setup[4] = dev_desc->touch_link;
if (fwnode_property_present(chan_node, "azoteq,use-prox"))
ref_setup[4] -= 2;
} else if (dev_desc->reg_grps[IQS7222_REG_GRP_TPAD].num_row &&
fwnode_property_present(chan_node,
"azoteq,counts-filt-enable")) {
/*
* In the case of IQS7222D, however, the reference mode field
* is partially repurposed as a counts filter enable control.
*/
chan_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_REF;
}
if (fwnode_property_present(chan_node, "azoteq,rx-enable")) {
/*
* Each channel can claim up to 4 CRx pins. The first half of
* the channels can use CRx0-3, while the second half can use
* CRx4-7.
*/
unsigned int pins[4];
int count;
count = fwnode_property_count_u32(chan_node,
"azoteq,rx-enable");
if (count < 0) {
dev_err(&client->dev,
"Failed to count %s CRx pins: %d\n",
fwnode_get_name(chan_node), count);
return count;
} else if (count > ARRAY_SIZE(pins)) {
dev_err(&client->dev,
"Invalid number of %s CRx pins\n",
fwnode_get_name(chan_node));
return -EINVAL;
}
error = fwnode_property_read_u32_array(chan_node,
"azoteq,rx-enable",
pins, count);
if (error) {
dev_err(&client->dev,
"Failed to read %s CRx pins: %d\n",
fwnode_get_name(chan_node), error);
return error;
}
chan_setup[0] &= ~GENMASK(4 + ARRAY_SIZE(pins) - 1, 4);
for (i = 0; i < count; i++) {
int min_crx = chan_index < ext_chan / 2 ? 0 : 4;
if (pins[i] < min_crx || pins[i] > min_crx + 3) {
dev_err(&client->dev,
"Invalid %s CRx pin: %u\n",
fwnode_get_name(chan_node), pins[i]);
return -EINVAL;
}
chan_setup[0] |= BIT(pins[i] + 4 - min_crx);
}
}
for (i = 0; i < ARRAY_SIZE(iqs7222_kp_events); i++) {
const char *event_name = iqs7222_kp_events[i].name;
u16 event_enable = iqs7222_kp_events[i].enable;
struct fwnode_handle *event_node;
event_node = fwnode_get_named_child_node(chan_node, event_name);
if (!event_node)
continue;
error = fwnode_property_read_u32(event_node,
"azoteq,timeout-press-ms",
&val);
if (!error) {
/*
* The IQS7222B employs a global pair of press timeout
* registers as opposed to channel-specific registers.
*/
u16 *setup = dev_desc->reg_grps
[IQS7222_REG_GRP_BTN].num_col > 2 ?
&iqs7222->btn_setup[chan_index][2] :
&sys_setup[9];
if (val > U8_MAX * 500) {
dev_err(&client->dev,
"Invalid %s press timeout: %u\n",
fwnode_get_name(event_node), val);
fwnode_handle_put(event_node);
return -EINVAL;
}
*setup &= ~(U8_MAX << i * 8);
*setup |= (val / 500 << i * 8);
} else if (error != -EINVAL) {
dev_err(&client->dev,
"Failed to read %s press timeout: %d\n",
fwnode_get_name(event_node), error);
fwnode_handle_put(event_node);
return error;
}
error = iqs7222_parse_event(iqs7222, event_node, chan_index,
IQS7222_REG_GRP_BTN,
iqs7222_kp_events[i].reg_key,
BIT(chan_index),
dev_desc->touch_link - (i ? 0 : 2),
&iqs7222->kp_type[chan_index][i],
&iqs7222->kp_code[chan_index][i]);
fwnode_handle_put(event_node);
if (error)
return error;
if (!dev_desc->event_offset)
continue;
sys_setup[dev_desc->event_offset] |= event_enable;
}
/*
* The following call handles a special pair of properties that apply
* to a channel node, but reside within the button (event) group.
*/
return iqs7222_parse_props(iqs7222, chan_node, chan_index,
IQS7222_REG_GRP_BTN,
IQS7222_REG_KEY_DEBOUNCE);
}
static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222,
struct fwnode_handle *sldr_node, int sldr_index)
{
const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
struct i2c_client *client = iqs7222->client;
int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
int ext_chan = rounddown(num_chan, 10);
int count, error, reg_offset, i;
u16 *event_mask = &iqs7222->sys_setup[dev_desc->event_offset];
u16 *sldr_setup = iqs7222->sldr_setup[sldr_index];
unsigned int chan_sel[4], val;
/*
* Each slider can be spread across 3 to 4 channels. It is possible to
* select only 2 channels, but doing so prevents the slider from using
* the specified resolution.
*/
count = fwnode_property_count_u32(sldr_node, "azoteq,channel-select");
if (count < 0) {
dev_err(&client->dev, "Failed to count %s channels: %d\n",
fwnode_get_name(sldr_node), count);
return count;
} else if (count < 3 || count > ARRAY_SIZE(chan_sel)) {
dev_err(&client->dev, "Invalid number of %s channels\n",
fwnode_get_name(sldr_node));
return -EINVAL;
}
error = fwnode_property_read_u32_array(sldr_node,
"azoteq,channel-select",
chan_sel, count);
if (error) {
dev_err(&client->dev, "Failed to read %s channels: %d\n",
fwnode_get_name(sldr_node), error);
return error;
}
/*
* Resolution and top speed, if small enough, are packed into a single
* register. Otherwise, each occupies its own register and the rest of
* the slider-related register addresses are offset by one.
*/
reg_offset = dev_desc->sldr_res < U16_MAX ? 0 : 1;
sldr_setup[0] |= count;
sldr_setup[3 + reg_offset] &= ~GENMASK(ext_chan - 1, 0);
for (i = 0; i < ARRAY_SIZE(chan_sel); i++) {
sldr_setup[5 + reg_offset + i] = 0;
if (i >= count)
continue;
if (chan_sel[i] >= ext_chan) {
dev_err(&client->dev, "Invalid %s channel: %u\n",
fwnode_get_name(sldr_node), chan_sel[i]);
return -EINVAL;
}
/*
* The following fields indicate which channels participate in
* the slider, as well as each channel's relative placement.
*/
sldr_setup[3 + reg_offset] |= BIT(chan_sel[i]);
sldr_setup[5 + reg_offset + i] = chan_sel[i] * 42 + 1080;
}
sldr_setup[4 + reg_offset] = dev_desc->touch_link;
if (fwnode_property_present(sldr_node, "azoteq,use-prox"))
sldr_setup[4 + reg_offset] -= 2;
error = fwnode_property_read_u32(sldr_node, "azoteq,slider-size", &val);
if (!error) {
if (val > dev_desc->sldr_res) {
dev_err(&client->dev, "Invalid %s size: %u\n",
fwnode_get_name(sldr_node), val);
return -EINVAL;
}
if (reg_offset) {
sldr_setup[3] = val;
} else {
sldr_setup[2] &= ~IQS7222_SLDR_SETUP_2_RES_MASK;
sldr_setup[2] |= (val / 16 <<
IQS7222_SLDR_SETUP_2_RES_SHIFT);
}
} else if (error != -EINVAL) {
dev_err(&client->dev, "Failed to read %s size: %d\n",
fwnode_get_name(sldr_node), error);
return error;
}
if (!(reg_offset ? sldr_setup[3]
: sldr_setup[2] & IQS7222_SLDR_SETUP_2_RES_MASK)) {
dev_err(&client->dev, "Undefined %s size\n",
fwnode_get_name(sldr_node));
return -EINVAL;
}
error = fwnode_property_read_u32(sldr_node, "azoteq,top-speed", &val);
if (!error) {
if (val > (reg_offset ? U16_MAX : U8_MAX * 4)) {
dev_err(&client->dev, "Invalid %s top speed: %u\n",
fwnode_get_name(sldr_node), val);
return -EINVAL;
}
if (reg_offset) {
sldr_setup[2] = val;
} else {
sldr_setup[2] &= ~IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK;
sldr_setup[2] |= (val / 4);
}
} else if (error != -EINVAL) {
dev_err(&client->dev, "Failed to read %s top speed: %d\n",
fwnode_get_name(sldr_node), error);
return error;
}
error = fwnode_property_read_u32(sldr_node, "linux,axis", &val);
if (!error) {
u16 sldr_max = sldr_setup[3] - 1;
if (!reg_offset) {
sldr_max = sldr_setup[2];
sldr_max &= IQS7222_SLDR_SETUP_2_RES_MASK;
sldr_max >>= IQS7222_SLDR_SETUP_2_RES_SHIFT;
sldr_max = sldr_max * 16 - 1;
}
input_set_abs_params(iqs7222->keypad, val, 0, sldr_max, 0, 0);
iqs7222->sl_axis[sldr_index] = val;
} else if (error != -EINVAL) {
dev_err(&client->dev, "Failed to read %s axis: %d\n",
fwnode_get_name(sldr_node), error);
return error;
}
if (dev_desc->wheel_enable) {
sldr_setup[0] &= ~dev_desc->wheel_enable;
if (iqs7222->sl_axis[sldr_index] == ABS_WHEEL)
sldr_setup[0] |= dev_desc->wheel_enable;
}
/*
* The absence of a register offset makes it safe to assume the device
* supports gestures, each of which is first disabled until explicitly
* enabled.
*/
if (!reg_offset)
for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++)
sldr_setup[9] &= ~iqs7222_sl_events[i].enable;
for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) {
const char *event_name = iqs7222_sl_events[i].name;
struct fwnode_handle *event_node;
enum iqs7222_reg_key_id reg_key;
event_node = fwnode_get_named_child_node(sldr_node, event_name);
if (!event_node)
continue;
/*
* Depending on the device, gestures are either offered using
* one of two timing resolutions, or are not supported at all.
*/
if (reg_offset)
reg_key = IQS7222_REG_KEY_RESERVED;
else if (dev_desc->legacy_gesture &&
iqs7222_sl_events[i].reg_key == IQS7222_REG_KEY_TAP)
reg_key = IQS7222_REG_KEY_TAP_LEGACY;
else if (dev_desc->legacy_gesture &&
iqs7222_sl_events[i].reg_key == IQS7222_REG_KEY_AXIAL)
reg_key = IQS7222_REG_KEY_AXIAL_LEGACY;
else
reg_key = iqs7222_sl_events[i].reg_key;
/*
* The press/release event does not expose a direct GPIO link,
* but one can be emulated by tying each of the participating
* channels to the same GPIO.
*/
error = iqs7222_parse_event(iqs7222, event_node, sldr_index,
IQS7222_REG_GRP_SLDR, reg_key,
i ? iqs7222_sl_events[i].enable
: sldr_setup[3 + reg_offset],
i ? 1568 + sldr_index * 30
: sldr_setup[4 + reg_offset],
NULL,
&iqs7222->sl_code[sldr_index][i]);
fwnode_handle_put(event_node);
if (error)
return error;
if (!reg_offset)
sldr_setup[9] |= iqs7222_sl_events[i].enable;
if (!dev_desc->event_offset)
continue;
/*
* The press/release event is determined based on whether the
* coordinate field reports 0xFFFF and solely relies on touch
* or proximity interrupts to be unmasked.
*/
if (i && !reg_offset)
*event_mask |= (IQS7222_EVENT_MASK_SLDR << sldr_index);
else if (sldr_setup[4 + reg_offset] == dev_desc->touch_link)
*event_mask |= IQS7222_EVENT_MASK_TOUCH;
else
*event_mask |= IQS7222_EVENT_MASK_PROX;
}
/*
* The following call handles a special pair of properties that shift
* to make room for a wheel enable control in the case of IQS7222C.
*/
return iqs7222_parse_props(iqs7222, sldr_node, sldr_index,
IQS7222_REG_GRP_SLDR,
dev_desc->wheel_enable ?
IQS7222_REG_KEY_WHEEL :
IQS7222_REG_KEY_NO_WHEEL);
}
static int iqs7222_parse_tpad(struct iqs7222_private *iqs7222,
struct fwnode_handle *tpad_node, int tpad_index)
{
const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
struct touchscreen_properties *prop = &iqs7222->prop;
struct i2c_client *client = iqs7222->client;
int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
int count, error, i;
u16 *event_mask = &iqs7222->sys_setup[dev_desc->event_offset];
u16 *tpad_setup = iqs7222->tpad_setup;
unsigned int chan_sel[12];
error = iqs7222_parse_props(iqs7222, tpad_node, tpad_index,
IQS7222_REG_GRP_TPAD,
IQS7222_REG_KEY_NONE);
if (error)
return error;
count = fwnode_property_count_u32(tpad_node, "azoteq,channel-select");
if (count < 0) {
dev_err(&client->dev, "Failed to count %s channels: %d\n",
fwnode_get_name(tpad_node), count);
return count;
} else if (!count || count > ARRAY_SIZE(chan_sel)) {
dev_err(&client->dev, "Invalid number of %s channels\n",
fwnode_get_name(tpad_node));
return -EINVAL;
}
error = fwnode_property_read_u32_array(tpad_node,
"azoteq,channel-select",
chan_sel, count);
if (error) {
dev_err(&client->dev, "Failed to read %s channels: %d\n",
fwnode_get_name(tpad_node), error);
return error;
}
tpad_setup[6] &= ~GENMASK(num_chan - 1, 0);
for (i = 0; i < ARRAY_SIZE(chan_sel); i++) {
tpad_setup[8 + i] = 0;
if (i >= count || chan_sel[i] == U8_MAX)
continue;
if (chan_sel[i] >= num_chan) {
dev_err(&client->dev, "Invalid %s channel: %u\n",
fwnode_get_name(tpad_node), chan_sel[i]);
return -EINVAL;
}
/*
* The following fields indicate which channels participate in
* the trackpad, as well as each channel's relative placement.
*/
tpad_setup[6] |= BIT(chan_sel[i]);
tpad_setup[8 + i] = chan_sel[i] * 34 + 1072;
}
tpad_setup[7] = dev_desc->touch_link;
if (fwnode_property_present(tpad_node, "azoteq,use-prox"))
tpad_setup[7] -= 2;
for (i = 0; i < ARRAY_SIZE(iqs7222_tp_events); i++)
tpad_setup[20] &= ~(iqs7222_tp_events[i].strict |
iqs7222_tp_events[i].enable);
for (i = 0; i < ARRAY_SIZE(iqs7222_tp_events); i++) {
const char *event_name = iqs7222_tp_events[i].name;
struct fwnode_handle *event_node;
event_node = fwnode_get_named_child_node(tpad_node, event_name);
if (!event_node)
continue;
if (fwnode_property_present(event_node,
"azoteq,gesture-angle-tighten"))
tpad_setup[20] |= iqs7222_tp_events[i].strict;
tpad_setup[20] |= iqs7222_tp_events[i].enable;
error = iqs7222_parse_event(iqs7222, event_node, tpad_index,
IQS7222_REG_GRP_TPAD,
iqs7222_tp_events[i].reg_key,
iqs7222_tp_events[i].link, 1566,
NULL,
&iqs7222->tp_code[i]);
fwnode_handle_put(event_node);
if (error)
return error;
if (!dev_desc->event_offset)
continue;
/*
* The press/release event is determined based on whether the
* coordinate fields report 0xFFFF and solely relies on touch
* or proximity interrupts to be unmasked.
*/
if (i)
*event_mask |= IQS7222_EVENT_MASK_TPAD;
else if (tpad_setup[7] == dev_desc->touch_link)
*event_mask |= IQS7222_EVENT_MASK_TOUCH;
else
*event_mask |= IQS7222_EVENT_MASK_PROX;
}
if (!iqs7222->tp_code[0])
return 0;
input_set_abs_params(iqs7222->keypad, ABS_X,
0, (tpad_setup[4] ? : 1) - 1, 0, 0);
input_set_abs_params(iqs7222->keypad, ABS_Y,
0, (tpad_setup[5] ? : 1) - 1, 0, 0);
touchscreen_parse_properties(iqs7222->keypad, false, prop);
if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) {
dev_err(&client->dev, "Invalid trackpad size: %u*%u\n",
prop->max_x, prop->max_y);
return -EINVAL;
}
tpad_setup[4] = prop->max_x + 1;
tpad_setup[5] = prop->max_y + 1;
return 0;
}
static int (*iqs7222_parse_extra[IQS7222_NUM_REG_GRPS])
(struct iqs7222_private *iqs7222,
struct fwnode_handle *reg_grp_node,
int reg_grp_index) = {
[IQS7222_REG_GRP_CYCLE] = iqs7222_parse_cycle,
[IQS7222_REG_GRP_CHAN] = iqs7222_parse_chan,
[IQS7222_REG_GRP_SLDR] = iqs7222_parse_sldr,
[IQS7222_REG_GRP_TPAD] = iqs7222_parse_tpad,
};
static int iqs7222_parse_reg_grp(struct iqs7222_private *iqs7222,
enum iqs7222_reg_grp_id reg_grp,
int reg_grp_index)
{
struct i2c_client *client = iqs7222->client;
struct fwnode_handle *reg_grp_node;
int error;
if (iqs7222_reg_grp_names[reg_grp]) {
char reg_grp_name[16];
snprintf(reg_grp_name, sizeof(reg_grp_name),
iqs7222_reg_grp_names[reg_grp], reg_grp_index);
reg_grp_node = device_get_named_child_node(&client->dev,
reg_grp_name);
} else {
reg_grp_node = fwnode_handle_get(dev_fwnode(&client->dev));
}
if (!reg_grp_node)
return 0;
error = iqs7222_parse_props(iqs7222, reg_grp_node, reg_grp_index,
reg_grp, IQS7222_REG_KEY_NONE);
if (!error && iqs7222_parse_extra[reg_grp])
error = iqs7222_parse_extra[reg_grp](iqs7222, reg_grp_node,
reg_grp_index);
fwnode_handle_put(reg_grp_node);
return error;
}
static int iqs7222_parse_all(struct iqs7222_private *iqs7222)
{
const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
const struct iqs7222_reg_grp_desc *reg_grps = dev_desc->reg_grps;
u16 *sys_setup = iqs7222->sys_setup;
int error, i, j;
if (dev_desc->allow_offset)
sys_setup[dev_desc->allow_offset] = U16_MAX;
if (dev_desc->event_offset)
sys_setup[dev_desc->event_offset] = IQS7222_EVENT_MASK_ATI;
for (i = 0; i < reg_grps[IQS7222_REG_GRP_GPIO].num_row; i++) {
u16 *gpio_setup = iqs7222->gpio_setup[i];
gpio_setup[0] &= ~IQS7222_GPIO_SETUP_0_GPIO_EN;
gpio_setup[1] = 0;
gpio_setup[2] = 0;
if (reg_grps[IQS7222_REG_GRP_GPIO].num_row == 1)
continue;
/*
* The IQS7222C and IQS7222D expose multiple GPIO and must be
* informed as to which GPIO this group represents.
*/
for (j = 0; j < ARRAY_SIZE(iqs7222_gpio_links); j++)
gpio_setup[0] &= ~BIT(iqs7222_gpio_links[j]);
gpio_setup[0] |= BIT(iqs7222_gpio_links[i]);
}
for (i = 0; i < reg_grps[IQS7222_REG_GRP_CHAN].num_row; i++) {
u16 *chan_setup = iqs7222->chan_setup[i];
chan_setup[0] &= ~IQS7222_CHAN_SETUP_0_REF_MODE_MASK;
chan_setup[0] &= ~IQS7222_CHAN_SETUP_0_CHAN_EN;
chan_setup[5] = 0;
}
for (i = 0; i < reg_grps[IQS7222_REG_GRP_SLDR].num_row; i++) {
u16 *sldr_setup = iqs7222->sldr_setup[i];
sldr_setup[0] &= ~IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK;
}
for (i = 0; i < IQS7222_NUM_REG_GRPS; i++) {
for (j = 0; j < reg_grps[i].num_row; j++) {
error = iqs7222_parse_reg_grp(iqs7222, i, j);
if (error)
return error;
}
}
return 0;
}
static int iqs7222_report(struct iqs7222_private *iqs7222)
{
const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
struct i2c_client *client = iqs7222->client;
int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
int num_stat = dev_desc->reg_grps[IQS7222_REG_GRP_STAT].num_col;
int error, i, j;
__le16 status[IQS7222_MAX_COLS_STAT];
error = iqs7222_read_burst(iqs7222, IQS7222_SYS_STATUS, status,
num_stat);
if (error)
return error;
if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_RESET) {
dev_err(&client->dev, "Unexpected device reset\n");
return iqs7222_dev_init(iqs7222, WRITE);
}
if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_ATI_ERROR) {
dev_err(&client->dev, "Unexpected ATI error\n");
return iqs7222_ati_trigger(iqs7222);
}
if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_ATI_ACTIVE)
return 0;
for (i = 0; i < num_chan; i++) {
u16 *chan_setup = iqs7222->chan_setup[i];
if (!(chan_setup[0] & IQS7222_CHAN_SETUP_0_CHAN_EN))
continue;
for (j = 0; j < ARRAY_SIZE(iqs7222_kp_events); j++) {
/*
* Proximity state begins at offset 2 and spills into
* offset 3 for devices with more than 16 channels.
*
* Touch state begins at the first offset immediately
* following proximity state.
*/
int k = 2 + j * (num_chan > 16 ? 2 : 1);
u16 state = le16_to_cpu(status[k + i / 16]);
if (!iqs7222->kp_type[i][j])
continue;
input_event(iqs7222->keypad,
iqs7222->kp_type[i][j],
iqs7222->kp_code[i][j],
!!(state & BIT(i % 16)));
}
}
for (i = 0; i < dev_desc->reg_grps[IQS7222_REG_GRP_SLDR].num_row; i++) {
u16 *sldr_setup = iqs7222->sldr_setup[i];
u16 sldr_pos = le16_to_cpu(status[4 + i]);
u16 state = le16_to_cpu(status[6 + i]);
if (!(sldr_setup[0] & IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK))
continue;
if (sldr_pos < dev_desc->sldr_res)
input_report_abs(iqs7222->keypad, iqs7222->sl_axis[i],
sldr_pos);
input_report_key(iqs7222->keypad, iqs7222->sl_code[i][0],
sldr_pos < dev_desc->sldr_res);
/*
* A maximum resolution indicates the device does not support
* gestures, in which case the remaining fields are ignored.
*/
if (dev_desc->sldr_res == U16_MAX)
continue;
if (!(le16_to_cpu(status[1]) & IQS7222_EVENT_MASK_SLDR << i))
continue;
/*
* Skip the press/release event, as it does not have separate
* status fields and is handled separately.
*/
for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++) {
u16 mask = iqs7222_sl_events[j].mask;
u16 val = iqs7222_sl_events[j].val;
input_report_key(iqs7222->keypad,
iqs7222->sl_code[i][j],
(state & mask) == val);
}
input_sync(iqs7222->keypad);
for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++)
input_report_key(iqs7222->keypad,
iqs7222->sl_code[i][j], 0);
}
for (i = 0; i < dev_desc->reg_grps[IQS7222_REG_GRP_TPAD].num_row; i++) {
u16 tpad_pos_x = le16_to_cpu(status[4]);
u16 tpad_pos_y = le16_to_cpu(status[5]);
u16 state = le16_to_cpu(status[6]);
input_report_key(iqs7222->keypad, iqs7222->tp_code[0],
tpad_pos_x < U16_MAX);
if (tpad_pos_x < U16_MAX)
touchscreen_report_pos(iqs7222->keypad, &iqs7222->prop,
tpad_pos_x, tpad_pos_y, false);
if (!(le16_to_cpu(status[1]) & IQS7222_EVENT_MASK_TPAD))
continue;
/*
* Skip the press/release event, as it does not have separate
* status fields and is handled separately.
*/
for (j = 1; j < ARRAY_SIZE(iqs7222_tp_events); j++) {
u16 mask = iqs7222_tp_events[j].mask;
u16 val = iqs7222_tp_events[j].val;
input_report_key(iqs7222->keypad,
iqs7222->tp_code[j],
(state & mask) == val);
}
input_sync(iqs7222->keypad);
for (j = 1; j < ARRAY_SIZE(iqs7222_tp_events); j++)
input_report_key(iqs7222->keypad,
iqs7222->tp_code[j], 0);
}
input_sync(iqs7222->keypad);
return 0;
}
static irqreturn_t iqs7222_irq(int irq, void *context)
{
struct iqs7222_private *iqs7222 = context;
return iqs7222_report(iqs7222) ? IRQ_NONE : IRQ_HANDLED;
}
static int iqs7222_probe(struct i2c_client *client)
{
struct iqs7222_private *iqs7222;
unsigned long irq_flags;
int error, irq;
iqs7222 = devm_kzalloc(&client->dev, sizeof(*iqs7222), GFP_KERNEL);
if (!iqs7222)
return -ENOMEM;
i2c_set_clientdata(client, iqs7222);
iqs7222->client = client;
iqs7222->keypad = devm_input_allocate_device(&client->dev);
if (!iqs7222->keypad)
return -ENOMEM;
iqs7222->keypad->name = client->name;
iqs7222->keypad->id.bustype = BUS_I2C;
/*
* The RDY pin behaves as an interrupt, but must also be polled ahead
* of unsolicited I2C communication. As such, it is first opened as a
* GPIO and then passed to gpiod_to_irq() to register the interrupt.
*/
iqs7222->irq_gpio = devm_gpiod_get(&client->dev, "irq", GPIOD_IN);
if (IS_ERR(iqs7222->irq_gpio)) {
error = PTR_ERR(iqs7222->irq_gpio);
dev_err(&client->dev, "Failed to request IRQ GPIO: %d\n",
error);
return error;
}
iqs7222->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(iqs7222->reset_gpio)) {
error = PTR_ERR(iqs7222->reset_gpio);
dev_err(&client->dev, "Failed to request reset GPIO: %d\n",
error);
return error;
}
error = iqs7222_hard_reset(iqs7222);
if (error)
return error;
error = iqs7222_dev_info(iqs7222);
if (error)
return error;
error = iqs7222_dev_init(iqs7222, READ);
if (error)
return error;
error = iqs7222_parse_all(iqs7222);
if (error)
return error;
error = iqs7222_dev_init(iqs7222, WRITE);
if (error)
return error;
error = iqs7222_report(iqs7222);
if (error)
return error;
error = input_register_device(iqs7222->keypad);
if (error) {
dev_err(&client->dev, "Failed to register device: %d\n", error);
return error;
}
irq = gpiod_to_irq(iqs7222->irq_gpio);
if (irq < 0)
return irq;
irq_flags = gpiod_is_active_low(iqs7222->irq_gpio) ? IRQF_TRIGGER_LOW
: IRQF_TRIGGER_HIGH;
irq_flags |= IRQF_ONESHOT;
error = devm_request_threaded_irq(&client->dev, irq, NULL, iqs7222_irq,
irq_flags, client->name, iqs7222);
if (error)
dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
return error;
}
static const struct of_device_id iqs7222_of_match[] = {
{ .compatible = "azoteq,iqs7222a" },
{ .compatible = "azoteq,iqs7222b" },
{ .compatible = "azoteq,iqs7222c" },
{ .compatible = "azoteq,iqs7222d" },
{ }
};
MODULE_DEVICE_TABLE(of, iqs7222_of_match);
static struct i2c_driver iqs7222_i2c_driver = {
.driver = {
.name = "iqs7222",
.of_match_table = iqs7222_of_match,
},
.probe = iqs7222_probe,
};
module_i2c_driver(iqs7222_i2c_driver);
MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
MODULE_DESCRIPTION("Azoteq IQS7222A/B/C/D Capacitive Touch Controller");
MODULE_LICENSE("GPL");