#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/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#define IQS7211_PROD_NUM 0x00
#define IQS7211_EVENT_MASK_ALL GENMASK(14, 8)
#define IQS7211_EVENT_MASK_ALP BIT(13)
#define IQS7211_EVENT_MASK_BTN BIT(12)
#define IQS7211_EVENT_MASK_ATI BIT(11)
#define IQS7211_EVENT_MASK_MOVE BIT(10)
#define IQS7211_EVENT_MASK_GSTR BIT(9)
#define IQS7211_EVENT_MODE BIT(8)
#define IQS7211_COMMS_ERROR 0xEEEE
#define IQS7211_COMMS_RETRY_MS 50
#define IQS7211_COMMS_SLEEP_US 100
#define IQS7211_COMMS_TIMEOUT_US (100 * USEC_PER_MSEC)
#define IQS7211_RESET_TIMEOUT_MS 150
#define IQS7211_START_TIMEOUT_US (1 * USEC_PER_SEC)
#define IQS7211_NUM_RETRIES 5
#define IQS7211_NUM_CRX 8
#define IQS7211_MAX_CTX 13
#define IQS7211_MAX_CONTACTS 2
#define IQS7211_MAX_CYCLES 21
#define iqs7211_irq_wait() usleep_range(2500, 2600)
enum iqs7211_dev_id {
IQS7210A,
IQS7211A,
IQS7211E,
};
enum iqs7211_comms_mode {
IQS7211_COMMS_MODE_WAIT,
IQS7211_COMMS_MODE_FREE,
IQS7211_COMMS_MODE_FORCE,
};
struct iqs7211_reg_field_desc {
struct list_head list;
u8 addr;
u16 mask;
u16 val;
};
enum iqs7211_reg_key_id {
IQS7211_REG_KEY_NONE,
IQS7211_REG_KEY_PROX,
IQS7211_REG_KEY_TOUCH,
IQS7211_REG_KEY_TAP,
IQS7211_REG_KEY_HOLD,
IQS7211_REG_KEY_PALM,
IQS7211_REG_KEY_AXIAL_X,
IQS7211_REG_KEY_AXIAL_Y,
IQS7211_REG_KEY_RESERVED
};
enum iqs7211_reg_grp_id {
IQS7211_REG_GRP_TP,
IQS7211_REG_GRP_BTN,
IQS7211_REG_GRP_ALP,
IQS7211_REG_GRP_SYS,
IQS7211_NUM_REG_GRPS
};
static const char * const iqs7211_reg_grp_names[IQS7211_NUM_REG_GRPS] = {
[IQS7211_REG_GRP_TP] = "trackpad",
[IQS7211_REG_GRP_BTN] = "button",
[IQS7211_REG_GRP_ALP] = "alp",
};
static const u16 iqs7211_reg_grp_masks[IQS7211_NUM_REG_GRPS] = {
[IQS7211_REG_GRP_TP] = IQS7211_EVENT_MASK_GSTR,
[IQS7211_REG_GRP_BTN] = IQS7211_EVENT_MASK_BTN,
[IQS7211_REG_GRP_ALP] = IQS7211_EVENT_MASK_ALP,
};
struct iqs7211_event_desc {
const char *name;
u16 mask;
u16 enable;
enum iqs7211_reg_grp_id reg_grp;
enum iqs7211_reg_key_id reg_key;
};
static const struct iqs7211_event_desc iqs7210a_kp_events[] = {
{
.mask = BIT(10),
.enable = BIT(13) | BIT(12),
.reg_grp = IQS7211_REG_GRP_ALP,
},
{
.name = "event-prox",
.mask = BIT(2),
.enable = BIT(5) | BIT(4),
.reg_grp = IQS7211_REG_GRP_BTN,
.reg_key = IQS7211_REG_KEY_PROX,
},
{
.name = "event-touch",
.mask = BIT(3),
.enable = BIT(5) | BIT(4),
.reg_grp = IQS7211_REG_GRP_BTN,
.reg_key = IQS7211_REG_KEY_TOUCH,
},
{
.name = "event-tap",
.mask = BIT(0),
.enable = BIT(0),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_TAP,
},
{
.name = "event-hold",
.mask = BIT(1),
.enable = BIT(1),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_HOLD,
},
{
.name = "event-swipe-x-neg",
.mask = BIT(2),
.enable = BIT(2),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_X,
},
{
.name = "event-swipe-x-pos",
.mask = BIT(3),
.enable = BIT(3),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_X,
},
{
.name = "event-swipe-y-pos",
.mask = BIT(4),
.enable = BIT(4),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
},
{
.name = "event-swipe-y-neg",
.mask = BIT(5),
.enable = BIT(5),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
},
};
static const struct iqs7211_event_desc iqs7211a_kp_events[] = {
{
.mask = BIT(14),
.reg_grp = IQS7211_REG_GRP_ALP,
},
{
.name = "event-tap",
.mask = BIT(0),
.enable = BIT(0),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_TAP,
},
{
.name = "event-hold",
.mask = BIT(1),
.enable = BIT(1),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_HOLD,
},
{
.name = "event-swipe-x-neg",
.mask = BIT(2),
.enable = BIT(2),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_X,
},
{
.name = "event-swipe-x-pos",
.mask = BIT(3),
.enable = BIT(3),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_X,
},
{
.name = "event-swipe-y-pos",
.mask = BIT(4),
.enable = BIT(4),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
},
{
.name = "event-swipe-y-neg",
.mask = BIT(5),
.enable = BIT(5),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
},
};
static const struct iqs7211_event_desc iqs7211e_kp_events[] = {
{
.mask = BIT(14),
.reg_grp = IQS7211_REG_GRP_ALP,
},
{
.name = "event-tap",
.mask = BIT(0),
.enable = BIT(0),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_TAP,
},
{
.name = "event-tap-double",
.mask = BIT(1),
.enable = BIT(1),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_TAP,
},
{
.name = "event-tap-triple",
.mask = BIT(2),
.enable = BIT(2),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_TAP,
},
{
.name = "event-hold",
.mask = BIT(3),
.enable = BIT(3),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_HOLD,
},
{
.name = "event-palm",
.mask = BIT(4),
.enable = BIT(4),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_PALM,
},
{
.name = "event-swipe-x-pos",
.mask = BIT(8),
.enable = BIT(8),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_X,
},
{
.name = "event-swipe-x-neg",
.mask = BIT(9),
.enable = BIT(9),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_X,
},
{
.name = "event-swipe-y-pos",
.mask = BIT(10),
.enable = BIT(10),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
},
{
.name = "event-swipe-y-neg",
.mask = BIT(11),
.enable = BIT(11),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
},
{
.name = "event-swipe-x-pos-hold",
.mask = BIT(12),
.enable = BIT(12),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_HOLD,
},
{
.name = "event-swipe-x-neg-hold",
.mask = BIT(13),
.enable = BIT(13),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_HOLD,
},
{
.name = "event-swipe-y-pos-hold",
.mask = BIT(14),
.enable = BIT(14),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_HOLD,
},
{
.name = "event-swipe-y-neg-hold",
.mask = BIT(15),
.enable = BIT(15),
.reg_grp = IQS7211_REG_GRP_TP,
.reg_key = IQS7211_REG_KEY_HOLD,
},
};
struct iqs7211_dev_desc {
const char *tp_name;
const char *kp_name;
u16 prod_num;
u16 show_reset;
u16 ati_error[IQS7211_NUM_REG_GRPS];
u16 ati_start[IQS7211_NUM_REG_GRPS];
u16 suspend;
u16 ack_reset;
u16 comms_end;
u16 comms_req;
int charge_shift;
int info_offs;
int gesture_offs;
int contact_offs;
u8 sys_stat;
u8 sys_ctrl;
u8 alp_config;
u8 tp_config;
u8 exp_file;
u8 kp_enable[IQS7211_NUM_REG_GRPS];
u8 gesture_angle;
u8 rx_tx_map;
u8 cycle_alloc[2];
u8 cycle_limit[2];
const struct iqs7211_event_desc *kp_events;
int num_kp_events;
int min_crx_alp;
int num_ctx;
};
static const struct iqs7211_dev_desc iqs7211_devs[] = {
[IQS7210A] = {
.tp_name = "iqs7210a_trackpad",
.kp_name = "iqs7210a_keys",
.prod_num = 944,
.show_reset = BIT(15),
.ati_error = {
[IQS7211_REG_GRP_TP] = BIT(12),
[IQS7211_REG_GRP_BTN] = BIT(0),
[IQS7211_REG_GRP_ALP] = BIT(8),
},
.ati_start = {
[IQS7211_REG_GRP_TP] = BIT(13),
[IQS7211_REG_GRP_BTN] = BIT(1),
[IQS7211_REG_GRP_ALP] = BIT(9),
},
.suspend = BIT(11),
.ack_reset = BIT(7),
.comms_end = BIT(2),
.comms_req = BIT(1),
.charge_shift = 4,
.info_offs = 0,
.gesture_offs = 1,
.contact_offs = 4,
.sys_stat = 0x0A,
.sys_ctrl = 0x35,
.alp_config = 0x39,
.tp_config = 0x4E,
.exp_file = 0x57,
.kp_enable = {
[IQS7211_REG_GRP_TP] = 0x58,
[IQS7211_REG_GRP_BTN] = 0x37,
[IQS7211_REG_GRP_ALP] = 0x37,
},
.gesture_angle = 0x5F,
.rx_tx_map = 0x60,
.cycle_alloc = { 0x66, 0x75, },
.cycle_limit = { 10, 6, },
.kp_events = iqs7210a_kp_events,
.num_kp_events = ARRAY_SIZE(iqs7210a_kp_events),
.min_crx_alp = 4,
.num_ctx = IQS7211_MAX_CTX - 1,
},
[IQS7211A] = {
.tp_name = "iqs7211a_trackpad",
.kp_name = "iqs7211a_keys",
.prod_num = 763,
.show_reset = BIT(7),
.ati_error = {
[IQS7211_REG_GRP_TP] = BIT(3),
[IQS7211_REG_GRP_ALP] = BIT(5),
},
.ati_start = {
[IQS7211_REG_GRP_TP] = BIT(5),
[IQS7211_REG_GRP_ALP] = BIT(6),
},
.ack_reset = BIT(7),
.comms_req = BIT(4),
.charge_shift = 0,
.info_offs = 0,
.gesture_offs = 1,
.contact_offs = 4,
.sys_stat = 0x10,
.sys_ctrl = 0x50,
.tp_config = 0x60,
.alp_config = 0x72,
.exp_file = 0x74,
.kp_enable = {
[IQS7211_REG_GRP_TP] = 0x80,
},
.gesture_angle = 0x87,
.rx_tx_map = 0x90,
.cycle_alloc = { 0xA0, 0xB0, },
.cycle_limit = { 10, 8, },
.kp_events = iqs7211a_kp_events,
.num_kp_events = ARRAY_SIZE(iqs7211a_kp_events),
.num_ctx = IQS7211_MAX_CTX - 1,
},
[IQS7211E] = {
.tp_name = "iqs7211e_trackpad",
.kp_name = "iqs7211e_keys",
.prod_num = 1112,
.show_reset = BIT(7),
.ati_error = {
[IQS7211_REG_GRP_TP] = BIT(3),
[IQS7211_REG_GRP_ALP] = BIT(5),
},
.ati_start = {
[IQS7211_REG_GRP_TP] = BIT(5),
[IQS7211_REG_GRP_ALP] = BIT(6),
},
.suspend = BIT(11),
.ack_reset = BIT(7),
.comms_end = BIT(6),
.comms_req = BIT(4),
.charge_shift = 0,
.info_offs = 1,
.gesture_offs = 0,
.contact_offs = 2,
.sys_stat = 0x0E,
.sys_ctrl = 0x33,
.tp_config = 0x41,
.alp_config = 0x36,
.exp_file = 0x4A,
.kp_enable = {
[IQS7211_REG_GRP_TP] = 0x4B,
},
.gesture_angle = 0x55,
.rx_tx_map = 0x56,
.cycle_alloc = { 0x5D, 0x6C, },
.cycle_limit = { 10, 11, },
.kp_events = iqs7211e_kp_events,
.num_kp_events = ARRAY_SIZE(iqs7211e_kp_events),
.num_ctx = IQS7211_MAX_CTX,
},
};
struct iqs7211_prop_desc {
const char *name;
enum iqs7211_reg_key_id reg_key;
u8 reg_addr[IQS7211_NUM_REG_GRPS][ARRAY_SIZE(iqs7211_devs)];
int reg_shift;
int reg_width;
int val_pitch;
int val_min;
int val_max;
const char *label;
};
static const struct iqs7211_prop_desc iqs7211_props[] = {
{
.name = "azoteq,ati-frac-div-fine",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x1E,
[IQS7211A] = 0x30,
[IQS7211E] = 0x21,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x22,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x23,
[IQS7211A] = 0x36,
[IQS7211E] = 0x25,
},
},
.reg_shift = 9,
.reg_width = 5,
.label = "ATI fine fractional divider",
},
{
.name = "azoteq,ati-frac-mult-coarse",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x1E,
[IQS7211A] = 0x30,
[IQS7211E] = 0x21,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x22,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x23,
[IQS7211A] = 0x36,
[IQS7211E] = 0x25,
},
},
.reg_shift = 5,
.reg_width = 4,
.label = "ATI coarse fractional multiplier",
},
{
.name = "azoteq,ati-frac-div-coarse",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x1E,
[IQS7211A] = 0x30,
[IQS7211E] = 0x21,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x22,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x23,
[IQS7211A] = 0x36,
[IQS7211E] = 0x25,
},
},
.reg_shift = 0,
.reg_width = 5,
.label = "ATI coarse fractional divider",
},
{
.name = "azoteq,ati-comp-div",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x1F,
[IQS7211E] = 0x22,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x24,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7211E] = 0x26,
},
},
.reg_shift = 0,
.reg_width = 8,
.val_max = 31,
.label = "ATI compensation divider",
},
{
.name = "azoteq,ati-comp-div",
.reg_addr = {
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x24,
},
},
.reg_shift = 8,
.reg_width = 8,
.val_max = 31,
.label = "ATI compensation divider",
},
{
.name = "azoteq,ati-comp-div",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7211A] = 0x31,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7211A] = 0x37,
},
},
.val_max = 31,
.label = "ATI compensation divider",
},
{
.name = "azoteq,ati-target",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x20,
[IQS7211A] = 0x32,
[IQS7211E] = 0x23,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x27,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x28,
[IQS7211A] = 0x38,
[IQS7211E] = 0x27,
},
},
.label = "ATI target",
},
{
.name = "azoteq,ati-base",
.reg_addr[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x26,
},
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 8,
.label = "ATI base",
},
{
.name = "azoteq,ati-base",
.reg_addr[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x26,
},
.reg_shift = 0,
.reg_width = 8,
.val_pitch = 8,
.label = "ATI base",
},
{
.name = "azoteq,rate-active-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x29,
[IQS7211A] = 0x40,
[IQS7211E] = 0x28,
},
.label = "active mode report rate",
},
{
.name = "azoteq,rate-touch-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x2A,
[IQS7211A] = 0x41,
[IQS7211E] = 0x29,
},
.label = "idle-touch mode report rate",
},
{
.name = "azoteq,rate-idle-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x2B,
[IQS7211A] = 0x42,
[IQS7211E] = 0x2A,
},
.label = "idle mode report rate",
},
{
.name = "azoteq,rate-lp1-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x2C,
[IQS7211A] = 0x43,
[IQS7211E] = 0x2B,
},
.label = "low-power mode 1 report rate",
},
{
.name = "azoteq,rate-lp2-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x2D,
[IQS7211A] = 0x44,
[IQS7211E] = 0x2C,
},
.label = "low-power mode 2 report rate",
},
{
.name = "azoteq,timeout-active-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x2E,
[IQS7211A] = 0x45,
[IQS7211E] = 0x2D,
},
.val_pitch = 1000,
.label = "active mode timeout",
},
{
.name = "azoteq,timeout-touch-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x2F,
[IQS7211A] = 0x46,
[IQS7211E] = 0x2E,
},
.val_pitch = 1000,
.label = "idle-touch mode timeout",
},
{
.name = "azoteq,timeout-idle-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x30,
[IQS7211A] = 0x47,
[IQS7211E] = 0x2F,
},
.val_pitch = 1000,
.label = "idle mode timeout",
},
{
.name = "azoteq,timeout-lp1-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x31,
[IQS7211A] = 0x48,
[IQS7211E] = 0x30,
},
.val_pitch = 1000,
.label = "low-power mode 1 timeout",
},
{
.name = "azoteq,timeout-lp2-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x32,
[IQS7211E] = 0x31,
},
.reg_shift = 8,
.reg_width = 8,
.val_pitch = 1000,
.val_max = 60000,
.label = "trackpad reference value update rate",
},
{
.name = "azoteq,timeout-lp2-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7211A] = 0x49,
},
.val_pitch = 1000,
.val_max = 60000,
.label = "trackpad reference value update rate",
},
{
.name = "azoteq,timeout-ati-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x32,
[IQS7211E] = 0x31,
},
.reg_width = 8,
.val_pitch = 1000,
.val_max = 60000,
.label = "ATI error timeout",
},
{
.name = "azoteq,timeout-ati-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7211A] = 0x35,
},
.val_pitch = 1000,
.val_max = 60000,
.label = "ATI error timeout",
},
{
.name = "azoteq,timeout-comms-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x33,
[IQS7211A] = 0x4A,
[IQS7211E] = 0x32,
},
.label = "communication timeout",
},
{
.name = "azoteq,timeout-press-ms",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x34,
},
.reg_width = 8,
.val_pitch = 1000,
.val_max = 60000,
.label = "press timeout",
},
{
.name = "azoteq,ati-mode",
.reg_addr[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x37,
},
.reg_shift = 15,
.reg_width = 1,
.label = "ATI mode",
},
{
.name = "azoteq,ati-mode",
.reg_addr[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x37,
},
.reg_shift = 7,
.reg_width = 1,
.label = "ATI mode",
},
{
.name = "azoteq,sense-mode",
.reg_addr[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x37,
[IQS7211A] = 0x72,
[IQS7211E] = 0x36,
},
.reg_shift = 8,
.reg_width = 1,
.label = "sensing mode",
},
{
.name = "azoteq,sense-mode",
.reg_addr[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x37,
},
.reg_shift = 0,
.reg_width = 2,
.val_max = 2,
.label = "sensing mode",
},
{
.name = "azoteq,fosc-freq",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x38,
[IQS7211A] = 0x52,
[IQS7211E] = 0x35,
},
.reg_shift = 4,
.reg_width = 1,
.label = "core clock frequency selection",
},
{
.name = "azoteq,fosc-trim",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x38,
[IQS7211A] = 0x52,
[IQS7211E] = 0x35,
},
.reg_shift = 0,
.reg_width = 4,
.label = "core clock frequency trim",
},
{
.name = "azoteq,touch-exit",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x3B,
[IQS7211A] = 0x53,
[IQS7211E] = 0x38,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x3E,
},
},
.reg_shift = 8,
.reg_width = 8,
.label = "touch exit factor",
},
{
.name = "azoteq,touch-enter",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x3B,
[IQS7211A] = 0x53,
[IQS7211E] = 0x38,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x3E,
},
},
.reg_shift = 0,
.reg_width = 8,
.label = "touch entrance factor",
},
{
.name = "azoteq,thresh",
.reg_addr = {
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x3C,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x3D,
[IQS7211A] = 0x54,
[IQS7211E] = 0x39,
},
},
.label = "threshold",
},
{
.name = "azoteq,debounce-exit",
.reg_addr = {
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x3F,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x40,
[IQS7211A] = 0x56,
[IQS7211E] = 0x3A,
},
},
.reg_shift = 8,
.reg_width = 8,
.label = "debounce exit factor",
},
{
.name = "azoteq,debounce-enter",
.reg_addr = {
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x3F,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x40,
[IQS7211A] = 0x56,
[IQS7211E] = 0x3A,
},
},
.reg_shift = 0,
.reg_width = 8,
.label = "debounce entrance factor",
},
{
.name = "azoteq,conv-frac",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x48,
[IQS7211A] = 0x58,
[IQS7211E] = 0x3D,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x49,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x4A,
[IQS7211A] = 0x59,
[IQS7211E] = 0x3E,
},
},
.reg_shift = 8,
.reg_width = 8,
.label = "conversion frequency fractional divider",
},
{
.name = "azoteq,conv-period",
.reg_addr = {
[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x48,
[IQS7211A] = 0x58,
[IQS7211E] = 0x3D,
},
[IQS7211_REG_GRP_BTN] = {
[IQS7210A] = 0x49,
},
[IQS7211_REG_GRP_ALP] = {
[IQS7210A] = 0x4A,
[IQS7211A] = 0x59,
[IQS7211E] = 0x3E,
},
},
.reg_shift = 0,
.reg_width = 8,
.label = "conversion period",
},
{
.name = "azoteq,thresh",
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x55,
[IQS7211A] = 0x67,
[IQS7211E] = 0x48,
},
.reg_shift = 0,
.reg_width = 8,
.label = "threshold",
},
{
.name = "azoteq,contact-split",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x55,
[IQS7211A] = 0x67,
[IQS7211E] = 0x48,
},
.reg_shift = 8,
.reg_width = 8,
.label = "contact split factor",
},
{
.name = "azoteq,trim-x",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x56,
[IQS7211E] = 0x49,
},
.reg_shift = 0,
.reg_width = 8,
.label = "horizontal trim width",
},
{
.name = "azoteq,trim-x",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7211A] = 0x68,
},
.label = "horizontal trim width",
},
{
.name = "azoteq,trim-y",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7210A] = 0x56,
[IQS7211E] = 0x49,
},
.reg_shift = 8,
.reg_width = 8,
.label = "vertical trim height",
},
{
.name = "azoteq,trim-y",
.reg_addr[IQS7211_REG_GRP_SYS] = {
[IQS7211A] = 0x69,
},
.label = "vertical trim height",
},
{
.name = "azoteq,gesture-max-ms",
.reg_key = IQS7211_REG_KEY_TAP,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x59,
[IQS7211A] = 0x81,
[IQS7211E] = 0x4C,
},
.label = "maximum gesture time",
},
{
.name = "azoteq,gesture-mid-ms",
.reg_key = IQS7211_REG_KEY_TAP,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7211E] = 0x4D,
},
.label = "repeated gesture time",
},
{
.name = "azoteq,gesture-dist",
.reg_key = IQS7211_REG_KEY_TAP,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x5A,
[IQS7211A] = 0x82,
[IQS7211E] = 0x4E,
},
.label = "gesture distance",
},
{
.name = "azoteq,gesture-dist",
.reg_key = IQS7211_REG_KEY_HOLD,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x5A,
[IQS7211A] = 0x82,
[IQS7211E] = 0x4E,
},
.label = "gesture distance",
},
{
.name = "azoteq,gesture-min-ms",
.reg_key = IQS7211_REG_KEY_HOLD,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x5B,
[IQS7211A] = 0x83,
[IQS7211E] = 0x4F,
},
.label = "minimum gesture time",
},
{
.name = "azoteq,gesture-max-ms",
.reg_key = IQS7211_REG_KEY_AXIAL_X,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x5C,
[IQS7211A] = 0x84,
[IQS7211E] = 0x50,
},
.label = "maximum gesture time",
},
{
.name = "azoteq,gesture-max-ms",
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x5C,
[IQS7211A] = 0x84,
[IQS7211E] = 0x50,
},
.label = "maximum gesture time",
},
{
.name = "azoteq,gesture-dist",
.reg_key = IQS7211_REG_KEY_AXIAL_X,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x5D,
[IQS7211A] = 0x85,
[IQS7211E] = 0x51,
},
.label = "gesture distance",
},
{
.name = "azoteq,gesture-dist",
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7210A] = 0x5E,
[IQS7211A] = 0x86,
[IQS7211E] = 0x52,
},
.label = "gesture distance",
},
{
.name = "azoteq,gesture-dist-rep",
.reg_key = IQS7211_REG_KEY_AXIAL_X,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7211E] = 0x53,
},
.label = "repeated gesture distance",
},
{
.name = "azoteq,gesture-dist-rep",
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7211E] = 0x54,
},
.label = "repeated gesture distance",
},
{
.name = "azoteq,thresh",
.reg_key = IQS7211_REG_KEY_PALM,
.reg_addr[IQS7211_REG_GRP_TP] = {
[IQS7211E] = 0x55,
},
.reg_shift = 8,
.reg_width = 8,
.val_max = 42,
.label = "threshold",
},
};
static const u8 iqs7211_gesture_angle[] = {
0x00, 0x01, 0x02, 0x03,
0x04, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C,
0x0E, 0x0F, 0x10, 0x11,
0x12, 0x14, 0x15, 0x16,
0x17, 0x19, 0x1A, 0x1B,
0x1C, 0x1E, 0x1F, 0x21,
0x22, 0x23, 0x25, 0x26,
0x28, 0x2A, 0x2B, 0x2D,
0x2E, 0x30, 0x32, 0x34,
0x36, 0x38, 0x3A, 0x3C,
0x3E, 0x40, 0x42, 0x45,
0x47, 0x4A, 0x4C, 0x4F,
0x52, 0x55, 0x58, 0x5B,
0x5F, 0x63, 0x66, 0x6B,
0x6F, 0x73, 0x78, 0x7E,
0x83, 0x89, 0x90, 0x97,
0x9E, 0xA7, 0xB0, 0xBA,
0xC5, 0xD1, 0xDF, 0xEF,
};
struct iqs7211_ver_info {
__le16 prod_num;
__le16 major;
__le16 minor;
__le32 patch;
} __packed;
struct iqs7211_touch_data {
__le16 abs_x;
__le16 abs_y;
__le16 pressure;
__le16 area;
} __packed;
struct iqs7211_tp_config {
u8 tp_settings;
u8 total_rx;
u8 total_tx;
u8 num_contacts;
__le16 max_x;
__le16 max_y;
} __packed;
struct iqs7211_private {
const struct iqs7211_dev_desc *dev_desc;
struct gpio_desc *reset_gpio;
struct gpio_desc *irq_gpio;
struct i2c_client *client;
struct input_dev *tp_idev;
struct input_dev *kp_idev;
struct iqs7211_ver_info ver_info;
struct iqs7211_tp_config tp_config;
struct touchscreen_properties prop;
struct list_head reg_field_head;
enum iqs7211_comms_mode comms_init;
enum iqs7211_comms_mode comms_mode;
unsigned int num_contacts;
unsigned int kp_code[ARRAY_SIZE(iqs7211e_kp_events)];
u8 rx_tx_map[IQS7211_MAX_CTX + 1];
u8 cycle_alloc[2][33];
u8 exp_file[2];
u16 event_mask;
u16 ati_start;
u16 gesture_cache;
};
static int iqs7211_irq_poll(struct iqs7211_private *iqs7211, u64 timeout_us)
{
int error, val;
error = readx_poll_timeout(gpiod_get_value_cansleep, iqs7211->irq_gpio,
val, val, IQS7211_COMMS_SLEEP_US, timeout_us);
return val < 0 ? val : error;
}
static int iqs7211_hard_reset(struct iqs7211_private *iqs7211)
{
if (!iqs7211->reset_gpio)
return 0;
gpiod_set_value_cansleep(iqs7211->reset_gpio, 1);
if (iqs7211->reset_gpio == iqs7211->irq_gpio)
msleep(IQS7211_RESET_TIMEOUT_MS);
else
usleep_range(1000, 1100);
gpiod_set_value_cansleep(iqs7211->reset_gpio, 0);
if (iqs7211->reset_gpio == iqs7211->irq_gpio)
iqs7211_irq_wait();
return iqs7211_irq_poll(iqs7211, IQS7211_START_TIMEOUT_US);
}
static int iqs7211_force_comms(struct iqs7211_private *iqs7211)
{
u8 msg_buf[] = { 0xFF, };
int ret;
switch (iqs7211->comms_mode) {
case IQS7211_COMMS_MODE_WAIT:
return iqs7211_irq_poll(iqs7211, IQS7211_START_TIMEOUT_US);
case IQS7211_COMMS_MODE_FREE:
return 0;
case IQS7211_COMMS_MODE_FORCE:
break;
default:
return -EINVAL;
}
ret = gpiod_get_value_cansleep(iqs7211->irq_gpio);
if (ret < 0)
return ret;
else if (ret > 0)
return 0;
ret = i2c_master_send(iqs7211->client, msg_buf, sizeof(msg_buf));
if (ret < (int)sizeof(msg_buf)) {
if (ret >= 0)
ret = -EIO;
msleep(IQS7211_COMMS_RETRY_MS);
return ret;
}
iqs7211_irq_wait();
return iqs7211_irq_poll(iqs7211, IQS7211_COMMS_TIMEOUT_US);
}
static int iqs7211_read_burst(struct iqs7211_private *iqs7211,
u8 reg, void *val, u16 val_len)
{
int ret, i;
struct i2c_client *client = iqs7211->client;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = sizeof(reg),
.buf = ®,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = val_len,
.buf = (u8 *)val,
},
};
for (i = 0; i < IQS7211_NUM_RETRIES; i++) {
ret = iqs7211_force_comms(iqs7211);
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(IQS7211_COMMS_RETRY_MS);
continue;
}
if (get_unaligned_le16(msg[1].buf) == IQS7211_COMMS_ERROR) {
ret = -ENODATA;
continue;
}
ret = 0;
break;
}
iqs7211_irq_wait();
if (ret < 0)
dev_err(&client->dev,
"Failed to read from address 0x%02X: %d\n", reg, ret);
return ret;
}
static int iqs7211_read_word(struct iqs7211_private *iqs7211, u8 reg, u16 *val)
{
__le16 val_buf;
int error;
error = iqs7211_read_burst(iqs7211, reg, &val_buf, sizeof(val_buf));
if (error)
return error;
*val = le16_to_cpu(val_buf);
return 0;
}
static int iqs7211_write_burst(struct iqs7211_private *iqs7211,
u8 reg, const void *val, u16 val_len)
{
int msg_len = sizeof(reg) + val_len;
int ret, i;
struct i2c_client *client = iqs7211->client;
u8 *msg_buf;
msg_buf = kzalloc(msg_len, GFP_KERNEL);
if (!msg_buf)
return -ENOMEM;
*msg_buf = reg;
memcpy(msg_buf + sizeof(reg), val, val_len);
for (i = 0; i < IQS7211_NUM_RETRIES; i++) {
ret = iqs7211_force_comms(iqs7211);
if (ret < 0)
continue;
ret = i2c_master_send(client, msg_buf, msg_len);
if (ret < msg_len) {
if (ret >= 0)
ret = -EIO;
msleep(IQS7211_COMMS_RETRY_MS);
continue;
}
ret = 0;
break;
}
kfree(msg_buf);
iqs7211_irq_wait();
if (ret < 0)
dev_err(&client->dev,
"Failed to write to address 0x%02X: %d\n", reg, ret);
return ret;
}
static int iqs7211_write_word(struct iqs7211_private *iqs7211, u8 reg, u16 val)
{
__le16 val_buf = cpu_to_le16(val);
return iqs7211_write_burst(iqs7211, reg, &val_buf, sizeof(val_buf));
}
static int iqs7211_start_comms(struct iqs7211_private *iqs7211)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct i2c_client *client = iqs7211->client;
bool forced_comms;
unsigned int val;
u16 comms_setup;
int error;
forced_comms = device_property_present(&client->dev,
"azoteq,forced-comms");
error = device_property_read_u32(&client->dev,
"azoteq,forced-comms-default", &val);
if (error == -EINVAL) {
iqs7211->comms_init = IQS7211_COMMS_MODE_WAIT;
} else if (error) {
dev_err(&client->dev,
"Failed to read default communication mode: %d\n",
error);
return error;
} else if (val) {
iqs7211->comms_init = forced_comms ? IQS7211_COMMS_MODE_FORCE
: IQS7211_COMMS_MODE_WAIT;
} else {
iqs7211->comms_init = forced_comms ? IQS7211_COMMS_MODE_WAIT
: IQS7211_COMMS_MODE_FREE;
}
iqs7211->comms_mode = iqs7211->comms_init;
error = iqs7211_hard_reset(iqs7211);
if (error) {
dev_err(&client->dev, "Failed to reset device: %d\n", error);
return error;
}
error = iqs7211_read_burst(iqs7211, IQS7211_PROD_NUM,
&iqs7211->ver_info,
sizeof(iqs7211->ver_info));
if (error)
return error;
if (le16_to_cpu(iqs7211->ver_info.prod_num) != dev_desc->prod_num) {
dev_err(&client->dev, "Invalid product number: %u\n",
le16_to_cpu(iqs7211->ver_info.prod_num));
return -EINVAL;
}
error = iqs7211_read_word(iqs7211, dev_desc->sys_ctrl + 1,
&comms_setup);
if (error)
return error;
if (forced_comms)
comms_setup |= dev_desc->comms_req;
else
comms_setup &= ~dev_desc->comms_req;
error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
comms_setup | dev_desc->comms_end);
if (error)
return error;
if (forced_comms)
iqs7211->comms_mode = IQS7211_COMMS_MODE_FORCE;
else
iqs7211->comms_mode = IQS7211_COMMS_MODE_FREE;
error = iqs7211_read_burst(iqs7211, dev_desc->exp_file,
iqs7211->exp_file,
sizeof(iqs7211->exp_file));
if (error)
return error;
error = iqs7211_read_burst(iqs7211, dev_desc->tp_config,
&iqs7211->tp_config,
sizeof(iqs7211->tp_config));
if (error)
return error;
error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
comms_setup);
if (error)
return error;
iqs7211->event_mask = comms_setup & ~IQS7211_EVENT_MASK_ALL;
iqs7211->event_mask |= (IQS7211_EVENT_MASK_ATI | IQS7211_EVENT_MODE);
return 0;
}
static int iqs7211_init_device(struct iqs7211_private *iqs7211)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct iqs7211_reg_field_desc *reg_field;
__le16 sys_ctrl[] = {
cpu_to_le16(dev_desc->ack_reset),
cpu_to_le16(iqs7211->event_mask),
};
int error, i;
error = iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
sizeof(sys_ctrl));
if (error)
return error;
if (iqs7211->event_mask & dev_desc->comms_req)
iqs7211->comms_mode = IQS7211_COMMS_MODE_FORCE;
else
iqs7211->comms_mode = IQS7211_COMMS_MODE_FREE;
error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
iqs7211->event_mask | dev_desc->comms_end);
if (error)
return error;
list_for_each_entry(reg_field, &iqs7211->reg_field_head, list) {
u16 new_val = reg_field->val;
if (reg_field->mask < U16_MAX) {
u16 old_val;
error = iqs7211_read_word(iqs7211, reg_field->addr,
&old_val);
if (error)
return error;
new_val = old_val & ~reg_field->mask;
new_val |= reg_field->val;
if (new_val == old_val)
continue;
}
error = iqs7211_write_word(iqs7211, reg_field->addr, new_val);
if (error)
return error;
}
error = iqs7211_write_burst(iqs7211, dev_desc->tp_config,
&iqs7211->tp_config,
sizeof(iqs7211->tp_config));
if (error)
return error;
if (**iqs7211->cycle_alloc) {
error = iqs7211_write_burst(iqs7211, dev_desc->rx_tx_map,
&iqs7211->rx_tx_map,
dev_desc->num_ctx);
if (error)
return error;
for (i = 0; i < sizeof(dev_desc->cycle_limit); i++) {
error = iqs7211_write_burst(iqs7211,
dev_desc->cycle_alloc[i],
iqs7211->cycle_alloc[i],
dev_desc->cycle_limit[i] * 3);
if (error)
return error;
}
}
*sys_ctrl = cpu_to_le16(iqs7211->ati_start);
return iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
sizeof(sys_ctrl));
}
static int iqs7211_add_field(struct iqs7211_private *iqs7211,
struct iqs7211_reg_field_desc new_field)
{
struct i2c_client *client = iqs7211->client;
struct iqs7211_reg_field_desc *reg_field;
if (!new_field.addr)
return 0;
list_for_each_entry(reg_field, &iqs7211->reg_field_head, list) {
if (reg_field->addr != new_field.addr)
continue;
reg_field->mask |= new_field.mask;
reg_field->val |= new_field.val;
return 0;
}
reg_field = devm_kzalloc(&client->dev, sizeof(*reg_field), GFP_KERNEL);
if (!reg_field)
return -ENOMEM;
reg_field->addr = new_field.addr;
reg_field->mask = new_field.mask;
reg_field->val = new_field.val;
list_add(®_field->list, &iqs7211->reg_field_head);
return 0;
}
static int iqs7211_parse_props(struct iqs7211_private *iqs7211,
struct fwnode_handle *reg_grp_node,
enum iqs7211_reg_grp_id reg_grp,
enum iqs7211_reg_key_id reg_key)
{
struct i2c_client *client = iqs7211->client;
int i;
for (i = 0; i < ARRAY_SIZE(iqs7211_props); i++) {
const char *name = iqs7211_props[i].name;
u8 reg_addr = iqs7211_props[i].reg_addr[reg_grp]
[iqs7211->dev_desc -
iqs7211_devs];
int reg_shift = iqs7211_props[i].reg_shift;
int reg_width = iqs7211_props[i].reg_width ? : 16;
int val_pitch = iqs7211_props[i].val_pitch ? : 1;
int val_min = iqs7211_props[i].val_min;
int val_max = iqs7211_props[i].val_max;
const char *label = iqs7211_props[i].label ? : name;
struct iqs7211_reg_field_desc reg_field;
unsigned int val;
int error;
if (iqs7211_props[i].reg_key != reg_key)
continue;
if (!reg_addr)
continue;
error = fwnode_property_read_u32(reg_grp_node, name, &val);
if (error == -EINVAL) {
continue;
} else 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: %u\n", label, val);
return -EINVAL;
}
reg_field.addr = reg_addr;
reg_field.mask = GENMASK(reg_shift + reg_width - 1, reg_shift);
reg_field.val = val / val_pitch << reg_shift;
error = iqs7211_add_field(iqs7211, reg_field);
if (error)
return error;
}
return 0;
}
static int iqs7211_parse_event(struct iqs7211_private *iqs7211,
struct fwnode_handle *event_node,
enum iqs7211_reg_grp_id reg_grp,
enum iqs7211_reg_key_id reg_key,
unsigned int *event_code)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct i2c_client *client = iqs7211->client;
struct iqs7211_reg_field_desc reg_field;
unsigned int val;
int error;
error = iqs7211_parse_props(iqs7211, event_node, reg_grp, reg_key);
if (error)
return error;
if (reg_key == IQS7211_REG_KEY_AXIAL_X ||
reg_key == IQS7211_REG_KEY_AXIAL_Y) {
error = fwnode_property_read_u32(event_node,
"azoteq,gesture-angle", &val);
if (!error) {
if (val >= ARRAY_SIZE(iqs7211_gesture_angle)) {
dev_err(&client->dev,
"Invalid %s gesture angle: %u\n",
fwnode_get_name(event_node), val);
return -EINVAL;
}
reg_field.addr = dev_desc->gesture_angle;
reg_field.mask = U8_MAX;
reg_field.val = iqs7211_gesture_angle[val];
error = iqs7211_add_field(iqs7211, reg_field);
if (error)
return error;
} else if (error != -EINVAL) {
dev_err(&client->dev,
"Failed to read %s gesture angle: %d\n",
fwnode_get_name(event_node), error);
return error;
}
}
error = fwnode_property_read_u32(event_node, "linux,code", event_code);
if (error == -EINVAL)
error = 0;
else if (error)
dev_err(&client->dev, "Failed to read %s code: %d\n",
fwnode_get_name(event_node), error);
return error;
}
static int iqs7211_parse_cycles(struct iqs7211_private *iqs7211,
struct fwnode_handle *tp_node)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct i2c_client *client = iqs7211->client;
int num_cycles = dev_desc->cycle_limit[0] + dev_desc->cycle_limit[1];
int error, count, i, j, k, cycle_start;
unsigned int cycle_alloc[IQS7211_MAX_CYCLES][2];
u8 total_rx = iqs7211->tp_config.total_rx;
u8 total_tx = iqs7211->tp_config.total_tx;
for (i = 0; i < IQS7211_MAX_CYCLES * 2; i++)
*(cycle_alloc[0] + i) = U8_MAX;
count = fwnode_property_count_u32(tp_node, "azoteq,channel-select");
if (count == -EINVAL) {
for (i = 0, cycle_start = 0; i < total_tx; i++) {
int cycle_stop = 0;
for (j = 0; j < total_rx; j++) {
int slot = iqs7211->rx_tx_map[j] < 4 ? 0 : 1;
int chan = i * total_rx + j;
for (k = cycle_start; k < num_cycles; k++) {
if (cycle_alloc[k][slot] < U8_MAX)
continue;
cycle_alloc[k][slot] = chan;
break;
}
if (k < num_cycles) {
cycle_stop = max(k, cycle_stop);
continue;
}
dev_err(&client->dev,
"Insufficient number of cycles\n");
return -EINVAL;
}
cycle_start = cycle_stop + 1;
}
} else if (count < 0) {
dev_err(&client->dev, "Failed to count channels: %d\n", count);
return count;
} else if (count > num_cycles * 2) {
dev_err(&client->dev, "Insufficient number of cycles\n");
return -EINVAL;
} else if (count > 0) {
error = fwnode_property_read_u32_array(tp_node,
"azoteq,channel-select",
cycle_alloc[0], count);
if (error) {
dev_err(&client->dev, "Failed to read channels: %d\n",
error);
return error;
}
for (i = 0; i < count; i++) {
int chan = *(cycle_alloc[0] + i);
if (chan == U8_MAX)
continue;
if (chan >= total_rx * total_tx) {
dev_err(&client->dev, "Invalid channel: %d\n",
chan);
return -EINVAL;
}
for (j = 0; j < count; j++) {
if (j == i || *(cycle_alloc[0] + j) != chan)
continue;
dev_err(&client->dev, "Duplicate channel: %d\n",
chan);
return -EINVAL;
}
}
}
for (i = 0, cycle_start = 0; i < sizeof(dev_desc->cycle_limit); i++) {
int offs = 0;
for (j = cycle_start;
j < cycle_start + dev_desc->cycle_limit[i]; j++) {
iqs7211->cycle_alloc[i][offs++] = 0x05;
iqs7211->cycle_alloc[i][offs++] = cycle_alloc[j][0];
iqs7211->cycle_alloc[i][offs++] = cycle_alloc[j][1];
}
cycle_start += dev_desc->cycle_limit[i];
}
return 0;
}
static int iqs7211_parse_tp(struct iqs7211_private *iqs7211,
struct fwnode_handle *tp_node)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct i2c_client *client = iqs7211->client;
unsigned int pins[IQS7211_MAX_CTX];
int error, count, i, j;
count = fwnode_property_count_u32(tp_node, "azoteq,rx-enable");
if (count == -EINVAL) {
return 0;
} else if (count < 0) {
dev_err(&client->dev, "Failed to count CRx pins: %d\n", count);
return count;
} else if (count > IQS7211_NUM_CRX) {
dev_err(&client->dev, "Invalid number of CRx pins\n");
return -EINVAL;
}
error = fwnode_property_read_u32_array(tp_node, "azoteq,rx-enable",
pins, count);
if (error) {
dev_err(&client->dev, "Failed to read CRx pins: %d\n", error);
return error;
}
for (i = 0; i < count; i++) {
if (pins[i] >= IQS7211_NUM_CRX) {
dev_err(&client->dev, "Invalid CRx pin: %u\n", pins[i]);
return -EINVAL;
}
iqs7211->rx_tx_map[i] = pins[i];
}
iqs7211->tp_config.total_rx = count;
count = fwnode_property_count_u32(tp_node, "azoteq,tx-enable");
if (count < 0) {
dev_err(&client->dev, "Failed to count CTx pins: %d\n", count);
return count;
} else if (count > dev_desc->num_ctx) {
dev_err(&client->dev, "Invalid number of CTx pins\n");
return -EINVAL;
}
error = fwnode_property_read_u32_array(tp_node, "azoteq,tx-enable",
pins, count);
if (error) {
dev_err(&client->dev, "Failed to read CTx pins: %d\n", error);
return error;
}
for (i = 0; i < count; i++) {
if (pins[i] >= dev_desc->num_ctx) {
dev_err(&client->dev, "Invalid CTx pin: %u\n", pins[i]);
return -EINVAL;
}
for (j = 0; j < iqs7211->tp_config.total_rx; j++) {
if (iqs7211->rx_tx_map[j] != pins[i])
continue;
dev_err(&client->dev, "Conflicting CTx pin: %u\n",
pins[i]);
return -EINVAL;
}
iqs7211->rx_tx_map[iqs7211->tp_config.total_rx + i] = pins[i];
}
iqs7211->tp_config.total_tx = count;
return iqs7211_parse_cycles(iqs7211, tp_node);
}
static int iqs7211_parse_alp(struct iqs7211_private *iqs7211,
struct fwnode_handle *alp_node)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct i2c_client *client = iqs7211->client;
struct iqs7211_reg_field_desc reg_field;
int error, count, i;
count = fwnode_property_count_u32(alp_node, "azoteq,rx-enable");
if (count < 0 && count != -EINVAL) {
dev_err(&client->dev, "Failed to count CRx pins: %d\n", count);
return count;
} else if (count > IQS7211_NUM_CRX) {
dev_err(&client->dev, "Invalid number of CRx pins\n");
return -EINVAL;
} else if (count >= 0) {
unsigned int pins[IQS7211_NUM_CRX];
error = fwnode_property_read_u32_array(alp_node,
"azoteq,rx-enable",
pins, count);
if (error) {
dev_err(&client->dev, "Failed to read CRx pins: %d\n",
error);
return error;
}
reg_field.addr = dev_desc->alp_config;
reg_field.mask = GENMASK(IQS7211_NUM_CRX - 1, 0);
reg_field.val = 0;
for (i = 0; i < count; i++) {
if (pins[i] < dev_desc->min_crx_alp ||
pins[i] >= IQS7211_NUM_CRX) {
dev_err(&client->dev, "Invalid CRx pin: %u\n",
pins[i]);
return -EINVAL;
}
reg_field.val |= BIT(pins[i]);
}
error = iqs7211_add_field(iqs7211, reg_field);
if (error)
return error;
}
count = fwnode_property_count_u32(alp_node, "azoteq,tx-enable");
if (count < 0 && count != -EINVAL) {
dev_err(&client->dev, "Failed to count CTx pins: %d\n", count);
return count;
} else if (count > dev_desc->num_ctx) {
dev_err(&client->dev, "Invalid number of CTx pins\n");
return -EINVAL;
} else if (count >= 0) {
unsigned int pins[IQS7211_MAX_CTX];
error = fwnode_property_read_u32_array(alp_node,
"azoteq,tx-enable",
pins, count);
if (error) {
dev_err(&client->dev, "Failed to read CTx pins: %d\n",
error);
return error;
}
reg_field.addr = dev_desc->alp_config + 1;
reg_field.mask = GENMASK(dev_desc->num_ctx - 1, 0);
reg_field.val = 0;
for (i = 0; i < count; i++) {
if (pins[i] >= dev_desc->num_ctx) {
dev_err(&client->dev, "Invalid CTx pin: %u\n",
pins[i]);
return -EINVAL;
}
reg_field.val |= BIT(pins[i]);
}
error = iqs7211_add_field(iqs7211, reg_field);
if (error)
return error;
}
return 0;
}
static int (*iqs7211_parse_extra[IQS7211_NUM_REG_GRPS])
(struct iqs7211_private *iqs7211,
struct fwnode_handle *reg_grp_node) = {
[IQS7211_REG_GRP_TP] = iqs7211_parse_tp,
[IQS7211_REG_GRP_ALP] = iqs7211_parse_alp,
};
static int iqs7211_parse_reg_grp(struct iqs7211_private *iqs7211,
struct fwnode_handle *reg_grp_node,
enum iqs7211_reg_grp_id reg_grp)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct iqs7211_reg_field_desc reg_field;
int error, i;
error = iqs7211_parse_props(iqs7211, reg_grp_node, reg_grp,
IQS7211_REG_KEY_NONE);
if (error)
return error;
if (iqs7211_parse_extra[reg_grp]) {
error = iqs7211_parse_extra[reg_grp](iqs7211, reg_grp_node);
if (error)
return error;
}
iqs7211->ati_start |= dev_desc->ati_start[reg_grp];
reg_field.addr = dev_desc->kp_enable[reg_grp];
reg_field.mask = 0;
reg_field.val = 0;
for (i = 0; i < dev_desc->num_kp_events; i++) {
const char *event_name = dev_desc->kp_events[i].name;
struct fwnode_handle *event_node;
if (dev_desc->kp_events[i].reg_grp != reg_grp)
continue;
reg_field.mask |= dev_desc->kp_events[i].enable;
if (event_name)
event_node = fwnode_get_named_child_node(reg_grp_node,
event_name);
else
event_node = fwnode_handle_get(reg_grp_node);
if (!event_node)
continue;
error = iqs7211_parse_event(iqs7211, event_node,
dev_desc->kp_events[i].reg_grp,
dev_desc->kp_events[i].reg_key,
&iqs7211->kp_code[i]);
fwnode_handle_put(event_node);
if (error)
return error;
reg_field.val |= dev_desc->kp_events[i].enable;
iqs7211->event_mask |= iqs7211_reg_grp_masks[reg_grp];
}
return iqs7211_add_field(iqs7211, reg_field);
}
static int iqs7211_register_kp(struct iqs7211_private *iqs7211)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct input_dev *kp_idev = iqs7211->kp_idev;
struct i2c_client *client = iqs7211->client;
int error, i;
for (i = 0; i < dev_desc->num_kp_events; i++)
if (iqs7211->kp_code[i])
break;
if (i == dev_desc->num_kp_events)
return 0;
kp_idev = devm_input_allocate_device(&client->dev);
if (!kp_idev)
return -ENOMEM;
iqs7211->kp_idev = kp_idev;
kp_idev->name = dev_desc->kp_name;
kp_idev->id.bustype = BUS_I2C;
for (i = 0; i < dev_desc->num_kp_events; i++)
if (iqs7211->kp_code[i])
input_set_capability(iqs7211->kp_idev, EV_KEY,
iqs7211->kp_code[i]);
error = input_register_device(kp_idev);
if (error)
dev_err(&client->dev, "Failed to register %s: %d\n",
kp_idev->name, error);
return error;
}
static int iqs7211_register_tp(struct iqs7211_private *iqs7211)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct touchscreen_properties *prop = &iqs7211->prop;
struct input_dev *tp_idev = iqs7211->tp_idev;
struct i2c_client *client = iqs7211->client;
int error;
error = device_property_read_u32(&client->dev, "azoteq,num-contacts",
&iqs7211->num_contacts);
if (error == -EINVAL) {
return 0;
} else if (error) {
dev_err(&client->dev, "Failed to read number of contacts: %d\n",
error);
return error;
} else if (iqs7211->num_contacts > IQS7211_MAX_CONTACTS) {
dev_err(&client->dev, "Invalid number of contacts: %u\n",
iqs7211->num_contacts);
return -EINVAL;
}
iqs7211->tp_config.num_contacts = iqs7211->num_contacts ? : 1;
if (!iqs7211->num_contacts)
return 0;
iqs7211->event_mask |= IQS7211_EVENT_MASK_MOVE;
tp_idev = devm_input_allocate_device(&client->dev);
if (!tp_idev)
return -ENOMEM;
iqs7211->tp_idev = tp_idev;
tp_idev->name = dev_desc->tp_name;
tp_idev->id.bustype = BUS_I2C;
input_set_abs_params(tp_idev, ABS_MT_POSITION_X,
0, le16_to_cpu(iqs7211->tp_config.max_x), 0, 0);
input_set_abs_params(tp_idev, ABS_MT_POSITION_Y,
0, le16_to_cpu(iqs7211->tp_config.max_y), 0, 0);
input_set_abs_params(tp_idev, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
touchscreen_parse_properties(tp_idev, true, 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;
}
iqs7211->tp_config.max_x = cpu_to_le16(prop->max_x);
iqs7211->tp_config.max_y = cpu_to_le16(prop->max_y);
error = input_mt_init_slots(tp_idev, iqs7211->num_contacts,
INPUT_MT_DIRECT);
if (error) {
dev_err(&client->dev, "Failed to initialize slots: %d\n",
error);
return error;
}
error = input_register_device(tp_idev);
if (error)
dev_err(&client->dev, "Failed to register %s: %d\n",
tp_idev->name, error);
return error;
}
static int iqs7211_report(struct iqs7211_private *iqs7211)
{
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
struct i2c_client *client = iqs7211->client;
struct iqs7211_touch_data *touch_data;
u16 info_flags, charge_mode, gesture_flags;
__le16 status[12];
int error, i;
error = iqs7211_read_burst(iqs7211, dev_desc->sys_stat, status,
dev_desc->contact_offs * sizeof(__le16) +
iqs7211->num_contacts * sizeof(*touch_data));
if (error)
return error;
info_flags = le16_to_cpu(status[dev_desc->info_offs]);
if (info_flags & dev_desc->show_reset) {
dev_err(&client->dev, "Unexpected device reset\n");
iqs7211->comms_mode = iqs7211->comms_init;
return iqs7211_init_device(iqs7211);
}
for (i = 0; i < ARRAY_SIZE(dev_desc->ati_error); i++) {
if (!(info_flags & dev_desc->ati_error[i]))
continue;
dev_err(&client->dev, "Unexpected %s ATI error\n",
iqs7211_reg_grp_names[i]);
return 0;
}
for (i = 0; i < iqs7211->num_contacts; i++) {
u16 pressure;
touch_data = (struct iqs7211_touch_data *)
&status[dev_desc->contact_offs] + i;
pressure = le16_to_cpu(touch_data->pressure);
input_mt_slot(iqs7211->tp_idev, i);
if (input_mt_report_slot_state(iqs7211->tp_idev, MT_TOOL_FINGER,
pressure != 0)) {
touchscreen_report_pos(iqs7211->tp_idev, &iqs7211->prop,
le16_to_cpu(touch_data->abs_x),
le16_to_cpu(touch_data->abs_y),
true);
input_report_abs(iqs7211->tp_idev, ABS_MT_PRESSURE,
pressure);
}
}
if (iqs7211->num_contacts) {
input_mt_sync_frame(iqs7211->tp_idev);
input_sync(iqs7211->tp_idev);
}
if (!iqs7211->kp_idev)
return 0;
charge_mode = info_flags & GENMASK(dev_desc->charge_shift + 2,
dev_desc->charge_shift);
charge_mode >>= dev_desc->charge_shift;
if (info_flags & dev_desc->kp_events->mask && charge_mode > 2) {
input_report_key(iqs7211->kp_idev, *iqs7211->kp_code, 1);
input_sync(iqs7211->kp_idev);
input_report_key(iqs7211->kp_idev, *iqs7211->kp_code, 0);
}
for (i = 0; i < dev_desc->num_kp_events; i++) {
if (dev_desc->kp_events[i].reg_grp != IQS7211_REG_GRP_BTN)
continue;
input_report_key(iqs7211->kp_idev, iqs7211->kp_code[i],
info_flags & dev_desc->kp_events[i].mask);
}
gesture_flags = le16_to_cpu(status[dev_desc->gesture_offs]);
for (i = 0; i < dev_desc->num_kp_events; i++) {
enum iqs7211_reg_key_id reg_key = dev_desc->kp_events[i].reg_key;
u16 mask = dev_desc->kp_events[i].mask;
if (dev_desc->kp_events[i].reg_grp != IQS7211_REG_GRP_TP)
continue;
if ((gesture_flags ^ iqs7211->gesture_cache) & mask)
input_report_key(iqs7211->kp_idev, iqs7211->kp_code[i],
gesture_flags & mask);
iqs7211->gesture_cache &= ~mask;
if (reg_key == IQS7211_REG_KEY_HOLD ||
reg_key == IQS7211_REG_KEY_PALM) {
iqs7211->gesture_cache |= gesture_flags & mask;
gesture_flags &= ~mask;
}
}
if (gesture_flags) {
input_sync(iqs7211->kp_idev);
for (i = 0; i < dev_desc->num_kp_events; i++)
if (dev_desc->kp_events[i].reg_grp == IQS7211_REG_GRP_TP &&
gesture_flags & dev_desc->kp_events[i].mask)
input_report_key(iqs7211->kp_idev,
iqs7211->kp_code[i], 0);
}
input_sync(iqs7211->kp_idev);
return 0;
}
static irqreturn_t iqs7211_irq(int irq, void *context)
{
struct iqs7211_private *iqs7211 = context;
return iqs7211_report(iqs7211) ? IRQ_NONE : IRQ_HANDLED;
}
static int iqs7211_suspend(struct device *dev)
{
struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
int error;
if (!dev_desc->suspend || device_may_wakeup(dev))
return 0;
disable_irq(gpiod_to_irq(iqs7211->irq_gpio));
error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl,
dev_desc->suspend);
enable_irq(gpiod_to_irq(iqs7211->irq_gpio));
return error;
}
static int iqs7211_resume(struct device *dev)
{
struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
__le16 sys_ctrl[] = {
0,
cpu_to_le16(iqs7211->event_mask),
};
int error;
if (!dev_desc->suspend || device_may_wakeup(dev))
return 0;
disable_irq(gpiod_to_irq(iqs7211->irq_gpio));
error = iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
sizeof(sys_ctrl));
enable_irq(gpiod_to_irq(iqs7211->irq_gpio));
return error;
}
static DEFINE_SIMPLE_DEV_PM_OPS(iqs7211_pm, iqs7211_suspend, iqs7211_resume);
static ssize_t fw_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u:%u.%u\n",
le16_to_cpu(iqs7211->ver_info.prod_num),
le32_to_cpu(iqs7211->ver_info.patch),
le16_to_cpu(iqs7211->ver_info.major),
le16_to_cpu(iqs7211->ver_info.minor),
iqs7211->exp_file[1], iqs7211->exp_file[0]);
}
static DEVICE_ATTR_RO(fw_info);
static struct attribute *iqs7211_attrs[] = {
&dev_attr_fw_info.attr,
NULL
};
ATTRIBUTE_GROUPS(iqs7211);
static const struct of_device_id iqs7211_of_match[] = {
{
.compatible = "azoteq,iqs7210a",
.data = &iqs7211_devs[IQS7210A],
},
{
.compatible = "azoteq,iqs7211a",
.data = &iqs7211_devs[IQS7211A],
},
{
.compatible = "azoteq,iqs7211e",
.data = &iqs7211_devs[IQS7211E],
},
{ }
};
MODULE_DEVICE_TABLE(of, iqs7211_of_match);
static int iqs7211_probe(struct i2c_client *client)
{
struct iqs7211_private *iqs7211;
enum iqs7211_reg_grp_id reg_grp;
unsigned long irq_flags;
bool shared_irq;
int error, irq;
iqs7211 = devm_kzalloc(&client->dev, sizeof(*iqs7211), GFP_KERNEL);
if (!iqs7211)
return -ENOMEM;
i2c_set_clientdata(client, iqs7211);
iqs7211->client = client;
INIT_LIST_HEAD(&iqs7211->reg_field_head);
iqs7211->dev_desc = device_get_match_data(&client->dev);
if (!iqs7211->dev_desc)
return -ENODEV;
shared_irq = iqs7211->dev_desc->num_ctx == IQS7211_MAX_CTX;
iqs7211->irq_gpio = devm_gpiod_get(&client->dev, "irq",
shared_irq ? GPIOD_OUT_LOW
: GPIOD_IN);
if (IS_ERR(iqs7211->irq_gpio)) {
error = PTR_ERR(iqs7211->irq_gpio);
dev_err(&client->dev, "Failed to request IRQ GPIO: %d\n",
error);
return error;
}
if (shared_irq) {
iqs7211->reset_gpio = iqs7211->irq_gpio;
} else {
iqs7211->reset_gpio = devm_gpiod_get_optional(&client->dev,
"reset",
GPIOD_OUT_HIGH);
if (IS_ERR(iqs7211->reset_gpio)) {
error = PTR_ERR(iqs7211->reset_gpio);
dev_err(&client->dev,
"Failed to request reset GPIO: %d\n", error);
return error;
}
}
error = iqs7211_start_comms(iqs7211);
if (error)
return error;
for (reg_grp = 0; reg_grp < IQS7211_NUM_REG_GRPS; reg_grp++) {
const char *reg_grp_name = iqs7211_reg_grp_names[reg_grp];
struct fwnode_handle *reg_grp_node;
if (reg_grp_name)
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)
continue;
error = iqs7211_parse_reg_grp(iqs7211, reg_grp_node, reg_grp);
fwnode_handle_put(reg_grp_node);
if (error)
return error;
}
error = iqs7211_register_kp(iqs7211);
if (error)
return error;
error = iqs7211_register_tp(iqs7211);
if (error)
return error;
error = iqs7211_init_device(iqs7211);
if (error)
return error;
irq = gpiod_to_irq(iqs7211->irq_gpio);
if (irq < 0)
return irq;
irq_flags = gpiod_is_active_low(iqs7211->irq_gpio) ? IRQF_TRIGGER_LOW
: IRQF_TRIGGER_HIGH;
irq_flags |= IRQF_ONESHOT;
error = devm_request_threaded_irq(&client->dev, irq, NULL, iqs7211_irq,
irq_flags, client->name, iqs7211);
if (error)
dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
return error;
}
static struct i2c_driver iqs7211_i2c_driver = {
.probe = iqs7211_probe,
.driver = {
.name = "iqs7211",
.of_match_table = iqs7211_of_match,
.dev_groups = iqs7211_groups,
.pm = pm_sleep_ptr(&iqs7211_pm),
},
};
module_i2c_driver(iqs7211_i2c_driver);
MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
MODULE_DESCRIPTION("Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller");
MODULE_LICENSE("GPL"