| // 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"); |