| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller |
| * |
| * Copyright (C) 2023 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/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 <linux/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 |
| |
| /* |
| * The following delay is used during instances that must wait for the open- |
| * drain RDY pin to settle. Its value is calculated as 5*R*C, where R and C |
| * represent typical datasheet values of 4.7k and 100 nF, respectively. |
| */ |
| #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); |
| |
| /* |
| * The following delay ensures the shared RDY/MCLR pin is sampled in |
| * between periodic assertions by the device and assumes the default |
| * communication timeout has not been overwritten in OTP memory. |
| */ |
| 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; |
| } |
| |
| /* |
| * 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(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, |
| }, |
| }; |
| |
| /* |
| * 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 < 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); |
| |
| /* |
| * 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 < 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; |
| |
| /* |
| * Until forced communication can be enabled, the host must wait for a |
| * communication window each time it intends to elicit a response from |
| * the device. |
| * |
| * Forced communication is not necessary, however, if the host adapter |
| * can support clock stretching. In that case, the device freely clock |
| * stretches until all pending conversions are complete. |
| */ |
| 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; |
| |
| /* |
| * Acknowledge reset before writing any registers in case the device |
| * suffers a spurious reset during initialization. The communication |
| * mode is configured at this time as well. |
| */ |
| 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; |
| |
| /* |
| * Take advantage of the stop-bit disable function, if available, to |
| * save the trouble of having to reopen a communication window after |
| * each read or write. |
| */ |
| 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) { |
| /* |
| * Assign each sensing cycle's slots (0 and 1) to a channel, |
| * defined as the intersection between two CRx and CTx pins. |
| * A channel assignment of 255 means the slot is unused. |
| */ |
| for (i = 0, cycle_start = 0; i < total_tx; i++) { |
| int cycle_stop = 0; |
| |
| for (j = 0; j < total_rx; j++) { |
| /* |
| * Channels formed by CRx0-3 and CRx4-7 are |
| * bound to slots 0 and 1, respectively. |
| */ |
| 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; |
| } |
| |
| /* |
| * Sensing cycles cannot straddle more than one CTx |
| * pin. As such, the next row's starting cycle must |
| * be greater than the previous row's highest cycle. |
| */ |
| 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; |
| } |
| } |
| } |
| |
| /* |
| * Once the raw channel assignments have been derived, they must be |
| * packed according to the device's register map. |
| */ |
| 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); |
| |
| /* |
| * The device reserves 0xFFFF for coordinates that correspond to slots |
| * which are not in a state of touch. |
| */ |
| 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"); |
| |
| /* |
| * The device may or may not expect forced communication after |
| * it exits hardware reset, so the corresponding state machine |
| * must be reset as well. |
| */ |
| 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; |
| |
| /* |
| * A charging mode higher than 2 (idle mode) indicates the device last |
| * operated in low-power mode and intends to express an ALP event. |
| */ |
| 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; |
| |
| /* |
| * Hold and palm gestures persist while the contact remains in |
| * place; all others are momentary and hence are followed by a |
| * complementary release event. |
| */ |
| 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; |
| |
| /* |
| * I2C communication prompts the device to assert its RDY pin if it is |
| * not already asserted. As such, the interrupt must be disabled so as |
| * to prevent reentrant interrupts. |
| */ |
| 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)); |
| |
| /* |
| * Forced communication, if in use, must be explicitly enabled as part |
| * of the wake-up command. |
| */ |
| 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 sysfs_emit(buf, "%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; |
| |
| /* |
| * 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. |
| * |
| * If an extra CTx pin is present, the RDY and MCLR pins are combined |
| * into a single bidirectional pin. In that case, the platform's GPIO |
| * must be configured as an open-drain output. |
| */ |
| 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"); |