1d7e34d12SBenson Leung /* 2d7e34d12SBenson Leung * Cypress APA trackpad with I2C interface 3d7e34d12SBenson Leung * 4d7e34d12SBenson Leung * Author: Dudley Du <dudl@cypress.com> 5d7e34d12SBenson Leung * Further cleanup and restructuring by: 6d7e34d12SBenson Leung * Daniel Kurtz <djkurtz@chromium.org> 7d7e34d12SBenson Leung * Benson Leung <bleung@chromium.org> 8d7e34d12SBenson Leung * 9d7e34d12SBenson Leung * Copyright (C) 2011-2012 Cypress Semiconductor, Inc. 10d7e34d12SBenson Leung * Copyright (C) 2011-2012 Google, Inc. 11d7e34d12SBenson Leung * 12d7e34d12SBenson Leung * This file is subject to the terms and conditions of the GNU General Public 13d7e34d12SBenson Leung * License. See the file COPYING in the main directory of this archive for 14d7e34d12SBenson Leung * more details. 15d7e34d12SBenson Leung */ 16d7e34d12SBenson Leung 17d7e34d12SBenson Leung #include <linux/delay.h> 18d7e34d12SBenson Leung #include <linux/i2c.h> 19d7e34d12SBenson Leung #include <linux/input.h> 20d7e34d12SBenson Leung #include <linux/input/mt.h> 21d7e34d12SBenson Leung #include <linux/interrupt.h> 22d7e34d12SBenson Leung #include <linux/module.h> 23d7e34d12SBenson Leung #include <linux/slab.h> 24d7e34d12SBenson Leung 25d7e34d12SBenson Leung /* APA trackpad firmware generation */ 26d7e34d12SBenson Leung #define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */ 27d7e34d12SBenson Leung 28d7e34d12SBenson Leung #define CYAPA_NAME "Cypress APA Trackpad (cyapa)" 29d7e34d12SBenson Leung 30d7e34d12SBenson Leung /* commands for read/write registers of Cypress trackpad */ 31d7e34d12SBenson Leung #define CYAPA_CMD_SOFT_RESET 0x00 32d7e34d12SBenson Leung #define CYAPA_CMD_POWER_MODE 0x01 33d7e34d12SBenson Leung #define CYAPA_CMD_DEV_STATUS 0x02 34d7e34d12SBenson Leung #define CYAPA_CMD_GROUP_DATA 0x03 35d7e34d12SBenson Leung #define CYAPA_CMD_GROUP_CMD 0x04 36d7e34d12SBenson Leung #define CYAPA_CMD_GROUP_QUERY 0x05 37d7e34d12SBenson Leung #define CYAPA_CMD_BL_STATUS 0x06 38d7e34d12SBenson Leung #define CYAPA_CMD_BL_HEAD 0x07 39d7e34d12SBenson Leung #define CYAPA_CMD_BL_CMD 0x08 40d7e34d12SBenson Leung #define CYAPA_CMD_BL_DATA 0x09 41d7e34d12SBenson Leung #define CYAPA_CMD_BL_ALL 0x0a 42d7e34d12SBenson Leung #define CYAPA_CMD_BLK_PRODUCT_ID 0x0b 43d7e34d12SBenson Leung #define CYAPA_CMD_BLK_HEAD 0x0c 44d7e34d12SBenson Leung 45d7e34d12SBenson Leung /* report data start reg offset address. */ 46d7e34d12SBenson Leung #define DATA_REG_START_OFFSET 0x0000 47d7e34d12SBenson Leung 48d7e34d12SBenson Leung #define BL_HEAD_OFFSET 0x00 49d7e34d12SBenson Leung #define BL_DATA_OFFSET 0x10 50d7e34d12SBenson Leung 51d7e34d12SBenson Leung /* 52d7e34d12SBenson Leung * Operational Device Status Register 53d7e34d12SBenson Leung * 54d7e34d12SBenson Leung * bit 7: Valid interrupt source 55d7e34d12SBenson Leung * bit 6 - 4: Reserved 56d7e34d12SBenson Leung * bit 3 - 2: Power status 57d7e34d12SBenson Leung * bit 1 - 0: Device status 58d7e34d12SBenson Leung */ 59d7e34d12SBenson Leung #define REG_OP_STATUS 0x00 60d7e34d12SBenson Leung #define OP_STATUS_SRC 0x80 61d7e34d12SBenson Leung #define OP_STATUS_POWER 0x0c 62d7e34d12SBenson Leung #define OP_STATUS_DEV 0x03 63d7e34d12SBenson Leung #define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV) 64d7e34d12SBenson Leung 65d7e34d12SBenson Leung /* 66d7e34d12SBenson Leung * Operational Finger Count/Button Flags Register 67d7e34d12SBenson Leung * 68d7e34d12SBenson Leung * bit 7 - 4: Number of touched finger 69d7e34d12SBenson Leung * bit 3: Valid data 70d7e34d12SBenson Leung * bit 2: Middle Physical Button 71d7e34d12SBenson Leung * bit 1: Right Physical Button 72d7e34d12SBenson Leung * bit 0: Left physical Button 73d7e34d12SBenson Leung */ 74d7e34d12SBenson Leung #define REG_OP_DATA1 0x01 75d7e34d12SBenson Leung #define OP_DATA_VALID 0x08 76d7e34d12SBenson Leung #define OP_DATA_MIDDLE_BTN 0x04 77d7e34d12SBenson Leung #define OP_DATA_RIGHT_BTN 0x02 78d7e34d12SBenson Leung #define OP_DATA_LEFT_BTN 0x01 79d7e34d12SBenson Leung #define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \ 80d7e34d12SBenson Leung OP_DATA_LEFT_BTN) 81d7e34d12SBenson Leung 82d7e34d12SBenson Leung /* 83d7e34d12SBenson Leung * Bootloader Status Register 84d7e34d12SBenson Leung * 85d7e34d12SBenson Leung * bit 7: Busy 86d7e34d12SBenson Leung * bit 6 - 5: Reserved 87d7e34d12SBenson Leung * bit 4: Bootloader running 88d7e34d12SBenson Leung * bit 3 - 1: Reserved 89d7e34d12SBenson Leung * bit 0: Checksum valid 90d7e34d12SBenson Leung */ 91d7e34d12SBenson Leung #define REG_BL_STATUS 0x01 92d7e34d12SBenson Leung #define BL_STATUS_BUSY 0x80 93d7e34d12SBenson Leung #define BL_STATUS_RUNNING 0x10 94d7e34d12SBenson Leung #define BL_STATUS_DATA_VALID 0x08 95d7e34d12SBenson Leung #define BL_STATUS_CSUM_VALID 0x01 96d7e34d12SBenson Leung 97d7e34d12SBenson Leung /* 98d7e34d12SBenson Leung * Bootloader Error Register 99d7e34d12SBenson Leung * 100d7e34d12SBenson Leung * bit 7: Invalid 101d7e34d12SBenson Leung * bit 6: Invalid security key 102d7e34d12SBenson Leung * bit 5: Bootloading 103d7e34d12SBenson Leung * bit 4: Command checksum 104d7e34d12SBenson Leung * bit 3: Flash protection error 105d7e34d12SBenson Leung * bit 2: Flash checksum error 106d7e34d12SBenson Leung * bit 1 - 0: Reserved 107d7e34d12SBenson Leung */ 108d7e34d12SBenson Leung #define REG_BL_ERROR 0x02 109d7e34d12SBenson Leung #define BL_ERROR_INVALID 0x80 110d7e34d12SBenson Leung #define BL_ERROR_INVALID_KEY 0x40 111d7e34d12SBenson Leung #define BL_ERROR_BOOTLOADING 0x20 112d7e34d12SBenson Leung #define BL_ERROR_CMD_CSUM 0x10 113d7e34d12SBenson Leung #define BL_ERROR_FLASH_PROT 0x08 114d7e34d12SBenson Leung #define BL_ERROR_FLASH_CSUM 0x04 115d7e34d12SBenson Leung 116d7e34d12SBenson Leung #define BL_STATUS_SIZE 3 /* length of bootloader status registers */ 117d7e34d12SBenson Leung #define BLK_HEAD_BYTES 32 118d7e34d12SBenson Leung 119d7e34d12SBenson Leung #define PRODUCT_ID_SIZE 16 120d7e34d12SBenson Leung #define QUERY_DATA_SIZE 27 121d7e34d12SBenson Leung #define REG_PROTOCOL_GEN_QUERY_OFFSET 20 122d7e34d12SBenson Leung 123d7e34d12SBenson Leung #define REG_OFFSET_DATA_BASE 0x0000 124d7e34d12SBenson Leung #define REG_OFFSET_COMMAND_BASE 0x0028 125d7e34d12SBenson Leung #define REG_OFFSET_QUERY_BASE 0x002a 126d7e34d12SBenson Leung 127d7e34d12SBenson Leung #define CAPABILITY_LEFT_BTN_MASK (0x01 << 3) 128d7e34d12SBenson Leung #define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4) 129d7e34d12SBenson Leung #define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5) 130d7e34d12SBenson Leung #define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \ 131d7e34d12SBenson Leung CAPABILITY_RIGHT_BTN_MASK | \ 132d7e34d12SBenson Leung CAPABILITY_MIDDLE_BTN_MASK) 133d7e34d12SBenson Leung 134d7e34d12SBenson Leung #define CYAPA_OFFSET_SOFT_RESET REG_OFFSET_COMMAND_BASE 135d7e34d12SBenson Leung 136d7e34d12SBenson Leung #define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1) 137d7e34d12SBenson Leung 138d7e34d12SBenson Leung #define PWR_MODE_MASK 0xfc 139d7e34d12SBenson Leung #define PWR_MODE_FULL_ACTIVE (0x3f << 2) 140d7e34d12SBenson Leung #define PWR_MODE_IDLE (0x05 << 2) /* default sleep time is 50 ms. */ 141d7e34d12SBenson Leung #define PWR_MODE_OFF (0x00 << 2) 142d7e34d12SBenson Leung 143d7e34d12SBenson Leung #define PWR_STATUS_MASK 0x0c 144d7e34d12SBenson Leung #define PWR_STATUS_ACTIVE (0x03 << 2) 145d7e34d12SBenson Leung #define PWR_STATUS_IDLE (0x02 << 2) 146d7e34d12SBenson Leung #define PWR_STATUS_OFF (0x00 << 2) 147d7e34d12SBenson Leung 148d7e34d12SBenson Leung /* 149d7e34d12SBenson Leung * CYAPA trackpad device states. 150d7e34d12SBenson Leung * Used in register 0x00, bit1-0, DeviceStatus field. 151d7e34d12SBenson Leung * Other values indicate device is in an abnormal state and must be reset. 152d7e34d12SBenson Leung */ 153d7e34d12SBenson Leung #define CYAPA_DEV_NORMAL 0x03 154d7e34d12SBenson Leung #define CYAPA_DEV_BUSY 0x01 155d7e34d12SBenson Leung 156d7e34d12SBenson Leung enum cyapa_state { 157d7e34d12SBenson Leung CYAPA_STATE_OP, 158d7e34d12SBenson Leung CYAPA_STATE_BL_IDLE, 159d7e34d12SBenson Leung CYAPA_STATE_BL_ACTIVE, 160d7e34d12SBenson Leung CYAPA_STATE_BL_BUSY, 161d7e34d12SBenson Leung CYAPA_STATE_NO_DEVICE, 162d7e34d12SBenson Leung }; 163d7e34d12SBenson Leung 164d7e34d12SBenson Leung 165d7e34d12SBenson Leung struct cyapa_touch { 166d7e34d12SBenson Leung /* 167d7e34d12SBenson Leung * high bits or x/y position value 168d7e34d12SBenson Leung * bit 7 - 4: high 4 bits of x position value 169d7e34d12SBenson Leung * bit 3 - 0: high 4 bits of y position value 170d7e34d12SBenson Leung */ 171d7e34d12SBenson Leung u8 xy_hi; 172d7e34d12SBenson Leung u8 x_lo; /* low 8 bits of x position value. */ 173d7e34d12SBenson Leung u8 y_lo; /* low 8 bits of y position value. */ 174d7e34d12SBenson Leung u8 pressure; 175d7e34d12SBenson Leung /* id range is 1 - 15. It is incremented with every new touch. */ 176d7e34d12SBenson Leung u8 id; 177d7e34d12SBenson Leung } __packed; 178d7e34d12SBenson Leung 179d7e34d12SBenson Leung /* The touch.id is used as the MT slot id, thus max MT slot is 15 */ 180d7e34d12SBenson Leung #define CYAPA_MAX_MT_SLOTS 15 181d7e34d12SBenson Leung 182d7e34d12SBenson Leung struct cyapa_reg_data { 183d7e34d12SBenson Leung /* 184d7e34d12SBenson Leung * bit 0 - 1: device status 185d7e34d12SBenson Leung * bit 3 - 2: power mode 186d7e34d12SBenson Leung * bit 6 - 4: reserved 187d7e34d12SBenson Leung * bit 7: interrupt valid bit 188d7e34d12SBenson Leung */ 189d7e34d12SBenson Leung u8 device_status; 190d7e34d12SBenson Leung /* 191d7e34d12SBenson Leung * bit 7 - 4: number of fingers currently touching pad 192d7e34d12SBenson Leung * bit 3: valid data check bit 193d7e34d12SBenson Leung * bit 2: middle mechanism button state if exists 194d7e34d12SBenson Leung * bit 1: right mechanism button state if exists 195d7e34d12SBenson Leung * bit 0: left mechanism button state if exists 196d7e34d12SBenson Leung */ 197d7e34d12SBenson Leung u8 finger_btn; 198d7e34d12SBenson Leung /* CYAPA reports up to 5 touches per packet. */ 199d7e34d12SBenson Leung struct cyapa_touch touches[5]; 200d7e34d12SBenson Leung } __packed; 201d7e34d12SBenson Leung 202d7e34d12SBenson Leung /* The main device structure */ 203d7e34d12SBenson Leung struct cyapa { 204d7e34d12SBenson Leung enum cyapa_state state; 205d7e34d12SBenson Leung 206d7e34d12SBenson Leung struct i2c_client *client; 207d7e34d12SBenson Leung struct input_dev *input; 208d7e34d12SBenson Leung char phys[32]; /* device physical location */ 209d7e34d12SBenson Leung bool irq_wake; /* irq wake is enabled */ 2106ddaf744SBenson Leung bool smbus; 211d7e34d12SBenson Leung 212d7e34d12SBenson Leung /* read from query data region. */ 213d7e34d12SBenson Leung char product_id[16]; 214d7e34d12SBenson Leung u8 btn_capability; 215d7e34d12SBenson Leung u8 gen; 216d7e34d12SBenson Leung int max_abs_x; 217d7e34d12SBenson Leung int max_abs_y; 218d7e34d12SBenson Leung int physical_size_x; 219d7e34d12SBenson Leung int physical_size_y; 220d7e34d12SBenson Leung }; 221d7e34d12SBenson Leung 222d7e34d12SBenson Leung static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03, 223d7e34d12SBenson Leung 0x04, 0x05, 0x06, 0x07 }; 224d7e34d12SBenson Leung static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04, 225d7e34d12SBenson Leung 0x05, 0x06, 0x07 }; 226d7e34d12SBenson Leung 227d7e34d12SBenson Leung struct cyapa_cmd_len { 228d7e34d12SBenson Leung u8 cmd; 229d7e34d12SBenson Leung u8 len; 230d7e34d12SBenson Leung }; 231d7e34d12SBenson Leung 2326ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_NONE 0 2336ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_I2C 1 2346ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_SMBUS 2 2356ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_BOTH 3 2366ddaf744SBenson Leung 2376ddaf744SBenson Leung /* 2386ddaf744SBenson Leung * macros for SMBus communication 2396ddaf744SBenson Leung */ 2406ddaf744SBenson Leung #define SMBUS_READ 0x01 2416ddaf744SBenson Leung #define SMBUS_WRITE 0x00 2426ddaf744SBenson Leung #define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1)) 2436ddaf744SBenson Leung #define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01)) 2446ddaf744SBenson Leung #define SMBUS_BYTE_BLOCK_CMD_MASK 0x80 2456ddaf744SBenson Leung #define SMBUS_GROUP_BLOCK_CMD_MASK 0x40 2466ddaf744SBenson Leung 2476ddaf744SBenson Leung /* for byte read/write command */ 2486ddaf744SBenson Leung #define CMD_RESET 0 2496ddaf744SBenson Leung #define CMD_POWER_MODE 1 2506ddaf744SBenson Leung #define CMD_DEV_STATUS 2 2516ddaf744SBenson Leung #define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1) 2526ddaf744SBenson Leung #define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET) 2536ddaf744SBenson Leung #define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE) 2546ddaf744SBenson Leung #define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS) 2556ddaf744SBenson Leung 2566ddaf744SBenson Leung /* for group registers read/write command */ 2576ddaf744SBenson Leung #define REG_GROUP_DATA 0 2586ddaf744SBenson Leung #define REG_GROUP_CMD 2 2596ddaf744SBenson Leung #define REG_GROUP_QUERY 3 2606ddaf744SBenson Leung #define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3)) 2616ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA) 2626ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD) 2636ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY) 2646ddaf744SBenson Leung 2656ddaf744SBenson Leung /* for register block read/write command */ 2666ddaf744SBenson Leung #define CMD_BL_STATUS 0 2676ddaf744SBenson Leung #define CMD_BL_HEAD 1 2686ddaf744SBenson Leung #define CMD_BL_CMD 2 2696ddaf744SBenson Leung #define CMD_BL_DATA 3 2706ddaf744SBenson Leung #define CMD_BL_ALL 4 2716ddaf744SBenson Leung #define CMD_BLK_PRODUCT_ID 5 2726ddaf744SBenson Leung #define CMD_BLK_HEAD 6 2736ddaf744SBenson Leung #define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1)) 2746ddaf744SBenson Leung 2756ddaf744SBenson Leung /* register block read/write command in bootloader mode */ 2766ddaf744SBenson Leung #define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS) 2776ddaf744SBenson Leung #define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD) 2786ddaf744SBenson Leung #define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD) 2796ddaf744SBenson Leung #define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA) 2806ddaf744SBenson Leung #define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL) 2816ddaf744SBenson Leung 2826ddaf744SBenson Leung /* register block read/write command in operational mode */ 2836ddaf744SBenson Leung #define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID) 2846ddaf744SBenson Leung #define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD) 2856ddaf744SBenson Leung 286d7e34d12SBenson Leung static const struct cyapa_cmd_len cyapa_i2c_cmds[] = { 287d7e34d12SBenson Leung { CYAPA_OFFSET_SOFT_RESET, 1 }, 288d7e34d12SBenson Leung { REG_OFFSET_COMMAND_BASE + 1, 1 }, 289d7e34d12SBenson Leung { REG_OFFSET_DATA_BASE, 1 }, 290d7e34d12SBenson Leung { REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) }, 291d7e34d12SBenson Leung { REG_OFFSET_COMMAND_BASE, 0 }, 292d7e34d12SBenson Leung { REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE }, 293d7e34d12SBenson Leung { BL_HEAD_OFFSET, 3 }, 294d7e34d12SBenson Leung { BL_HEAD_OFFSET, 16 }, 295d7e34d12SBenson Leung { BL_HEAD_OFFSET, 16 }, 296d7e34d12SBenson Leung { BL_DATA_OFFSET, 16 }, 297d7e34d12SBenson Leung { BL_HEAD_OFFSET, 32 }, 298d7e34d12SBenson Leung { REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE }, 299d7e34d12SBenson Leung { REG_OFFSET_DATA_BASE, 32 } 300d7e34d12SBenson Leung }; 301d7e34d12SBenson Leung 3026ddaf744SBenson Leung static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { 3036ddaf744SBenson Leung { CYAPA_SMBUS_RESET, 1 }, 3046ddaf744SBenson Leung { CYAPA_SMBUS_POWER_MODE, 1 }, 3056ddaf744SBenson Leung { CYAPA_SMBUS_DEV_STATUS, 1 }, 3066ddaf744SBenson Leung { CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) }, 3076ddaf744SBenson Leung { CYAPA_SMBUS_GROUP_CMD, 2 }, 3086ddaf744SBenson Leung { CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE }, 3096ddaf744SBenson Leung { CYAPA_SMBUS_BL_STATUS, 3 }, 3106ddaf744SBenson Leung { CYAPA_SMBUS_BL_HEAD, 16 }, 3116ddaf744SBenson Leung { CYAPA_SMBUS_BL_CMD, 16 }, 3126ddaf744SBenson Leung { CYAPA_SMBUS_BL_DATA, 16 }, 3136ddaf744SBenson Leung { CYAPA_SMBUS_BL_ALL, 32 }, 3146ddaf744SBenson Leung { CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE }, 3156ddaf744SBenson Leung { CYAPA_SMBUS_BLK_HEAD, 16 }, 3166ddaf744SBenson Leung }; 3176ddaf744SBenson Leung 318d7e34d12SBenson Leung static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, 319d7e34d12SBenson Leung u8 *values) 320d7e34d12SBenson Leung { 321d7e34d12SBenson Leung return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values); 322d7e34d12SBenson Leung } 323d7e34d12SBenson Leung 324d7e34d12SBenson Leung static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg, 325d7e34d12SBenson Leung size_t len, const u8 *values) 326d7e34d12SBenson Leung { 327d7e34d12SBenson Leung return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values); 328d7e34d12SBenson Leung } 329d7e34d12SBenson Leung 3306ddaf744SBenson Leung /* 3316ddaf744SBenson Leung * cyapa_smbus_read_block - perform smbus block read command 3326ddaf744SBenson Leung * @cyapa - private data structure of the driver 3336ddaf744SBenson Leung * @cmd - the properly encoded smbus command 3346ddaf744SBenson Leung * @len - expected length of smbus command result 3356ddaf744SBenson Leung * @values - buffer to store smbus command result 3366ddaf744SBenson Leung * 3376ddaf744SBenson Leung * Returns negative errno, else the number of bytes written. 3386ddaf744SBenson Leung * 3396ddaf744SBenson Leung * Note: 3406ddaf744SBenson Leung * In trackpad device, the memory block allocated for I2C register map 3416ddaf744SBenson Leung * is 256 bytes, so the max read block for I2C bus is 256 bytes. 3426ddaf744SBenson Leung */ 3436ddaf744SBenson Leung static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len, 3446ddaf744SBenson Leung u8 *values) 3456ddaf744SBenson Leung { 3466ddaf744SBenson Leung ssize_t ret; 3476ddaf744SBenson Leung u8 index; 3486ddaf744SBenson Leung u8 smbus_cmd; 3496ddaf744SBenson Leung u8 *buf; 3506ddaf744SBenson Leung struct i2c_client *client = cyapa->client; 3516ddaf744SBenson Leung 3526ddaf744SBenson Leung if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd)) 3536ddaf744SBenson Leung return -EINVAL; 3546ddaf744SBenson Leung 3556ddaf744SBenson Leung if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) { 3566ddaf744SBenson Leung /* read specific block registers command. */ 3576ddaf744SBenson Leung smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); 3586ddaf744SBenson Leung ret = i2c_smbus_read_block_data(client, smbus_cmd, values); 3596ddaf744SBenson Leung goto out; 3606ddaf744SBenson Leung } 3616ddaf744SBenson Leung 3626ddaf744SBenson Leung ret = 0; 3636ddaf744SBenson Leung for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) { 3646ddaf744SBenson Leung smbus_cmd = SMBUS_ENCODE_IDX(cmd, index); 3656ddaf744SBenson Leung smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ); 3666ddaf744SBenson Leung buf = values + I2C_SMBUS_BLOCK_MAX * index; 3676ddaf744SBenson Leung ret = i2c_smbus_read_block_data(client, smbus_cmd, buf); 3686ddaf744SBenson Leung if (ret < 0) 3696ddaf744SBenson Leung goto out; 3706ddaf744SBenson Leung } 3716ddaf744SBenson Leung 3726ddaf744SBenson Leung out: 3736ddaf744SBenson Leung return ret > 0 ? len : ret; 3746ddaf744SBenson Leung } 3756ddaf744SBenson Leung 376d7e34d12SBenson Leung static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx) 377d7e34d12SBenson Leung { 3786ddaf744SBenson Leung u8 cmd; 379d7e34d12SBenson Leung 3806ddaf744SBenson Leung if (cyapa->smbus) { 3816ddaf744SBenson Leung cmd = cyapa_smbus_cmds[cmd_idx].cmd; 3826ddaf744SBenson Leung cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); 3836ddaf744SBenson Leung } else { 3846ddaf744SBenson Leung cmd = cyapa_i2c_cmds[cmd_idx].cmd; 3856ddaf744SBenson Leung } 386d7e34d12SBenson Leung return i2c_smbus_read_byte_data(cyapa->client, cmd); 387d7e34d12SBenson Leung } 388d7e34d12SBenson Leung 389d7e34d12SBenson Leung static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value) 390d7e34d12SBenson Leung { 3916ddaf744SBenson Leung u8 cmd; 392d7e34d12SBenson Leung 3936ddaf744SBenson Leung if (cyapa->smbus) { 3946ddaf744SBenson Leung cmd = cyapa_smbus_cmds[cmd_idx].cmd; 3956ddaf744SBenson Leung cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE); 3966ddaf744SBenson Leung } else { 3976ddaf744SBenson Leung cmd = cyapa_i2c_cmds[cmd_idx].cmd; 3986ddaf744SBenson Leung } 399d7e34d12SBenson Leung return i2c_smbus_write_byte_data(cyapa->client, cmd, value); 400d7e34d12SBenson Leung } 401d7e34d12SBenson Leung 402d7e34d12SBenson Leung static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values) 403d7e34d12SBenson Leung { 4046ddaf744SBenson Leung u8 cmd; 4056ddaf744SBenson Leung size_t len; 406d7e34d12SBenson Leung 4076ddaf744SBenson Leung if (cyapa->smbus) { 4086ddaf744SBenson Leung cmd = cyapa_smbus_cmds[cmd_idx].cmd; 4096ddaf744SBenson Leung len = cyapa_smbus_cmds[cmd_idx].len; 4106ddaf744SBenson Leung return cyapa_smbus_read_block(cyapa, cmd, len, values); 4116ddaf744SBenson Leung } else { 4126ddaf744SBenson Leung cmd = cyapa_i2c_cmds[cmd_idx].cmd; 4136ddaf744SBenson Leung len = cyapa_i2c_cmds[cmd_idx].len; 414d7e34d12SBenson Leung return cyapa_i2c_reg_read_block(cyapa, cmd, len, values); 415d7e34d12SBenson Leung } 4166ddaf744SBenson Leung } 417d7e34d12SBenson Leung 418d7e34d12SBenson Leung /* 419d7e34d12SBenson Leung * Query device for its current operating state. 420d7e34d12SBenson Leung * 421d7e34d12SBenson Leung */ 422d7e34d12SBenson Leung static int cyapa_get_state(struct cyapa *cyapa) 423d7e34d12SBenson Leung { 424d7e34d12SBenson Leung int ret; 425d7e34d12SBenson Leung u8 status[BL_STATUS_SIZE]; 426d7e34d12SBenson Leung 427d7e34d12SBenson Leung cyapa->state = CYAPA_STATE_NO_DEVICE; 428d7e34d12SBenson Leung 429d7e34d12SBenson Leung /* 430d7e34d12SBenson Leung * Get trackpad status by reading 3 registers starting from 0. 431d7e34d12SBenson Leung * If the device is in the bootloader, this will be BL_HEAD. 432d7e34d12SBenson Leung * If the device is in operation mode, this will be the DATA regs. 433d7e34d12SBenson Leung * 434d7e34d12SBenson Leung */ 435d7e34d12SBenson Leung ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE, 436d7e34d12SBenson Leung status); 4376ddaf744SBenson Leung 4386ddaf744SBenson Leung /* 4396ddaf744SBenson Leung * On smbus systems in OP mode, the i2c_reg_read will fail with 4406ddaf744SBenson Leung * -ETIMEDOUT. In this case, try again using the smbus equivalent 4416ddaf744SBenson Leung * command. This should return a BL_HEAD indicating CYAPA_STATE_OP. 4426ddaf744SBenson Leung */ 4436ddaf744SBenson Leung if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO)) 4446ddaf744SBenson Leung ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status); 4456ddaf744SBenson Leung 446d7e34d12SBenson Leung if (ret != BL_STATUS_SIZE) 447d7e34d12SBenson Leung goto error; 448d7e34d12SBenson Leung 449d7e34d12SBenson Leung if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) { 450d7e34d12SBenson Leung switch (status[REG_OP_STATUS] & OP_STATUS_DEV) { 451d7e34d12SBenson Leung case CYAPA_DEV_NORMAL: 452d7e34d12SBenson Leung case CYAPA_DEV_BUSY: 453d7e34d12SBenson Leung cyapa->state = CYAPA_STATE_OP; 454d7e34d12SBenson Leung break; 455d7e34d12SBenson Leung default: 456d7e34d12SBenson Leung ret = -EAGAIN; 457d7e34d12SBenson Leung goto error; 458d7e34d12SBenson Leung } 459d7e34d12SBenson Leung } else { 460d7e34d12SBenson Leung if (status[REG_BL_STATUS] & BL_STATUS_BUSY) 461d7e34d12SBenson Leung cyapa->state = CYAPA_STATE_BL_BUSY; 462d7e34d12SBenson Leung else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING) 463d7e34d12SBenson Leung cyapa->state = CYAPA_STATE_BL_ACTIVE; 464d7e34d12SBenson Leung else 465d7e34d12SBenson Leung cyapa->state = CYAPA_STATE_BL_IDLE; 466d7e34d12SBenson Leung } 467d7e34d12SBenson Leung 468d7e34d12SBenson Leung return 0; 469d7e34d12SBenson Leung error: 470d7e34d12SBenson Leung return (ret < 0) ? ret : -EAGAIN; 471d7e34d12SBenson Leung } 472d7e34d12SBenson Leung 473d7e34d12SBenson Leung /* 474d7e34d12SBenson Leung * Poll device for its status in a loop, waiting up to timeout for a response. 475d7e34d12SBenson Leung * 476d7e34d12SBenson Leung * When the device switches state, it usually takes ~300 ms. 477d7e34d12SBenson Leung * However, when running a new firmware image, the device must calibrate its 478d7e34d12SBenson Leung * sensors, which can take as long as 2 seconds. 479d7e34d12SBenson Leung * 480d7e34d12SBenson Leung * Note: The timeout has granularity of the polling rate, which is 100 ms. 481d7e34d12SBenson Leung * 482d7e34d12SBenson Leung * Returns: 483d7e34d12SBenson Leung * 0 when the device eventually responds with a valid non-busy state. 484d7e34d12SBenson Leung * -ETIMEDOUT if device never responds (too many -EAGAIN) 485d7e34d12SBenson Leung * < 0 other errors 486d7e34d12SBenson Leung */ 487d7e34d12SBenson Leung static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout) 488d7e34d12SBenson Leung { 489d7e34d12SBenson Leung int ret; 490d7e34d12SBenson Leung int tries = timeout / 100; 491d7e34d12SBenson Leung 492d7e34d12SBenson Leung ret = cyapa_get_state(cyapa); 493d7e34d12SBenson Leung while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) { 494d7e34d12SBenson Leung msleep(100); 495d7e34d12SBenson Leung ret = cyapa_get_state(cyapa); 496d7e34d12SBenson Leung } 497d7e34d12SBenson Leung return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret; 498d7e34d12SBenson Leung } 499d7e34d12SBenson Leung 500d7e34d12SBenson Leung static int cyapa_bl_deactivate(struct cyapa *cyapa) 501d7e34d12SBenson Leung { 502d7e34d12SBenson Leung int ret; 503d7e34d12SBenson Leung 504d7e34d12SBenson Leung ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate), 505d7e34d12SBenson Leung bl_deactivate); 506d7e34d12SBenson Leung if (ret < 0) 507d7e34d12SBenson Leung return ret; 508d7e34d12SBenson Leung 509d7e34d12SBenson Leung /* wait for bootloader to switch to idle state; should take < 100ms */ 510d7e34d12SBenson Leung msleep(100); 511d7e34d12SBenson Leung ret = cyapa_poll_state(cyapa, 500); 512d7e34d12SBenson Leung if (ret < 0) 513d7e34d12SBenson Leung return ret; 514d7e34d12SBenson Leung if (cyapa->state != CYAPA_STATE_BL_IDLE) 515d7e34d12SBenson Leung return -EAGAIN; 516d7e34d12SBenson Leung return 0; 517d7e34d12SBenson Leung } 518d7e34d12SBenson Leung 519d7e34d12SBenson Leung /* 520d7e34d12SBenson Leung * Exit bootloader 521d7e34d12SBenson Leung * 522d7e34d12SBenson Leung * Send bl_exit command, then wait 50 - 100 ms to let device transition to 523d7e34d12SBenson Leung * operational mode. If this is the first time the device's firmware is 524d7e34d12SBenson Leung * running, it can take up to 2 seconds to calibrate its sensors. So, poll 525d7e34d12SBenson Leung * the device's new state for up to 2 seconds. 526d7e34d12SBenson Leung * 527d7e34d12SBenson Leung * Returns: 528d7e34d12SBenson Leung * -EIO failure while reading from device 529d7e34d12SBenson Leung * -EAGAIN device is stuck in bootloader, b/c it has invalid firmware 530d7e34d12SBenson Leung * 0 device is supported and in operational mode 531d7e34d12SBenson Leung */ 532d7e34d12SBenson Leung static int cyapa_bl_exit(struct cyapa *cyapa) 533d7e34d12SBenson Leung { 534d7e34d12SBenson Leung int ret; 535d7e34d12SBenson Leung 536d7e34d12SBenson Leung ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit); 537d7e34d12SBenson Leung if (ret < 0) 538d7e34d12SBenson Leung return ret; 539d7e34d12SBenson Leung 540d7e34d12SBenson Leung /* 541d7e34d12SBenson Leung * Wait for bootloader to exit, and operation mode to start. 542d7e34d12SBenson Leung * Normally, this takes at least 50 ms. 543d7e34d12SBenson Leung */ 544d7e34d12SBenson Leung usleep_range(50000, 100000); 545d7e34d12SBenson Leung /* 546d7e34d12SBenson Leung * In addition, when a device boots for the first time after being 547d7e34d12SBenson Leung * updated to new firmware, it must first calibrate its sensors, which 548d7e34d12SBenson Leung * can take up to an additional 2 seconds. 549d7e34d12SBenson Leung */ 550d7e34d12SBenson Leung ret = cyapa_poll_state(cyapa, 2000); 551d7e34d12SBenson Leung if (ret < 0) 552d7e34d12SBenson Leung return ret; 553d7e34d12SBenson Leung if (cyapa->state != CYAPA_STATE_OP) 554d7e34d12SBenson Leung return -EAGAIN; 555d7e34d12SBenson Leung 556d7e34d12SBenson Leung return 0; 557d7e34d12SBenson Leung } 558d7e34d12SBenson Leung 559d7e34d12SBenson Leung /* 560d7e34d12SBenson Leung * Set device power mode 561d7e34d12SBenson Leung * 562d7e34d12SBenson Leung */ 563d7e34d12SBenson Leung static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode) 564d7e34d12SBenson Leung { 565d7e34d12SBenson Leung struct device *dev = &cyapa->client->dev; 566d7e34d12SBenson Leung int ret; 567d7e34d12SBenson Leung u8 power; 568d7e34d12SBenson Leung 569d7e34d12SBenson Leung if (cyapa->state != CYAPA_STATE_OP) 570d7e34d12SBenson Leung return 0; 571d7e34d12SBenson Leung 572d7e34d12SBenson Leung ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE); 573d7e34d12SBenson Leung if (ret < 0) 574d7e34d12SBenson Leung return ret; 575d7e34d12SBenson Leung 576d7e34d12SBenson Leung power = ret & ~PWR_MODE_MASK; 577d7e34d12SBenson Leung power |= power_mode & PWR_MODE_MASK; 578d7e34d12SBenson Leung ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power); 579b1cfa7b4SDudley Du if (ret < 0) { 580d7e34d12SBenson Leung dev_err(dev, "failed to set power_mode 0x%02x err = %d\n", 581d7e34d12SBenson Leung power_mode, ret); 582d7e34d12SBenson Leung return ret; 583d7e34d12SBenson Leung } 584d7e34d12SBenson Leung 585b1cfa7b4SDudley Du return 0; 586b1cfa7b4SDudley Du } 587b1cfa7b4SDudley Du 588d7e34d12SBenson Leung static int cyapa_get_query_data(struct cyapa *cyapa) 589d7e34d12SBenson Leung { 590d7e34d12SBenson Leung u8 query_data[QUERY_DATA_SIZE]; 591d7e34d12SBenson Leung int ret; 592d7e34d12SBenson Leung 593d7e34d12SBenson Leung if (cyapa->state != CYAPA_STATE_OP) 594d7e34d12SBenson Leung return -EBUSY; 595d7e34d12SBenson Leung 596d7e34d12SBenson Leung ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data); 597d7e34d12SBenson Leung if (ret < 0) 598d7e34d12SBenson Leung return ret; 599d7e34d12SBenson Leung if (ret != QUERY_DATA_SIZE) 600d7e34d12SBenson Leung return -EIO; 601d7e34d12SBenson Leung 602d7e34d12SBenson Leung memcpy(&cyapa->product_id[0], &query_data[0], 5); 603d7e34d12SBenson Leung cyapa->product_id[5] = '-'; 604d7e34d12SBenson Leung memcpy(&cyapa->product_id[6], &query_data[5], 6); 605d7e34d12SBenson Leung cyapa->product_id[12] = '-'; 606d7e34d12SBenson Leung memcpy(&cyapa->product_id[13], &query_data[11], 2); 607d7e34d12SBenson Leung cyapa->product_id[15] = '\0'; 608d7e34d12SBenson Leung 609d7e34d12SBenson Leung cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK; 610d7e34d12SBenson Leung 611d7e34d12SBenson Leung cyapa->gen = query_data[20] & 0x0f; 612d7e34d12SBenson Leung 613d7e34d12SBenson Leung cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22]; 614d7e34d12SBenson Leung cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23]; 615d7e34d12SBenson Leung 616d7e34d12SBenson Leung cyapa->physical_size_x = 617d7e34d12SBenson Leung ((query_data[24] & 0xf0) << 4) | query_data[25]; 618d7e34d12SBenson Leung cyapa->physical_size_y = 619d7e34d12SBenson Leung ((query_data[24] & 0x0f) << 8) | query_data[26]; 620d7e34d12SBenson Leung 621d7e34d12SBenson Leung return 0; 622d7e34d12SBenson Leung } 623d7e34d12SBenson Leung 624d7e34d12SBenson Leung /* 625d7e34d12SBenson Leung * Check if device is operational. 626d7e34d12SBenson Leung * 627d7e34d12SBenson Leung * An operational device is responding, has exited bootloader, and has 628d7e34d12SBenson Leung * firmware supported by this driver. 629d7e34d12SBenson Leung * 630d7e34d12SBenson Leung * Returns: 631d7e34d12SBenson Leung * -EBUSY no device or in bootloader 632d7e34d12SBenson Leung * -EIO failure while reading from device 633d7e34d12SBenson Leung * -EAGAIN device is still in bootloader 634d7e34d12SBenson Leung * if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware 635d7e34d12SBenson Leung * -EINVAL device is in operational mode, but not supported by this driver 636d7e34d12SBenson Leung * 0 device is supported 637d7e34d12SBenson Leung */ 638d7e34d12SBenson Leung static int cyapa_check_is_operational(struct cyapa *cyapa) 639d7e34d12SBenson Leung { 640d7e34d12SBenson Leung struct device *dev = &cyapa->client->dev; 641d7e34d12SBenson Leung static const char unique_str[] = "CYTRA"; 642d7e34d12SBenson Leung int ret; 643d7e34d12SBenson Leung 644d7e34d12SBenson Leung ret = cyapa_poll_state(cyapa, 2000); 645d7e34d12SBenson Leung if (ret < 0) 646d7e34d12SBenson Leung return ret; 647d7e34d12SBenson Leung switch (cyapa->state) { 648d7e34d12SBenson Leung case CYAPA_STATE_BL_ACTIVE: 649d7e34d12SBenson Leung ret = cyapa_bl_deactivate(cyapa); 650d7e34d12SBenson Leung if (ret) 651d7e34d12SBenson Leung return ret; 652d7e34d12SBenson Leung 653d7e34d12SBenson Leung /* Fallthrough state */ 654d7e34d12SBenson Leung case CYAPA_STATE_BL_IDLE: 655d7e34d12SBenson Leung ret = cyapa_bl_exit(cyapa); 656d7e34d12SBenson Leung if (ret) 657d7e34d12SBenson Leung return ret; 658d7e34d12SBenson Leung 659d7e34d12SBenson Leung /* Fallthrough state */ 660d7e34d12SBenson Leung case CYAPA_STATE_OP: 661d7e34d12SBenson Leung ret = cyapa_get_query_data(cyapa); 662d7e34d12SBenson Leung if (ret < 0) 663d7e34d12SBenson Leung return ret; 664d7e34d12SBenson Leung 665d7e34d12SBenson Leung /* only support firmware protocol gen3 */ 666d7e34d12SBenson Leung if (cyapa->gen != CYAPA_GEN3) { 667d7e34d12SBenson Leung dev_err(dev, "unsupported protocol version (%d)", 668d7e34d12SBenson Leung cyapa->gen); 669d7e34d12SBenson Leung return -EINVAL; 670d7e34d12SBenson Leung } 671d7e34d12SBenson Leung 672d7e34d12SBenson Leung /* only support product ID starting with CYTRA */ 673d7e34d12SBenson Leung if (memcmp(cyapa->product_id, unique_str, 674d7e34d12SBenson Leung sizeof(unique_str) - 1) != 0) { 675d7e34d12SBenson Leung dev_err(dev, "unsupported product ID (%s)\n", 676d7e34d12SBenson Leung cyapa->product_id); 677d7e34d12SBenson Leung return -EINVAL; 678d7e34d12SBenson Leung } 679d7e34d12SBenson Leung return 0; 680d7e34d12SBenson Leung 681d7e34d12SBenson Leung default: 682d7e34d12SBenson Leung return -EIO; 683d7e34d12SBenson Leung } 684d7e34d12SBenson Leung return 0; 685d7e34d12SBenson Leung } 686d7e34d12SBenson Leung 687d7e34d12SBenson Leung static irqreturn_t cyapa_irq(int irq, void *dev_id) 688d7e34d12SBenson Leung { 689d7e34d12SBenson Leung struct cyapa *cyapa = dev_id; 690d7e34d12SBenson Leung struct device *dev = &cyapa->client->dev; 691d7e34d12SBenson Leung struct input_dev *input = cyapa->input; 692d7e34d12SBenson Leung struct cyapa_reg_data data; 693d7e34d12SBenson Leung int i; 694d7e34d12SBenson Leung int ret; 695d7e34d12SBenson Leung int num_fingers; 696d7e34d12SBenson Leung 697d7e34d12SBenson Leung if (device_may_wakeup(dev)) 698d7e34d12SBenson Leung pm_wakeup_event(dev, 0); 699d7e34d12SBenson Leung 700d7e34d12SBenson Leung ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); 701d7e34d12SBenson Leung if (ret != sizeof(data)) 702d7e34d12SBenson Leung goto out; 703d7e34d12SBenson Leung 704d7e34d12SBenson Leung if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || 705d7e34d12SBenson Leung (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || 706d7e34d12SBenson Leung (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { 707d7e34d12SBenson Leung goto out; 708d7e34d12SBenson Leung } 709d7e34d12SBenson Leung 710d7e34d12SBenson Leung num_fingers = (data.finger_btn >> 4) & 0x0f; 711d7e34d12SBenson Leung for (i = 0; i < num_fingers; i++) { 712d7e34d12SBenson Leung const struct cyapa_touch *touch = &data.touches[i]; 713d7e34d12SBenson Leung /* Note: touch->id range is 1 to 15; slots are 0 to 14. */ 714d7e34d12SBenson Leung int slot = touch->id - 1; 715d7e34d12SBenson Leung 716d7e34d12SBenson Leung input_mt_slot(input, slot); 717d7e34d12SBenson Leung input_mt_report_slot_state(input, MT_TOOL_FINGER, true); 718d7e34d12SBenson Leung input_report_abs(input, ABS_MT_POSITION_X, 719d7e34d12SBenson Leung ((touch->xy_hi & 0xf0) << 4) | touch->x_lo); 720d7e34d12SBenson Leung input_report_abs(input, ABS_MT_POSITION_Y, 721d7e34d12SBenson Leung ((touch->xy_hi & 0x0f) << 8) | touch->y_lo); 722d7e34d12SBenson Leung input_report_abs(input, ABS_MT_PRESSURE, touch->pressure); 723d7e34d12SBenson Leung } 724d7e34d12SBenson Leung 725d7e34d12SBenson Leung input_mt_sync_frame(input); 726d7e34d12SBenson Leung 727d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) 728d7e34d12SBenson Leung input_report_key(input, BTN_LEFT, 729d7e34d12SBenson Leung data.finger_btn & OP_DATA_LEFT_BTN); 730d7e34d12SBenson Leung 731d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) 732d7e34d12SBenson Leung input_report_key(input, BTN_MIDDLE, 733d7e34d12SBenson Leung data.finger_btn & OP_DATA_MIDDLE_BTN); 734d7e34d12SBenson Leung 735d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) 736d7e34d12SBenson Leung input_report_key(input, BTN_RIGHT, 737d7e34d12SBenson Leung data.finger_btn & OP_DATA_RIGHT_BTN); 738d7e34d12SBenson Leung 739d7e34d12SBenson Leung input_sync(input); 740d7e34d12SBenson Leung 741d7e34d12SBenson Leung out: 742d7e34d12SBenson Leung return IRQ_HANDLED; 743d7e34d12SBenson Leung } 744d7e34d12SBenson Leung 7456ddaf744SBenson Leung static u8 cyapa_check_adapter_functionality(struct i2c_client *client) 7466ddaf744SBenson Leung { 7476ddaf744SBenson Leung u8 ret = CYAPA_ADAPTER_FUNC_NONE; 7486ddaf744SBenson Leung 7496ddaf744SBenson Leung if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 7506ddaf744SBenson Leung ret |= CYAPA_ADAPTER_FUNC_I2C; 7516ddaf744SBenson Leung if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | 7526ddaf744SBenson Leung I2C_FUNC_SMBUS_BLOCK_DATA | 7536ddaf744SBenson Leung I2C_FUNC_SMBUS_I2C_BLOCK)) 7546ddaf744SBenson Leung ret |= CYAPA_ADAPTER_FUNC_SMBUS; 7556ddaf744SBenson Leung return ret; 7566ddaf744SBenson Leung } 7576ddaf744SBenson Leung 758b1cfa7b4SDudley Du static int cyapa_open(struct input_dev *input) 759b1cfa7b4SDudley Du { 760b1cfa7b4SDudley Du struct cyapa *cyapa = input_get_drvdata(input); 761b1cfa7b4SDudley Du struct i2c_client *client = cyapa->client; 762b1cfa7b4SDudley Du int error; 763b1cfa7b4SDudley Du 764b1cfa7b4SDudley Du error = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); 765b1cfa7b4SDudley Du if (error) { 766b1cfa7b4SDudley Du dev_err(&client->dev, "set active power failed: %d\n", error); 767b1cfa7b4SDudley Du return error; 768b1cfa7b4SDudley Du } 769b1cfa7b4SDudley Du 770b1cfa7b4SDudley Du enable_irq(client->irq); 771b1cfa7b4SDudley Du return 0; 772b1cfa7b4SDudley Du } 773b1cfa7b4SDudley Du 774b1cfa7b4SDudley Du static void cyapa_close(struct input_dev *input) 775b1cfa7b4SDudley Du { 776b1cfa7b4SDudley Du struct cyapa *cyapa = input_get_drvdata(input); 777b1cfa7b4SDudley Du 778b1cfa7b4SDudley Du disable_irq(cyapa->client->irq); 779b1cfa7b4SDudley Du cyapa_set_power_mode(cyapa, PWR_MODE_OFF); 780b1cfa7b4SDudley Du } 781b1cfa7b4SDudley Du 782d7e34d12SBenson Leung static int cyapa_create_input_dev(struct cyapa *cyapa) 783d7e34d12SBenson Leung { 784d7e34d12SBenson Leung struct device *dev = &cyapa->client->dev; 785d7e34d12SBenson Leung struct input_dev *input; 786b1cfa7b4SDudley Du int error; 787d7e34d12SBenson Leung 788d7e34d12SBenson Leung if (!cyapa->physical_size_x || !cyapa->physical_size_y) 789d7e34d12SBenson Leung return -EINVAL; 790d7e34d12SBenson Leung 791b1cfa7b4SDudley Du input = devm_input_allocate_device(dev); 792d7e34d12SBenson Leung if (!input) { 793d7e34d12SBenson Leung dev_err(dev, "allocate memory for input device failed\n"); 794d7e34d12SBenson Leung return -ENOMEM; 795d7e34d12SBenson Leung } 796d7e34d12SBenson Leung 797d7e34d12SBenson Leung input->name = CYAPA_NAME; 798d7e34d12SBenson Leung input->phys = cyapa->phys; 799d7e34d12SBenson Leung input->id.bustype = BUS_I2C; 800d7e34d12SBenson Leung input->id.version = 1; 801d7e34d12SBenson Leung input->id.product = 0; /* means any product in eventcomm. */ 802d7e34d12SBenson Leung input->dev.parent = &cyapa->client->dev; 803d7e34d12SBenson Leung 804b1cfa7b4SDudley Du input->open = cyapa_open; 805b1cfa7b4SDudley Du input->close = cyapa_close; 806b1cfa7b4SDudley Du 807d7e34d12SBenson Leung input_set_drvdata(input, cyapa); 808d7e34d12SBenson Leung 809d7e34d12SBenson Leung __set_bit(EV_ABS, input->evbit); 810d7e34d12SBenson Leung 811d7e34d12SBenson Leung /* finger position */ 812d7e34d12SBenson Leung input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0, 813d7e34d12SBenson Leung 0); 814d7e34d12SBenson Leung input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0, 815d7e34d12SBenson Leung 0); 816d7e34d12SBenson Leung input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); 817d7e34d12SBenson Leung 818d7e34d12SBenson Leung input_abs_set_res(input, ABS_MT_POSITION_X, 819d7e34d12SBenson Leung cyapa->max_abs_x / cyapa->physical_size_x); 820d7e34d12SBenson Leung input_abs_set_res(input, ABS_MT_POSITION_Y, 821d7e34d12SBenson Leung cyapa->max_abs_y / cyapa->physical_size_y); 822d7e34d12SBenson Leung 823d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) 824d7e34d12SBenson Leung __set_bit(BTN_LEFT, input->keybit); 825d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) 826d7e34d12SBenson Leung __set_bit(BTN_MIDDLE, input->keybit); 827d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) 828d7e34d12SBenson Leung __set_bit(BTN_RIGHT, input->keybit); 829d7e34d12SBenson Leung 830d7e34d12SBenson Leung if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK) 831d7e34d12SBenson Leung __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); 832d7e34d12SBenson Leung 833d7e34d12SBenson Leung /* handle pointer emulation and unused slots in core */ 834b1cfa7b4SDudley Du error = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS, 835d7e34d12SBenson Leung INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); 836b1cfa7b4SDudley Du if (error) { 837b1cfa7b4SDudley Du dev_err(dev, "failed to initialize MT slots: %d\n", error); 838b1cfa7b4SDudley Du return error; 839d7e34d12SBenson Leung } 840d7e34d12SBenson Leung 841b1cfa7b4SDudley Du cyapa->input = input; 842d7e34d12SBenson Leung return 0; 843d7e34d12SBenson Leung } 844d7e34d12SBenson Leung 845d7e34d12SBenson Leung static int cyapa_probe(struct i2c_client *client, 846d7e34d12SBenson Leung const struct i2c_device_id *dev_id) 847d7e34d12SBenson Leung { 848d7e34d12SBenson Leung struct device *dev = &client->dev; 849b1cfa7b4SDudley Du struct cyapa *cyapa; 850b1cfa7b4SDudley Du u8 adapter_func; 851b1cfa7b4SDudley Du int error; 852d7e34d12SBenson Leung 8536ddaf744SBenson Leung adapter_func = cyapa_check_adapter_functionality(client); 8546ddaf744SBenson Leung if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) { 8556ddaf744SBenson Leung dev_err(dev, "not a supported I2C/SMBus adapter\n"); 8566ddaf744SBenson Leung return -EIO; 8576ddaf744SBenson Leung } 8586ddaf744SBenson Leung 859b1cfa7b4SDudley Du cyapa = devm_kzalloc(dev, sizeof(struct cyapa), GFP_KERNEL); 860b1cfa7b4SDudley Du if (!cyapa) 861d7e34d12SBenson Leung return -ENOMEM; 862d7e34d12SBenson Leung 863d7e34d12SBenson Leung cyapa->gen = CYAPA_GEN3; 864d7e34d12SBenson Leung cyapa->client = client; 865d7e34d12SBenson Leung i2c_set_clientdata(client, cyapa); 866d7e34d12SBenson Leung sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr, 867d7e34d12SBenson Leung client->addr); 868d7e34d12SBenson Leung 8696ddaf744SBenson Leung /* i2c isn't supported, use smbus */ 8706ddaf744SBenson Leung if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS) 8716ddaf744SBenson Leung cyapa->smbus = true; 872b1cfa7b4SDudley Du 873d7e34d12SBenson Leung cyapa->state = CYAPA_STATE_NO_DEVICE; 874b1cfa7b4SDudley Du 875b1cfa7b4SDudley Du error = cyapa_check_is_operational(cyapa); 876b1cfa7b4SDudley Du if (error) { 877b1cfa7b4SDudley Du dev_err(dev, "device not operational, %d\n", error); 878b1cfa7b4SDudley Du return error; 879d7e34d12SBenson Leung } 880d7e34d12SBenson Leung 881b1cfa7b4SDudley Du /* Power down the device until we need it */ 882b1cfa7b4SDudley Du error = cyapa_set_power_mode(cyapa, PWR_MODE_OFF); 883b1cfa7b4SDudley Du if (error) { 884b1cfa7b4SDudley Du dev_err(dev, "failed to quiesce the device: %d\n", error); 885b1cfa7b4SDudley Du return error; 886d7e34d12SBenson Leung } 887d7e34d12SBenson Leung 888b1cfa7b4SDudley Du error = cyapa_create_input_dev(cyapa); 889b1cfa7b4SDudley Du if (error) 890b1cfa7b4SDudley Du return error; 891d7e34d12SBenson Leung 892b1cfa7b4SDudley Du error = devm_request_threaded_irq(dev, client->irq, 893b1cfa7b4SDudley Du NULL, cyapa_irq, 894d7e34d12SBenson Leung IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 895b1cfa7b4SDudley Du "cyapa", cyapa); 896b1cfa7b4SDudley Du if (error) { 897b1cfa7b4SDudley Du dev_err(dev, "IRQ request failed: %d\n, ", error); 898b1cfa7b4SDudley Du return error; 899d7e34d12SBenson Leung } 900d7e34d12SBenson Leung 901b1cfa7b4SDudley Du /* Disable IRQ until the device is opened */ 902b1cfa7b4SDudley Du disable_irq(client->irq); 903d7e34d12SBenson Leung 904b1cfa7b4SDudley Du /* Register the device in input subsystem */ 905b1cfa7b4SDudley Du error = input_register_device(cyapa->input); 906b1cfa7b4SDudley Du if (error) { 907b1cfa7b4SDudley Du dev_err(dev, "failed to register input device: %d\n", error); 908b1cfa7b4SDudley Du return error; 909d7e34d12SBenson Leung } 910d7e34d12SBenson Leung 911d7e34d12SBenson Leung return 0; 912d7e34d12SBenson Leung } 913d7e34d12SBenson Leung 914572081a4SJingoo Han static int __maybe_unused cyapa_suspend(struct device *dev) 915d7e34d12SBenson Leung { 916b1cfa7b4SDudley Du struct i2c_client *client = to_i2c_client(dev); 917b1cfa7b4SDudley Du struct cyapa *cyapa = i2c_get_clientdata(client); 918b1cfa7b4SDudley Du struct input_dev *input = cyapa->input; 919d7e34d12SBenson Leung u8 power_mode; 920b1cfa7b4SDudley Du int error; 921d7e34d12SBenson Leung 922b1cfa7b4SDudley Du error = mutex_lock_interruptible(&input->mutex); 923b1cfa7b4SDudley Du if (error) 924b1cfa7b4SDudley Du return error; 925b1cfa7b4SDudley Du 926b1cfa7b4SDudley Du disable_irq(client->irq); 927d7e34d12SBenson Leung 928d7e34d12SBenson Leung /* 929d7e34d12SBenson Leung * Set trackpad device to idle mode if wakeup is allowed, 930d7e34d12SBenson Leung * otherwise turn off. 931d7e34d12SBenson Leung */ 932d7e34d12SBenson Leung power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE 933d7e34d12SBenson Leung : PWR_MODE_OFF; 934b1cfa7b4SDudley Du error = cyapa_set_power_mode(cyapa, power_mode); 935b1cfa7b4SDudley Du if (error) 936b1cfa7b4SDudley Du dev_err(dev, "resume: set power mode to %d failed: %d\n", 937b1cfa7b4SDudley Du power_mode, error); 938d7e34d12SBenson Leung 939d7e34d12SBenson Leung if (device_may_wakeup(dev)) 940f68a95cdSDudley Du cyapa->irq_wake = (enable_irq_wake(client->irq) == 0); 941b1cfa7b4SDudley Du 942b1cfa7b4SDudley Du mutex_unlock(&input->mutex); 943b1cfa7b4SDudley Du 944d7e34d12SBenson Leung return 0; 945d7e34d12SBenson Leung } 946d7e34d12SBenson Leung 947572081a4SJingoo Han static int __maybe_unused cyapa_resume(struct device *dev) 948d7e34d12SBenson Leung { 949b1cfa7b4SDudley Du struct i2c_client *client = to_i2c_client(dev); 950b1cfa7b4SDudley Du struct cyapa *cyapa = i2c_get_clientdata(client); 951b1cfa7b4SDudley Du struct input_dev *input = cyapa->input; 952b1cfa7b4SDudley Du u8 power_mode; 953b1cfa7b4SDudley Du int error; 954b1cfa7b4SDudley Du 955b1cfa7b4SDudley Du mutex_lock(&input->mutex); 956d7e34d12SBenson Leung 957d7e34d12SBenson Leung if (device_may_wakeup(dev) && cyapa->irq_wake) 958f68a95cdSDudley Du disable_irq_wake(client->irq); 959d7e34d12SBenson Leung 960b1cfa7b4SDudley Du power_mode = input->users ? PWR_MODE_FULL_ACTIVE : PWR_MODE_OFF; 961b1cfa7b4SDudley Du error = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); 962b1cfa7b4SDudley Du if (error) 963b1cfa7b4SDudley Du dev_warn(dev, "resume: set power mode to %d failed: %d\n", 964b1cfa7b4SDudley Du power_mode, error); 965d7e34d12SBenson Leung 966f68a95cdSDudley Du enable_irq(client->irq); 967b1cfa7b4SDudley Du 968b1cfa7b4SDudley Du mutex_unlock(&input->mutex); 969b1cfa7b4SDudley Du 970d7e34d12SBenson Leung return 0; 971d7e34d12SBenson Leung } 972d7e34d12SBenson Leung 973d7e34d12SBenson Leung static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume); 974d7e34d12SBenson Leung 975d7e34d12SBenson Leung static const struct i2c_device_id cyapa_id_table[] = { 976d7e34d12SBenson Leung { "cyapa", 0 }, 977d7e34d12SBenson Leung { }, 978d7e34d12SBenson Leung }; 979d7e34d12SBenson Leung MODULE_DEVICE_TABLE(i2c, cyapa_id_table); 980d7e34d12SBenson Leung 981d7e34d12SBenson Leung static struct i2c_driver cyapa_driver = { 982d7e34d12SBenson Leung .driver = { 983d7e34d12SBenson Leung .name = "cyapa", 984d7e34d12SBenson Leung .owner = THIS_MODULE, 985d7e34d12SBenson Leung .pm = &cyapa_pm_ops, 986d7e34d12SBenson Leung }, 987d7e34d12SBenson Leung 988d7e34d12SBenson Leung .probe = cyapa_probe, 989d7e34d12SBenson Leung .id_table = cyapa_id_table, 990d7e34d12SBenson Leung }; 991d7e34d12SBenson Leung 992d7e34d12SBenson Leung module_i2c_driver(cyapa_driver); 993d7e34d12SBenson Leung 994d7e34d12SBenson Leung MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver"); 995d7e34d12SBenson Leung MODULE_AUTHOR("Dudley Du <dudl@cypress.com>"); 996d7e34d12SBenson Leung MODULE_LICENSE("GPL"); 997