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 * 9823a11fdSDudley Du * Copyright (C) 2011-2014 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> 239f1cd857SDudley Du #include <linux/mutex.h> 24d7e34d12SBenson Leung #include <linux/slab.h> 259f1cd857SDudley Du #include <linux/uaccess.h> 2667286508SDudley Du #include <linux/pm_runtime.h> 279f1cd857SDudley Du #include "cyapa.h" 28d7e34d12SBenson Leung 29d7e34d12SBenson Leung 306ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_NONE 0 316ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_I2C 1 326ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_SMBUS 2 336ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_BOTH 3 346ddaf744SBenson Leung 35*c806b0b8SDudley Du #define CYAPA_FW_NAME "cyapa.bin" 36*c806b0b8SDudley Du 379f1cd857SDudley Du const char product_id[] = "CYTRA"; 386ddaf744SBenson Leung 399f1cd857SDudley Du static int cyapa_reinitialize(struct cyapa *cyapa); 406ddaf744SBenson Leung 419f1cd857SDudley Du static inline bool cyapa_is_bootloader_mode(struct cyapa *cyapa) 42d7e34d12SBenson Leung { 439f1cd857SDudley Du if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_BL) 449f1cd857SDudley Du return true; 459f1cd857SDudley Du 469f1cd857SDudley Du if (cyapa->gen == CYAPA_GEN3 && 479f1cd857SDudley Du cyapa->state >= CYAPA_STATE_BL_BUSY && 489f1cd857SDudley Du cyapa->state <= CYAPA_STATE_BL_ACTIVE) 499f1cd857SDudley Du return true; 509f1cd857SDudley Du 519f1cd857SDudley Du return false; 52d7e34d12SBenson Leung } 53d7e34d12SBenson Leung 549f1cd857SDudley Du static inline bool cyapa_is_operational_mode(struct cyapa *cyapa) 55d7e34d12SBenson Leung { 569f1cd857SDudley Du if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_APP) 579f1cd857SDudley Du return true; 589f1cd857SDudley Du 599f1cd857SDudley Du if (cyapa->gen == CYAPA_GEN3 && cyapa->state == CYAPA_STATE_OP) 609f1cd857SDudley Du return true; 619f1cd857SDudley Du 629f1cd857SDudley Du return false; 63d7e34d12SBenson Leung } 64d7e34d12SBenson Leung 659f1cd857SDudley Du /* Returns 0 on success, else negative errno on failure. */ 669f1cd857SDudley Du static ssize_t cyapa_i2c_read(struct cyapa *cyapa, u8 reg, size_t len, 676ddaf744SBenson Leung u8 *values) 686ddaf744SBenson Leung { 696ddaf744SBenson Leung struct i2c_client *client = cyapa->client; 709f1cd857SDudley Du struct i2c_msg msgs[] = { 71d7e34d12SBenson Leung { 729f1cd857SDudley Du .addr = client->addr, 739f1cd857SDudley Du .flags = 0, 749f1cd857SDudley Du .len = 1, 759f1cd857SDudley Du .buf = ®, 769f1cd857SDudley Du }, 77d7e34d12SBenson Leung { 789f1cd857SDudley Du .addr = client->addr, 799f1cd857SDudley Du .flags = I2C_M_RD, 809f1cd857SDudley Du .len = len, 819f1cd857SDudley Du .buf = values, 829f1cd857SDudley Du }, 839f1cd857SDudley Du }; 849f1cd857SDudley Du int ret; 85d7e34d12SBenson Leung 869f1cd857SDudley Du ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 879f1cd857SDudley Du 889f1cd857SDudley Du if (ret != ARRAY_SIZE(msgs)) 899f1cd857SDudley Du return ret < 0 ? ret : -EIO; 909f1cd857SDudley Du 919f1cd857SDudley Du return 0; 92d7e34d12SBenson Leung } 93d7e34d12SBenson Leung 949f1cd857SDudley Du /** 959f1cd857SDudley Du * cyapa_i2c_write - Execute i2c block data write operation 969f1cd857SDudley Du * @cyapa: Handle to this driver 979f1cd857SDudley Du * @ret: Offset of the data to written in the register map 989f1cd857SDudley Du * @len: number of bytes to write 999f1cd857SDudley Du * @values: Data to be written 1009f1cd857SDudley Du * 1019f1cd857SDudley Du * Return negative errno code on error; return zero when success. 1029f1cd857SDudley Du */ 1039f1cd857SDudley Du static int cyapa_i2c_write(struct cyapa *cyapa, u8 reg, 1049f1cd857SDudley Du size_t len, const void *values) 105d7e34d12SBenson Leung { 1069f1cd857SDudley Du struct i2c_client *client = cyapa->client; 1079f1cd857SDudley Du char buf[32]; 1089f1cd857SDudley Du int ret; 109d7e34d12SBenson Leung 1109f1cd857SDudley Du if (len > sizeof(buf) - 1) 1119f1cd857SDudley Du return -ENOMEM; 1129f1cd857SDudley Du 1139f1cd857SDudley Du buf[0] = reg; 1149f1cd857SDudley Du memcpy(&buf[1], values, len); 1159f1cd857SDudley Du 1169f1cd857SDudley Du ret = i2c_master_send(client, buf, len + 1); 1179f1cd857SDudley Du if (ret != len + 1) 1189f1cd857SDudley Du return ret < 0 ? ret : -EIO; 1199f1cd857SDudley Du 1209f1cd857SDudley Du return 0; 121d7e34d12SBenson Leung } 1229f1cd857SDudley Du 1239f1cd857SDudley Du static u8 cyapa_check_adapter_functionality(struct i2c_client *client) 1249f1cd857SDudley Du { 1259f1cd857SDudley Du u8 ret = CYAPA_ADAPTER_FUNC_NONE; 1269f1cd857SDudley Du 1279f1cd857SDudley Du if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 1289f1cd857SDudley Du ret |= CYAPA_ADAPTER_FUNC_I2C; 1299f1cd857SDudley Du if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | 1309f1cd857SDudley Du I2C_FUNC_SMBUS_BLOCK_DATA | 1319f1cd857SDudley Du I2C_FUNC_SMBUS_I2C_BLOCK)) 1329f1cd857SDudley Du ret |= CYAPA_ADAPTER_FUNC_SMBUS; 1339f1cd857SDudley Du return ret; 1346ddaf744SBenson Leung } 135d7e34d12SBenson Leung 136d7e34d12SBenson Leung /* 137d7e34d12SBenson Leung * Query device for its current operating state. 138d7e34d12SBenson Leung */ 139d7e34d12SBenson Leung static int cyapa_get_state(struct cyapa *cyapa) 140d7e34d12SBenson Leung { 141d7e34d12SBenson Leung u8 status[BL_STATUS_SIZE]; 1429f1cd857SDudley Du u8 cmd[32]; 1439f1cd857SDudley Du /* The i2c address of gen4 and gen5 trackpad device must be even. */ 1449f1cd857SDudley Du bool even_addr = ((cyapa->client->addr & 0x0001) == 0); 1459f1cd857SDudley Du bool smbus = false; 1469f1cd857SDudley Du int retries = 2; 147823a11fdSDudley Du int error; 148d7e34d12SBenson Leung 149d7e34d12SBenson Leung cyapa->state = CYAPA_STATE_NO_DEVICE; 150d7e34d12SBenson Leung 151d7e34d12SBenson Leung /* 152d7e34d12SBenson Leung * Get trackpad status by reading 3 registers starting from 0. 153d7e34d12SBenson Leung * If the device is in the bootloader, this will be BL_HEAD. 154d7e34d12SBenson Leung * If the device is in operation mode, this will be the DATA regs. 155d7e34d12SBenson Leung * 156d7e34d12SBenson Leung */ 157823a11fdSDudley Du error = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE, 158d7e34d12SBenson Leung status); 1596ddaf744SBenson Leung 1606ddaf744SBenson Leung /* 1616ddaf744SBenson Leung * On smbus systems in OP mode, the i2c_reg_read will fail with 1626ddaf744SBenson Leung * -ETIMEDOUT. In this case, try again using the smbus equivalent 1636ddaf744SBenson Leung * command. This should return a BL_HEAD indicating CYAPA_STATE_OP. 1646ddaf744SBenson Leung */ 1659f1cd857SDudley Du if (cyapa->smbus && (error == -ETIMEDOUT || error == -ENXIO)) { 1669f1cd857SDudley Du if (!even_addr) 1679f1cd857SDudley Du error = cyapa_read_block(cyapa, 1689f1cd857SDudley Du CYAPA_CMD_BL_STATUS, status); 1699f1cd857SDudley Du smbus = true; 1709f1cd857SDudley Du } 1716ddaf744SBenson Leung 172823a11fdSDudley Du if (error != BL_STATUS_SIZE) 173d7e34d12SBenson Leung goto error; 174d7e34d12SBenson Leung 1759f1cd857SDudley Du /* 1769f1cd857SDudley Du * Detect trackpad protocol based on characteristic registers and bits. 1779f1cd857SDudley Du */ 1789f1cd857SDudley Du do { 1799f1cd857SDudley Du cyapa->status[REG_OP_STATUS] = status[REG_OP_STATUS]; 1809f1cd857SDudley Du cyapa->status[REG_BL_STATUS] = status[REG_BL_STATUS]; 1819f1cd857SDudley Du cyapa->status[REG_BL_ERROR] = status[REG_BL_ERROR]; 1829f1cd857SDudley Du 1839f1cd857SDudley Du if (cyapa->gen == CYAPA_GEN_UNKNOWN || 1849f1cd857SDudley Du cyapa->gen == CYAPA_GEN3) { 1859f1cd857SDudley Du error = cyapa_gen3_ops.state_parse(cyapa, 1869f1cd857SDudley Du status, BL_STATUS_SIZE); 1879f1cd857SDudley Du if (!error) 1889f1cd857SDudley Du goto out_detected; 189d7e34d12SBenson Leung } 1906972a859SDudley Du if ((cyapa->gen == CYAPA_GEN_UNKNOWN || 1916972a859SDudley Du cyapa->gen == CYAPA_GEN5) && 1926972a859SDudley Du !smbus && even_addr) { 1936972a859SDudley Du error = cyapa_gen5_ops.state_parse(cyapa, 1946972a859SDudley Du status, BL_STATUS_SIZE); 1956972a859SDudley Du if (!error) 1966972a859SDudley Du goto out_detected; 1976972a859SDudley Du } 198d7e34d12SBenson Leung 1999f1cd857SDudley Du /* 2009f1cd857SDudley Du * Write 0x00 0x00 to trackpad device to force update its 2019f1cd857SDudley Du * status, then redo the detection again. 2029f1cd857SDudley Du */ 2039f1cd857SDudley Du if (!smbus) { 2049f1cd857SDudley Du cmd[0] = 0x00; 2059f1cd857SDudley Du cmd[1] = 0x00; 2069f1cd857SDudley Du error = cyapa_i2c_write(cyapa, 0, 2, cmd); 2079f1cd857SDudley Du if (error) 2089f1cd857SDudley Du goto error; 2099f1cd857SDudley Du 2109f1cd857SDudley Du msleep(50); 2119f1cd857SDudley Du 2129f1cd857SDudley Du error = cyapa_i2c_read(cyapa, BL_HEAD_OFFSET, 2139f1cd857SDudley Du BL_STATUS_SIZE, status); 2149f1cd857SDudley Du if (error) 2159f1cd857SDudley Du goto error; 2169f1cd857SDudley Du } 2179f1cd857SDudley Du } while (--retries > 0 && !smbus); 2189f1cd857SDudley Du 2199f1cd857SDudley Du goto error; 2209f1cd857SDudley Du 2219f1cd857SDudley Du out_detected: 2229f1cd857SDudley Du if (cyapa->state <= CYAPA_STATE_BL_BUSY) 2239f1cd857SDudley Du return -EAGAIN; 224d7e34d12SBenson Leung return 0; 2259f1cd857SDudley Du 226d7e34d12SBenson Leung error: 227823a11fdSDudley Du return (error < 0) ? error : -EAGAIN; 228d7e34d12SBenson Leung } 229d7e34d12SBenson Leung 230d7e34d12SBenson Leung /* 231d7e34d12SBenson Leung * Poll device for its status in a loop, waiting up to timeout for a response. 232d7e34d12SBenson Leung * 233d7e34d12SBenson Leung * When the device switches state, it usually takes ~300 ms. 234d7e34d12SBenson Leung * However, when running a new firmware image, the device must calibrate its 235d7e34d12SBenson Leung * sensors, which can take as long as 2 seconds. 236d7e34d12SBenson Leung * 237d7e34d12SBenson Leung * Note: The timeout has granularity of the polling rate, which is 100 ms. 238d7e34d12SBenson Leung * 239d7e34d12SBenson Leung * Returns: 240d7e34d12SBenson Leung * 0 when the device eventually responds with a valid non-busy state. 241d7e34d12SBenson Leung * -ETIMEDOUT if device never responds (too many -EAGAIN) 2429f1cd857SDudley Du * -EAGAIN if bootload is busy, or unknown state. 243d7e34d12SBenson Leung * < 0 other errors 244d7e34d12SBenson Leung */ 2459f1cd857SDudley Du int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout) 246d7e34d12SBenson Leung { 247823a11fdSDudley Du int error; 248d7e34d12SBenson Leung int tries = timeout / 100; 249d7e34d12SBenson Leung 2509f1cd857SDudley Du do { 251823a11fdSDudley Du error = cyapa_get_state(cyapa); 2529f1cd857SDudley Du if (!error && cyapa->state > CYAPA_STATE_BL_BUSY) 2539f1cd857SDudley Du return 0; 2549f1cd857SDudley Du 255d7e34d12SBenson Leung msleep(100); 2569f1cd857SDudley Du } while (tries--); 2579f1cd857SDudley Du 258823a11fdSDudley Du return (error == -EAGAIN || error == -ETIMEDOUT) ? -ETIMEDOUT : error; 259d7e34d12SBenson Leung } 260d7e34d12SBenson Leung 261d7e34d12SBenson Leung /* 262d7e34d12SBenson Leung * Check if device is operational. 263d7e34d12SBenson Leung * 264d7e34d12SBenson Leung * An operational device is responding, has exited bootloader, and has 265d7e34d12SBenson Leung * firmware supported by this driver. 266d7e34d12SBenson Leung * 267d7e34d12SBenson Leung * Returns: 2689f1cd857SDudley Du * -ENODEV no device 269d7e34d12SBenson Leung * -EBUSY no device or in bootloader 270d7e34d12SBenson Leung * -EIO failure while reading from device 2719f1cd857SDudley Du * -ETIMEDOUT timeout failure for bus idle or bus no response 272d7e34d12SBenson Leung * -EAGAIN device is still in bootloader 273d7e34d12SBenson Leung * if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware 274d7e34d12SBenson Leung * -EINVAL device is in operational mode, but not supported by this driver 275d7e34d12SBenson Leung * 0 device is supported 276d7e34d12SBenson Leung */ 277d7e34d12SBenson Leung static int cyapa_check_is_operational(struct cyapa *cyapa) 278d7e34d12SBenson Leung { 279823a11fdSDudley Du int error; 280d7e34d12SBenson Leung 2819f1cd857SDudley Du error = cyapa_poll_state(cyapa, 4000); 282823a11fdSDudley Du if (error) 283823a11fdSDudley Du return error; 284d7e34d12SBenson Leung 2859f1cd857SDudley Du switch (cyapa->gen) { 2866972a859SDudley Du case CYAPA_GEN5: 2876972a859SDudley Du cyapa->ops = &cyapa_gen5_ops; 2886972a859SDudley Du break; 2899f1cd857SDudley Du case CYAPA_GEN3: 2909f1cd857SDudley Du cyapa->ops = &cyapa_gen3_ops; 2919f1cd857SDudley Du break; 292d7e34d12SBenson Leung default: 2939f1cd857SDudley Du return -ENODEV; 294d7e34d12SBenson Leung } 2959f1cd857SDudley Du 2969f1cd857SDudley Du error = cyapa->ops->operational_check(cyapa); 2979f1cd857SDudley Du if (!error && cyapa_is_operational_mode(cyapa)) 2989f1cd857SDudley Du cyapa->operational = true; 2999f1cd857SDudley Du else 3009f1cd857SDudley Du cyapa->operational = false; 3019f1cd857SDudley Du 3029f1cd857SDudley Du return error; 3039f1cd857SDudley Du } 3049f1cd857SDudley Du 3059f1cd857SDudley Du 3069f1cd857SDudley Du /* 3079f1cd857SDudley Du * Returns 0 on device detected, negative errno on no device detected. 3089f1cd857SDudley Du * And when the device is detected and opertaional, it will be reset to 3099f1cd857SDudley Du * full power active mode automatically. 3109f1cd857SDudley Du */ 3119f1cd857SDudley Du static int cyapa_detect(struct cyapa *cyapa) 3129f1cd857SDudley Du { 3139f1cd857SDudley Du struct device *dev = &cyapa->client->dev; 3149f1cd857SDudley Du int error; 3159f1cd857SDudley Du 3169f1cd857SDudley Du error = cyapa_check_is_operational(cyapa); 3179f1cd857SDudley Du if (error) { 3189f1cd857SDudley Du if (error != -ETIMEDOUT && error != -ENODEV && 3199f1cd857SDudley Du cyapa_is_bootloader_mode(cyapa)) { 3209f1cd857SDudley Du dev_warn(dev, "device detected but not operational\n"); 321d7e34d12SBenson Leung return 0; 322d7e34d12SBenson Leung } 323d7e34d12SBenson Leung 3249f1cd857SDudley Du dev_err(dev, "no device detected: %d\n", error); 3259f1cd857SDudley Du return error; 326d7e34d12SBenson Leung } 327d7e34d12SBenson Leung 3289f1cd857SDudley Du return 0; 3296ddaf744SBenson Leung } 3306ddaf744SBenson Leung 331b1cfa7b4SDudley Du static int cyapa_open(struct input_dev *input) 332b1cfa7b4SDudley Du { 333b1cfa7b4SDudley Du struct cyapa *cyapa = input_get_drvdata(input); 334b1cfa7b4SDudley Du struct i2c_client *client = cyapa->client; 335b1cfa7b4SDudley Du int error; 336b1cfa7b4SDudley Du 3379f1cd857SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 3389f1cd857SDudley Du if (error) 339b1cfa7b4SDudley Du return error; 3409f1cd857SDudley Du 3419f1cd857SDudley Du if (cyapa->operational) { 3429f1cd857SDudley Du /* 3439f1cd857SDudley Du * though failed to set active power mode, 3449f1cd857SDudley Du * but still may be able to work in lower scan rate 3459f1cd857SDudley Du * when in operational mode. 3469f1cd857SDudley Du */ 3479f1cd857SDudley Du error = cyapa->ops->set_power_mode(cyapa, 3489f1cd857SDudley Du PWR_MODE_FULL_ACTIVE, 0); 3499f1cd857SDudley Du if (error) { 3509f1cd857SDudley Du dev_warn(&client->dev, 3519f1cd857SDudley Du "set active power failed: %d\n", error); 3529f1cd857SDudley Du goto out; 3539f1cd857SDudley Du } 3549f1cd857SDudley Du } else { 3559f1cd857SDudley Du error = cyapa_reinitialize(cyapa); 3569f1cd857SDudley Du if (error || !cyapa->operational) { 3579f1cd857SDudley Du error = error ? error : -EAGAIN; 3589f1cd857SDudley Du goto out; 3599f1cd857SDudley Du } 360b1cfa7b4SDudley Du } 361b1cfa7b4SDudley Du 362b1cfa7b4SDudley Du enable_irq(client->irq); 36367286508SDudley Du if (!pm_runtime_enabled(&client->dev)) { 36467286508SDudley Du pm_runtime_set_active(&client->dev); 36567286508SDudley Du pm_runtime_enable(&client->dev); 36667286508SDudley Du } 3679f1cd857SDudley Du out: 3689f1cd857SDudley Du mutex_unlock(&cyapa->state_sync_lock); 3699f1cd857SDudley Du return error; 370b1cfa7b4SDudley Du } 371b1cfa7b4SDudley Du 372b1cfa7b4SDudley Du static void cyapa_close(struct input_dev *input) 373b1cfa7b4SDudley Du { 374b1cfa7b4SDudley Du struct cyapa *cyapa = input_get_drvdata(input); 3759f1cd857SDudley Du struct i2c_client *client = cyapa->client; 376b1cfa7b4SDudley Du 3779f1cd857SDudley Du mutex_lock(&cyapa->state_sync_lock); 3789f1cd857SDudley Du 3799f1cd857SDudley Du disable_irq(client->irq); 38067286508SDudley Du if (pm_runtime_enabled(&client->dev)) 38167286508SDudley Du pm_runtime_disable(&client->dev); 38267286508SDudley Du pm_runtime_set_suspended(&client->dev); 38367286508SDudley Du 3849f1cd857SDudley Du if (cyapa->operational) 3859f1cd857SDudley Du cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); 3869f1cd857SDudley Du 3879f1cd857SDudley Du mutex_unlock(&cyapa->state_sync_lock); 388b1cfa7b4SDudley Du } 389b1cfa7b4SDudley Du 390d7e34d12SBenson Leung static int cyapa_create_input_dev(struct cyapa *cyapa) 391d7e34d12SBenson Leung { 392d7e34d12SBenson Leung struct device *dev = &cyapa->client->dev; 393d7e34d12SBenson Leung struct input_dev *input; 394b1cfa7b4SDudley Du int error; 395d7e34d12SBenson Leung 396d7e34d12SBenson Leung if (!cyapa->physical_size_x || !cyapa->physical_size_y) 397d7e34d12SBenson Leung return -EINVAL; 398d7e34d12SBenson Leung 399b1cfa7b4SDudley Du input = devm_input_allocate_device(dev); 400d7e34d12SBenson Leung if (!input) { 401823a11fdSDudley Du dev_err(dev, "failed to allocate memory for input device.\n"); 402d7e34d12SBenson Leung return -ENOMEM; 403d7e34d12SBenson Leung } 404d7e34d12SBenson Leung 405d7e34d12SBenson Leung input->name = CYAPA_NAME; 406d7e34d12SBenson Leung input->phys = cyapa->phys; 407d7e34d12SBenson Leung input->id.bustype = BUS_I2C; 408d7e34d12SBenson Leung input->id.version = 1; 409823a11fdSDudley Du input->id.product = 0; /* Means any product in eventcomm. */ 410d7e34d12SBenson Leung input->dev.parent = &cyapa->client->dev; 411d7e34d12SBenson Leung 412b1cfa7b4SDudley Du input->open = cyapa_open; 413b1cfa7b4SDudley Du input->close = cyapa_close; 414b1cfa7b4SDudley Du 415d7e34d12SBenson Leung input_set_drvdata(input, cyapa); 416d7e34d12SBenson Leung 417d7e34d12SBenson Leung __set_bit(EV_ABS, input->evbit); 418d7e34d12SBenson Leung 419823a11fdSDudley Du /* Finger position */ 420d7e34d12SBenson Leung input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0, 421d7e34d12SBenson Leung 0); 422d7e34d12SBenson Leung input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0, 423d7e34d12SBenson Leung 0); 4249f1cd857SDudley Du input_set_abs_params(input, ABS_MT_PRESSURE, 0, cyapa->max_z, 0, 0); 4259f1cd857SDudley Du if (cyapa->gen > CYAPA_GEN3) { 4269f1cd857SDudley Du input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 4279f1cd857SDudley Du input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); 4289f1cd857SDudley Du /* 4299f1cd857SDudley Du * Orientation is the angle between the vertical axis and 4309f1cd857SDudley Du * the major axis of the contact ellipse. 4319f1cd857SDudley Du * The range is -127 to 127. 4329f1cd857SDudley Du * the positive direction is clockwise form the vertical axis. 4339f1cd857SDudley Du * If the ellipse of contact degenerates into a circle, 4349f1cd857SDudley Du * orientation is reported as 0. 4359f1cd857SDudley Du * 4369f1cd857SDudley Du * Also, for Gen5 trackpad the accurate of this orientation 4379f1cd857SDudley Du * value is value + (-30 ~ 30). 4389f1cd857SDudley Du */ 4399f1cd857SDudley Du input_set_abs_params(input, ABS_MT_ORIENTATION, 4409f1cd857SDudley Du -127, 127, 0, 0); 4419f1cd857SDudley Du } 4429f1cd857SDudley Du if (cyapa->gen >= CYAPA_GEN5) { 4439f1cd857SDudley Du input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); 4449f1cd857SDudley Du input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0); 4459f1cd857SDudley Du } 446d7e34d12SBenson Leung 447d7e34d12SBenson Leung input_abs_set_res(input, ABS_MT_POSITION_X, 448d7e34d12SBenson Leung cyapa->max_abs_x / cyapa->physical_size_x); 449d7e34d12SBenson Leung input_abs_set_res(input, ABS_MT_POSITION_Y, 450d7e34d12SBenson Leung cyapa->max_abs_y / cyapa->physical_size_y); 451d7e34d12SBenson Leung 452d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) 453d7e34d12SBenson Leung __set_bit(BTN_LEFT, input->keybit); 454d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) 455d7e34d12SBenson Leung __set_bit(BTN_MIDDLE, input->keybit); 456d7e34d12SBenson Leung if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) 457d7e34d12SBenson Leung __set_bit(BTN_RIGHT, input->keybit); 458d7e34d12SBenson Leung 459d7e34d12SBenson Leung if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK) 460d7e34d12SBenson Leung __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); 461d7e34d12SBenson Leung 462823a11fdSDudley Du /* Handle pointer emulation and unused slots in core */ 463b1cfa7b4SDudley Du error = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS, 464d7e34d12SBenson Leung INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); 465b1cfa7b4SDudley Du if (error) { 466b1cfa7b4SDudley Du dev_err(dev, "failed to initialize MT slots: %d\n", error); 467b1cfa7b4SDudley Du return error; 468d7e34d12SBenson Leung } 469d7e34d12SBenson Leung 4709f1cd857SDudley Du /* Register the device in input subsystem */ 4719f1cd857SDudley Du error = input_register_device(input); 4729f1cd857SDudley Du if (error) { 4739f1cd857SDudley Du dev_err(dev, "failed to register input device: %d\n", error); 4749f1cd857SDudley Du return error; 4759f1cd857SDudley Du } 4769f1cd857SDudley Du 477b1cfa7b4SDudley Du cyapa->input = input; 478d7e34d12SBenson Leung return 0; 479d7e34d12SBenson Leung } 480d7e34d12SBenson Leung 481*c806b0b8SDudley Du static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa) 482*c806b0b8SDudley Du { 483*c806b0b8SDudley Du struct input_dev *input = cyapa->input; 484*c806b0b8SDudley Du 485*c806b0b8SDudley Du if (!input || !input->users) { 486*c806b0b8SDudley Du /* 487*c806b0b8SDudley Du * When input is NULL, TP must be in deep sleep mode. 488*c806b0b8SDudley Du * In this mode, later non-power I2C command will always failed 489*c806b0b8SDudley Du * if not bring it out of deep sleep mode firstly, 490*c806b0b8SDudley Du * so must command TP to active mode here. 491*c806b0b8SDudley Du */ 492*c806b0b8SDudley Du if (!input || cyapa->operational) 493*c806b0b8SDudley Du cyapa->ops->set_power_mode(cyapa, 494*c806b0b8SDudley Du PWR_MODE_FULL_ACTIVE, 0); 495*c806b0b8SDudley Du /* Gen3 always using polling mode for command. */ 496*c806b0b8SDudley Du if (cyapa->gen >= CYAPA_GEN5) 497*c806b0b8SDudley Du enable_irq(cyapa->client->irq); 498*c806b0b8SDudley Du } 499*c806b0b8SDudley Du } 500*c806b0b8SDudley Du 501*c806b0b8SDudley Du static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa) 502*c806b0b8SDudley Du { 503*c806b0b8SDudley Du struct input_dev *input = cyapa->input; 504*c806b0b8SDudley Du 505*c806b0b8SDudley Du if (!input || !input->users) { 506*c806b0b8SDudley Du if (cyapa->gen >= CYAPA_GEN5) 507*c806b0b8SDudley Du disable_irq(cyapa->client->irq); 508*c806b0b8SDudley Du if (!input || cyapa->operational) 509*c806b0b8SDudley Du cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); 510*c806b0b8SDudley Du } 511*c806b0b8SDudley Du } 512*c806b0b8SDudley Du 5139f1cd857SDudley Du /* 5149f1cd857SDudley Du * cyapa_sleep_time_to_pwr_cmd and cyapa_pwr_cmd_to_sleep_time 5159f1cd857SDudley Du * 5169f1cd857SDudley Du * These are helper functions that convert to and from integer idle 5179f1cd857SDudley Du * times and register settings to write to the PowerMode register. 5189f1cd857SDudley Du * The trackpad supports between 20ms to 1000ms scan intervals. 5199f1cd857SDudley Du * The time will be increased in increments of 10ms from 20ms to 100ms. 5209f1cd857SDudley Du * From 100ms to 1000ms, time will be increased in increments of 20ms. 5219f1cd857SDudley Du * 5229f1cd857SDudley Du * When Idle_Time < 100, the format to convert Idle_Time to Idle_Command is: 5239f1cd857SDudley Du * Idle_Command = Idle Time / 10; 5249f1cd857SDudley Du * When Idle_Time >= 100, the format to convert Idle_Time to Idle_Command is: 5259f1cd857SDudley Du * Idle_Command = Idle Time / 20 + 5; 5269f1cd857SDudley Du */ 5279f1cd857SDudley Du u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time) 5289f1cd857SDudley Du { 5299f1cd857SDudley Du u16 encoded_time; 5309f1cd857SDudley Du 5319f1cd857SDudley Du sleep_time = clamp_val(sleep_time, 20, 1000); 5329f1cd857SDudley Du encoded_time = sleep_time < 100 ? sleep_time / 10 : sleep_time / 20 + 5; 5339f1cd857SDudley Du return (encoded_time << 2) & PWR_MODE_MASK; 5349f1cd857SDudley Du } 5359f1cd857SDudley Du 5369f1cd857SDudley Du u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode) 5379f1cd857SDudley Du { 5389f1cd857SDudley Du u8 encoded_time = pwr_mode >> 2; 5399f1cd857SDudley Du 5409f1cd857SDudley Du return (encoded_time < 10) ? encoded_time * 10 5419f1cd857SDudley Du : (encoded_time - 5) * 20; 5429f1cd857SDudley Du } 5439f1cd857SDudley Du 5449f1cd857SDudley Du /* 0 on driver initialize and detected successfully, negative on failure. */ 5459f1cd857SDudley Du static int cyapa_initialize(struct cyapa *cyapa) 5469f1cd857SDudley Du { 5479f1cd857SDudley Du int error = 0; 5489f1cd857SDudley Du 5499f1cd857SDudley Du cyapa->state = CYAPA_STATE_NO_DEVICE; 5509f1cd857SDudley Du cyapa->gen = CYAPA_GEN_UNKNOWN; 5519f1cd857SDudley Du mutex_init(&cyapa->state_sync_lock); 5529f1cd857SDudley Du 5539f1cd857SDudley Du /* 5549f1cd857SDudley Du * Set to hard code default, they will be updated with trackpad set 5559f1cd857SDudley Du * default values after probe and initialized. 5569f1cd857SDudley Du */ 5579f1cd857SDudley Du cyapa->suspend_power_mode = PWR_MODE_SLEEP; 5589f1cd857SDudley Du cyapa->suspend_sleep_time = 5599f1cd857SDudley Du cyapa_pwr_cmd_to_sleep_time(cyapa->suspend_power_mode); 5609f1cd857SDudley Du 5619f1cd857SDudley Du /* ops.initialize() is aimed to prepare for module communications. */ 5629f1cd857SDudley Du error = cyapa_gen3_ops.initialize(cyapa); 5636972a859SDudley Du if (!error) 5646972a859SDudley Du error = cyapa_gen5_ops.initialize(cyapa); 5659f1cd857SDudley Du if (error) 5669f1cd857SDudley Du return error; 5679f1cd857SDudley Du 5689f1cd857SDudley Du error = cyapa_detect(cyapa); 5699f1cd857SDudley Du if (error) 5709f1cd857SDudley Du return error; 5719f1cd857SDudley Du 5729f1cd857SDudley Du /* Power down the device until we need it. */ 5739f1cd857SDudley Du if (cyapa->operational) 5749f1cd857SDudley Du cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); 5759f1cd857SDudley Du 5769f1cd857SDudley Du return 0; 5779f1cd857SDudley Du } 5789f1cd857SDudley Du 5799f1cd857SDudley Du static int cyapa_reinitialize(struct cyapa *cyapa) 5809f1cd857SDudley Du { 5819f1cd857SDudley Du struct device *dev = &cyapa->client->dev; 5829f1cd857SDudley Du struct input_dev *input = cyapa->input; 5839f1cd857SDudley Du int error; 5849f1cd857SDudley Du 58567286508SDudley Du if (pm_runtime_enabled(dev)) 58667286508SDudley Du pm_runtime_disable(dev); 58767286508SDudley Du 5889f1cd857SDudley Du /* Avoid command failures when TP was in OFF state. */ 5899f1cd857SDudley Du if (cyapa->operational) 5909f1cd857SDudley Du cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0); 5919f1cd857SDudley Du 5929f1cd857SDudley Du error = cyapa_detect(cyapa); 5939f1cd857SDudley Du if (error) 5949f1cd857SDudley Du goto out; 5959f1cd857SDudley Du 5969f1cd857SDudley Du if (!input && cyapa->operational) { 5979f1cd857SDudley Du error = cyapa_create_input_dev(cyapa); 5989f1cd857SDudley Du if (error) { 5999f1cd857SDudley Du dev_err(dev, "create input_dev instance failed: %d\n", 6009f1cd857SDudley Du error); 6019f1cd857SDudley Du goto out; 6029f1cd857SDudley Du } 6039f1cd857SDudley Du } 6049f1cd857SDudley Du 6059f1cd857SDudley Du out: 6069f1cd857SDudley Du if (!input || !input->users) { 6079f1cd857SDudley Du /* Reset to power OFF state to save power when no user open. */ 6089f1cd857SDudley Du if (cyapa->operational) 6099f1cd857SDudley Du cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); 61067286508SDudley Du } else if (!error && cyapa->operational) { 61167286508SDudley Du /* 61267286508SDudley Du * Make sure only enable runtime PM when device is 61367286508SDudley Du * in operational mode and input->users > 0. 61467286508SDudley Du */ 61567286508SDudley Du pm_runtime_set_active(dev); 61667286508SDudley Du pm_runtime_enable(dev); 6179f1cd857SDudley Du } 6189f1cd857SDudley Du 6199f1cd857SDudley Du return error; 6209f1cd857SDudley Du } 6219f1cd857SDudley Du 6229f1cd857SDudley Du static irqreturn_t cyapa_irq(int irq, void *dev_id) 6239f1cd857SDudley Du { 6249f1cd857SDudley Du struct cyapa *cyapa = dev_id; 6259f1cd857SDudley Du struct device *dev = &cyapa->client->dev; 6269f1cd857SDudley Du 62767286508SDudley Du pm_runtime_get_sync(dev); 6289f1cd857SDudley Du if (device_may_wakeup(dev)) 6299f1cd857SDudley Du pm_wakeup_event(dev, 0); 6309f1cd857SDudley Du 6319f1cd857SDudley Du /* Interrupt event maybe cuased by host command to trackpad device. */ 6329f1cd857SDudley Du if (cyapa->ops->irq_cmd_handler(cyapa)) { 6339f1cd857SDudley Du /* 6349f1cd857SDudley Du * Interrupt event maybe from trackpad device input reporting. 6359f1cd857SDudley Du */ 6369f1cd857SDudley Du if (!cyapa->input) { 6379f1cd857SDudley Du /* 6389f1cd857SDudley Du * Still in probling or in firware image 6399f1cd857SDudley Du * udpating or reading. 6409f1cd857SDudley Du */ 6419f1cd857SDudley Du cyapa->ops->sort_empty_output_data(cyapa, 6429f1cd857SDudley Du NULL, NULL, NULL); 6439f1cd857SDudley Du goto out; 6449f1cd857SDudley Du } 6459f1cd857SDudley Du 6469f1cd857SDudley Du if (!cyapa->operational || cyapa->ops->irq_handler(cyapa)) { 6479f1cd857SDudley Du if (!mutex_trylock(&cyapa->state_sync_lock)) { 6489f1cd857SDudley Du cyapa->ops->sort_empty_output_data(cyapa, 6499f1cd857SDudley Du NULL, NULL, NULL); 6509f1cd857SDudley Du goto out; 6519f1cd857SDudley Du } 6529f1cd857SDudley Du cyapa_reinitialize(cyapa); 6539f1cd857SDudley Du mutex_unlock(&cyapa->state_sync_lock); 6549f1cd857SDudley Du } 6559f1cd857SDudley Du } 6569f1cd857SDudley Du 6579f1cd857SDudley Du out: 65867286508SDudley Du pm_runtime_mark_last_busy(dev); 65967286508SDudley Du pm_runtime_put_sync_autosuspend(dev); 6609f1cd857SDudley Du return IRQ_HANDLED; 6619f1cd857SDudley Du } 6629f1cd857SDudley Du 66322e7db81SDudley Du /* 66422e7db81SDudley Du ************************************************************** 66522e7db81SDudley Du * sysfs interface 66622e7db81SDudley Du ************************************************************** 66722e7db81SDudley Du */ 66822e7db81SDudley Du #ifdef CONFIG_PM_SLEEP 66922e7db81SDudley Du static ssize_t cyapa_show_suspend_scanrate(struct device *dev, 67022e7db81SDudley Du struct device_attribute *attr, 67122e7db81SDudley Du char *buf) 67222e7db81SDudley Du { 67322e7db81SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 67422e7db81SDudley Du u8 pwr_cmd = cyapa->suspend_power_mode; 67522e7db81SDudley Du u16 sleep_time; 67622e7db81SDudley Du int len; 67722e7db81SDudley Du int error; 67822e7db81SDudley Du 67922e7db81SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 68022e7db81SDudley Du if (error) 68122e7db81SDudley Du return error; 68222e7db81SDudley Du 68322e7db81SDudley Du pwr_cmd = cyapa->suspend_power_mode; 68422e7db81SDudley Du sleep_time = cyapa->suspend_sleep_time; 68522e7db81SDudley Du 68622e7db81SDudley Du mutex_unlock(&cyapa->state_sync_lock); 68722e7db81SDudley Du 68822e7db81SDudley Du switch (pwr_cmd) { 68922e7db81SDudley Du case PWR_MODE_BTN_ONLY: 69022e7db81SDudley Du len = scnprintf(buf, PAGE_SIZE, "%s\n", BTN_ONLY_MODE_NAME); 69122e7db81SDudley Du break; 69222e7db81SDudley Du 69322e7db81SDudley Du case PWR_MODE_OFF: 69422e7db81SDudley Du len = scnprintf(buf, PAGE_SIZE, "%s\n", OFF_MODE_NAME); 69522e7db81SDudley Du break; 69622e7db81SDudley Du 69722e7db81SDudley Du default: 69822e7db81SDudley Du len = scnprintf(buf, PAGE_SIZE, "%u\n", 69922e7db81SDudley Du cyapa->gen == CYAPA_GEN3 ? 70022e7db81SDudley Du cyapa_pwr_cmd_to_sleep_time(pwr_cmd) : 70122e7db81SDudley Du sleep_time); 70222e7db81SDudley Du break; 70322e7db81SDudley Du } 70422e7db81SDudley Du 70522e7db81SDudley Du return len; 70622e7db81SDudley Du } 70722e7db81SDudley Du 70822e7db81SDudley Du static ssize_t cyapa_update_suspend_scanrate(struct device *dev, 70922e7db81SDudley Du struct device_attribute *attr, 71022e7db81SDudley Du const char *buf, size_t count) 71122e7db81SDudley Du { 71222e7db81SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 71322e7db81SDudley Du u16 sleep_time; 71422e7db81SDudley Du int error; 71522e7db81SDudley Du 71622e7db81SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 71722e7db81SDudley Du if (error) 71822e7db81SDudley Du return error; 71922e7db81SDudley Du 72022e7db81SDudley Du if (sysfs_streq(buf, BTN_ONLY_MODE_NAME)) { 72122e7db81SDudley Du cyapa->suspend_power_mode = PWR_MODE_BTN_ONLY; 72222e7db81SDudley Du } else if (sysfs_streq(buf, OFF_MODE_NAME)) { 72322e7db81SDudley Du cyapa->suspend_power_mode = PWR_MODE_OFF; 72422e7db81SDudley Du } else if (!kstrtou16(buf, 10, &sleep_time)) { 72522e7db81SDudley Du cyapa->suspend_sleep_time = max_t(u16, sleep_time, 1000); 72622e7db81SDudley Du cyapa->suspend_power_mode = 72722e7db81SDudley Du cyapa_sleep_time_to_pwr_cmd(cyapa->suspend_sleep_time); 72822e7db81SDudley Du } else { 72922e7db81SDudley Du count = -EINVAL; 73022e7db81SDudley Du } 73122e7db81SDudley Du 73222e7db81SDudley Du mutex_unlock(&cyapa->state_sync_lock); 73322e7db81SDudley Du 73422e7db81SDudley Du return count; 73522e7db81SDudley Du } 73622e7db81SDudley Du 73722e7db81SDudley Du static DEVICE_ATTR(suspend_scanrate_ms, S_IRUGO|S_IWUSR, 73822e7db81SDudley Du cyapa_show_suspend_scanrate, 73922e7db81SDudley Du cyapa_update_suspend_scanrate); 74022e7db81SDudley Du 74122e7db81SDudley Du static struct attribute *cyapa_power_wakeup_entries[] = { 74222e7db81SDudley Du &dev_attr_suspend_scanrate_ms.attr, 74322e7db81SDudley Du NULL, 74422e7db81SDudley Du }; 74522e7db81SDudley Du 74622e7db81SDudley Du static const struct attribute_group cyapa_power_wakeup_group = { 74722e7db81SDudley Du .name = power_group_name, 74822e7db81SDudley Du .attrs = cyapa_power_wakeup_entries, 74922e7db81SDudley Du }; 75022e7db81SDudley Du 75122e7db81SDudley Du static void cyapa_remove_power_wakeup_group(void *data) 75222e7db81SDudley Du { 75322e7db81SDudley Du struct cyapa *cyapa = data; 75422e7db81SDudley Du 75522e7db81SDudley Du sysfs_unmerge_group(&cyapa->client->dev.kobj, 75622e7db81SDudley Du &cyapa_power_wakeup_group); 75722e7db81SDudley Du } 75822e7db81SDudley Du 75922e7db81SDudley Du static int cyapa_prepare_wakeup_controls(struct cyapa *cyapa) 76022e7db81SDudley Du { 76122e7db81SDudley Du struct i2c_client *client = cyapa->client; 76222e7db81SDudley Du struct device *dev = &client->dev; 76322e7db81SDudley Du int error; 76422e7db81SDudley Du 76522e7db81SDudley Du if (device_can_wakeup(dev)) { 76622e7db81SDudley Du error = sysfs_merge_group(&client->dev.kobj, 76722e7db81SDudley Du &cyapa_power_wakeup_group); 76822e7db81SDudley Du if (error) { 76922e7db81SDudley Du dev_err(dev, "failed to add power wakeup group: %d\n", 77022e7db81SDudley Du error); 77122e7db81SDudley Du return error; 77222e7db81SDudley Du } 77322e7db81SDudley Du 77422e7db81SDudley Du error = devm_add_action(dev, 77522e7db81SDudley Du cyapa_remove_power_wakeup_group, cyapa); 77622e7db81SDudley Du if (error) { 77722e7db81SDudley Du cyapa_remove_power_wakeup_group(cyapa); 77822e7db81SDudley Du dev_err(dev, "failed to add power cleanup action: %d\n", 77922e7db81SDudley Du error); 78022e7db81SDudley Du return error; 78122e7db81SDudley Du } 78222e7db81SDudley Du } 78322e7db81SDudley Du 78422e7db81SDudley Du return 0; 78522e7db81SDudley Du } 78622e7db81SDudley Du #else 78722e7db81SDudley Du static inline int cyapa_prepare_wakeup_controls(struct cyapa *cyapa) 78822e7db81SDudley Du { 78922e7db81SDudley Du return 0; 79022e7db81SDudley Du } 79122e7db81SDudley Du #endif /* CONFIG_PM_SLEEP */ 79222e7db81SDudley Du 79367286508SDudley Du #ifdef CONFIG_PM 79467286508SDudley Du static ssize_t cyapa_show_rt_suspend_scanrate(struct device *dev, 79567286508SDudley Du struct device_attribute *attr, 79667286508SDudley Du char *buf) 79767286508SDudley Du { 79867286508SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 79967286508SDudley Du u8 pwr_cmd; 80067286508SDudley Du u16 sleep_time; 80167286508SDudley Du int error; 80267286508SDudley Du 80367286508SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 80467286508SDudley Du if (error) 80567286508SDudley Du return error; 80667286508SDudley Du 80767286508SDudley Du pwr_cmd = cyapa->runtime_suspend_power_mode; 80867286508SDudley Du sleep_time = cyapa->runtime_suspend_sleep_time; 80967286508SDudley Du 81067286508SDudley Du mutex_unlock(&cyapa->state_sync_lock); 81167286508SDudley Du 81267286508SDudley Du return scnprintf(buf, PAGE_SIZE, "%u\n", 81367286508SDudley Du cyapa->gen == CYAPA_GEN3 ? 81467286508SDudley Du cyapa_pwr_cmd_to_sleep_time(pwr_cmd) : 81567286508SDudley Du sleep_time); 81667286508SDudley Du } 81767286508SDudley Du 81867286508SDudley Du static ssize_t cyapa_update_rt_suspend_scanrate(struct device *dev, 81967286508SDudley Du struct device_attribute *attr, 82067286508SDudley Du const char *buf, size_t count) 82167286508SDudley Du { 82267286508SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 82367286508SDudley Du u16 time; 82467286508SDudley Du int error; 82567286508SDudley Du 82667286508SDudley Du if (buf == NULL || count == 0 || kstrtou16(buf, 10, &time)) { 82767286508SDudley Du dev_err(dev, "invalid runtime suspend scanrate ms parameter\n"); 82867286508SDudley Du return -EINVAL; 82967286508SDudley Du } 83067286508SDudley Du 83167286508SDudley Du /* 83267286508SDudley Du * When the suspend scanrate is changed, pm_runtime_get to resume 83367286508SDudley Du * a potentially suspended device, update to the new pwr_cmd 83467286508SDudley Du * and then pm_runtime_put to suspend into the new power mode. 83567286508SDudley Du */ 83667286508SDudley Du pm_runtime_get_sync(dev); 83767286508SDudley Du 83867286508SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 83967286508SDudley Du if (error) 84067286508SDudley Du return error; 84167286508SDudley Du 84267286508SDudley Du cyapa->runtime_suspend_sleep_time = max_t(u16, time, 1000); 84367286508SDudley Du cyapa->runtime_suspend_power_mode = 84467286508SDudley Du cyapa_sleep_time_to_pwr_cmd(cyapa->runtime_suspend_sleep_time); 84567286508SDudley Du 84667286508SDudley Du mutex_unlock(&cyapa->state_sync_lock); 84767286508SDudley Du 84867286508SDudley Du pm_runtime_put_sync_autosuspend(dev); 84967286508SDudley Du 85067286508SDudley Du return count; 85167286508SDudley Du } 85267286508SDudley Du 85367286508SDudley Du static DEVICE_ATTR(runtime_suspend_scanrate_ms, S_IRUGO|S_IWUSR, 85467286508SDudley Du cyapa_show_rt_suspend_scanrate, 85567286508SDudley Du cyapa_update_rt_suspend_scanrate); 85667286508SDudley Du 85767286508SDudley Du static struct attribute *cyapa_power_runtime_entries[] = { 85867286508SDudley Du &dev_attr_runtime_suspend_scanrate_ms.attr, 85967286508SDudley Du NULL, 86067286508SDudley Du }; 86167286508SDudley Du 86267286508SDudley Du static const struct attribute_group cyapa_power_runtime_group = { 86367286508SDudley Du .name = power_group_name, 86467286508SDudley Du .attrs = cyapa_power_runtime_entries, 86567286508SDudley Du }; 86667286508SDudley Du 86767286508SDudley Du static void cyapa_remove_power_runtime_group(void *data) 86867286508SDudley Du { 86967286508SDudley Du struct cyapa *cyapa = data; 87067286508SDudley Du 87167286508SDudley Du sysfs_unmerge_group(&cyapa->client->dev.kobj, 87267286508SDudley Du &cyapa_power_runtime_group); 87367286508SDudley Du } 87467286508SDudley Du 87567286508SDudley Du static int cyapa_start_runtime(struct cyapa *cyapa) 87667286508SDudley Du { 87767286508SDudley Du struct device *dev = &cyapa->client->dev; 87867286508SDudley Du int error; 87967286508SDudley Du 88067286508SDudley Du cyapa->runtime_suspend_power_mode = PWR_MODE_IDLE; 88167286508SDudley Du cyapa->runtime_suspend_sleep_time = 88267286508SDudley Du cyapa_pwr_cmd_to_sleep_time(cyapa->runtime_suspend_power_mode); 88367286508SDudley Du 88467286508SDudley Du error = sysfs_merge_group(&dev->kobj, &cyapa_power_runtime_group); 88567286508SDudley Du if (error) { 88667286508SDudley Du dev_err(dev, 88767286508SDudley Du "failed to create power runtime group: %d\n", error); 88867286508SDudley Du return error; 88967286508SDudley Du } 89067286508SDudley Du 89167286508SDudley Du error = devm_add_action(dev, cyapa_remove_power_runtime_group, cyapa); 89267286508SDudley Du if (error) { 89367286508SDudley Du cyapa_remove_power_runtime_group(cyapa); 89467286508SDudley Du dev_err(dev, 89567286508SDudley Du "failed to add power runtime cleanup action: %d\n", 89667286508SDudley Du error); 89767286508SDudley Du return error; 89867286508SDudley Du } 89967286508SDudley Du 90067286508SDudley Du /* runtime is enabled until device is operational and opened. */ 90167286508SDudley Du pm_runtime_set_suspended(dev); 90267286508SDudley Du pm_runtime_use_autosuspend(dev); 90367286508SDudley Du pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY); 90467286508SDudley Du 90567286508SDudley Du return 0; 90667286508SDudley Du } 90767286508SDudley Du #else 90867286508SDudley Du static inline int cyapa_start_runtime(struct cyapa *cyapa) 90967286508SDudley Du { 91067286508SDudley Du return 0; 91167286508SDudley Du } 91267286508SDudley Du #endif /* CONFIG_PM */ 91367286508SDudley Du 914*c806b0b8SDudley Du static ssize_t cyapa_show_fm_ver(struct device *dev, 915*c806b0b8SDudley Du struct device_attribute *attr, char *buf) 916*c806b0b8SDudley Du { 917*c806b0b8SDudley Du int error; 918*c806b0b8SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 919*c806b0b8SDudley Du 920*c806b0b8SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 921*c806b0b8SDudley Du if (error) 922*c806b0b8SDudley Du return error; 923*c806b0b8SDudley Du error = scnprintf(buf, PAGE_SIZE, "%d.%d\n", cyapa->fw_maj_ver, 924*c806b0b8SDudley Du cyapa->fw_min_ver); 925*c806b0b8SDudley Du mutex_unlock(&cyapa->state_sync_lock); 926*c806b0b8SDudley Du return error; 927*c806b0b8SDudley Du } 928*c806b0b8SDudley Du 929*c806b0b8SDudley Du static ssize_t cyapa_show_product_id(struct device *dev, 930*c806b0b8SDudley Du struct device_attribute *attr, char *buf) 931*c806b0b8SDudley Du { 932*c806b0b8SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 933*c806b0b8SDudley Du int size; 934*c806b0b8SDudley Du int error; 935*c806b0b8SDudley Du 936*c806b0b8SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 937*c806b0b8SDudley Du if (error) 938*c806b0b8SDudley Du return error; 939*c806b0b8SDudley Du size = scnprintf(buf, PAGE_SIZE, "%s\n", cyapa->product_id); 940*c806b0b8SDudley Du mutex_unlock(&cyapa->state_sync_lock); 941*c806b0b8SDudley Du return size; 942*c806b0b8SDudley Du } 943*c806b0b8SDudley Du 944*c806b0b8SDudley Du static int cyapa_firmware(struct cyapa *cyapa, const char *fw_name) 945*c806b0b8SDudley Du { 946*c806b0b8SDudley Du struct device *dev = &cyapa->client->dev; 947*c806b0b8SDudley Du const struct firmware *fw; 948*c806b0b8SDudley Du int error; 949*c806b0b8SDudley Du 950*c806b0b8SDudley Du error = request_firmware(&fw, fw_name, dev); 951*c806b0b8SDudley Du if (error) { 952*c806b0b8SDudley Du dev_err(dev, "Could not load firmware from %s: %d\n", 953*c806b0b8SDudley Du fw_name, error); 954*c806b0b8SDudley Du return error; 955*c806b0b8SDudley Du } 956*c806b0b8SDudley Du 957*c806b0b8SDudley Du error = cyapa->ops->check_fw(cyapa, fw); 958*c806b0b8SDudley Du if (error) { 959*c806b0b8SDudley Du dev_err(dev, "Invalid CYAPA firmware image: %s\n", 960*c806b0b8SDudley Du fw_name); 961*c806b0b8SDudley Du goto done; 962*c806b0b8SDudley Du } 963*c806b0b8SDudley Du 964*c806b0b8SDudley Du /* 965*c806b0b8SDudley Du * Resume the potentially suspended device because doing FW 966*c806b0b8SDudley Du * update on a device not in the FULL mode has a chance to 967*c806b0b8SDudley Du * fail. 968*c806b0b8SDudley Du */ 969*c806b0b8SDudley Du pm_runtime_get_sync(dev); 970*c806b0b8SDudley Du 971*c806b0b8SDudley Du /* Require IRQ support for firmware update commands. */ 972*c806b0b8SDudley Du cyapa_enable_irq_for_cmd(cyapa); 973*c806b0b8SDudley Du 974*c806b0b8SDudley Du error = cyapa->ops->bl_enter(cyapa); 975*c806b0b8SDudley Du if (error) { 976*c806b0b8SDudley Du dev_err(dev, "bl_enter failed, %d\n", error); 977*c806b0b8SDudley Du goto err_detect; 978*c806b0b8SDudley Du } 979*c806b0b8SDudley Du 980*c806b0b8SDudley Du error = cyapa->ops->bl_activate(cyapa); 981*c806b0b8SDudley Du if (error) { 982*c806b0b8SDudley Du dev_err(dev, "bl_activate failed, %d\n", error); 983*c806b0b8SDudley Du goto err_detect; 984*c806b0b8SDudley Du } 985*c806b0b8SDudley Du 986*c806b0b8SDudley Du error = cyapa->ops->bl_initiate(cyapa, fw); 987*c806b0b8SDudley Du if (error) { 988*c806b0b8SDudley Du dev_err(dev, "bl_initiate failed, %d\n", error); 989*c806b0b8SDudley Du goto err_detect; 990*c806b0b8SDudley Du } 991*c806b0b8SDudley Du 992*c806b0b8SDudley Du error = cyapa->ops->update_fw(cyapa, fw); 993*c806b0b8SDudley Du if (error) { 994*c806b0b8SDudley Du dev_err(dev, "update_fw failed, %d\n", error); 995*c806b0b8SDudley Du goto err_detect; 996*c806b0b8SDudley Du } 997*c806b0b8SDudley Du 998*c806b0b8SDudley Du err_detect: 999*c806b0b8SDudley Du cyapa_disable_irq_for_cmd(cyapa); 1000*c806b0b8SDudley Du pm_runtime_put_noidle(dev); 1001*c806b0b8SDudley Du 1002*c806b0b8SDudley Du done: 1003*c806b0b8SDudley Du release_firmware(fw); 1004*c806b0b8SDudley Du return error; 1005*c806b0b8SDudley Du } 1006*c806b0b8SDudley Du 1007*c806b0b8SDudley Du static ssize_t cyapa_update_fw_store(struct device *dev, 1008*c806b0b8SDudley Du struct device_attribute *attr, 1009*c806b0b8SDudley Du const char *buf, size_t count) 1010*c806b0b8SDudley Du { 1011*c806b0b8SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 1012*c806b0b8SDudley Du char fw_name[NAME_MAX]; 1013*c806b0b8SDudley Du int ret, error; 1014*c806b0b8SDudley Du 1015*c806b0b8SDudley Du if (count > NAME_MAX) { 1016*c806b0b8SDudley Du dev_err(dev, "File name too long\n"); 1017*c806b0b8SDudley Du return -EINVAL; 1018*c806b0b8SDudley Du } 1019*c806b0b8SDudley Du 1020*c806b0b8SDudley Du memcpy(fw_name, buf, count); 1021*c806b0b8SDudley Du if (fw_name[count - 1] == '\n') 1022*c806b0b8SDudley Du fw_name[count - 1] = '\0'; 1023*c806b0b8SDudley Du else 1024*c806b0b8SDudley Du fw_name[count] = '\0'; 1025*c806b0b8SDudley Du 1026*c806b0b8SDudley Du if (cyapa->input) { 1027*c806b0b8SDudley Du /* 1028*c806b0b8SDudley Du * Force the input device to be registered after the firmware 1029*c806b0b8SDudley Du * image is updated, so if the corresponding parameters updated 1030*c806b0b8SDudley Du * in the new firmware image can taken effect immediately. 1031*c806b0b8SDudley Du */ 1032*c806b0b8SDudley Du input_unregister_device(cyapa->input); 1033*c806b0b8SDudley Du cyapa->input = NULL; 1034*c806b0b8SDudley Du } 1035*c806b0b8SDudley Du 1036*c806b0b8SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 1037*c806b0b8SDudley Du if (error) { 1038*c806b0b8SDudley Du /* 1039*c806b0b8SDudley Du * Whatever, do reinitialize to try to recover TP state to 1040*c806b0b8SDudley Du * previous state just as it entered fw update entrance. 1041*c806b0b8SDudley Du */ 1042*c806b0b8SDudley Du cyapa_reinitialize(cyapa); 1043*c806b0b8SDudley Du return error; 1044*c806b0b8SDudley Du } 1045*c806b0b8SDudley Du 1046*c806b0b8SDudley Du error = cyapa_firmware(cyapa, fw_name); 1047*c806b0b8SDudley Du if (error) 1048*c806b0b8SDudley Du dev_err(dev, "firmware update failed: %d\n", error); 1049*c806b0b8SDudley Du else 1050*c806b0b8SDudley Du dev_dbg(dev, "firmware update successfully done.\n"); 1051*c806b0b8SDudley Du 1052*c806b0b8SDudley Du /* 1053*c806b0b8SDudley Du * Redetect trackpad device states because firmware update process 1054*c806b0b8SDudley Du * will reset trackpad device into bootloader mode. 1055*c806b0b8SDudley Du */ 1056*c806b0b8SDudley Du ret = cyapa_reinitialize(cyapa); 1057*c806b0b8SDudley Du if (ret) { 1058*c806b0b8SDudley Du dev_err(dev, "failed to redetect after updated: %d\n", ret); 1059*c806b0b8SDudley Du error = error ? error : ret; 1060*c806b0b8SDudley Du } 1061*c806b0b8SDudley Du 1062*c806b0b8SDudley Du mutex_unlock(&cyapa->state_sync_lock); 1063*c806b0b8SDudley Du 1064*c806b0b8SDudley Du return error ? error : count; 1065*c806b0b8SDudley Du } 1066*c806b0b8SDudley Du 1067*c806b0b8SDudley Du static ssize_t cyapa_calibrate_store(struct device *dev, 1068*c806b0b8SDudley Du struct device_attribute *attr, 1069*c806b0b8SDudley Du const char *buf, size_t count) 1070*c806b0b8SDudley Du { 1071*c806b0b8SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 1072*c806b0b8SDudley Du int error; 1073*c806b0b8SDudley Du 1074*c806b0b8SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 1075*c806b0b8SDudley Du if (error) 1076*c806b0b8SDudley Du return error; 1077*c806b0b8SDudley Du 1078*c806b0b8SDudley Du if (cyapa->operational) { 1079*c806b0b8SDudley Du cyapa_enable_irq_for_cmd(cyapa); 1080*c806b0b8SDudley Du error = cyapa->ops->calibrate_store(dev, attr, buf, count); 1081*c806b0b8SDudley Du cyapa_disable_irq_for_cmd(cyapa); 1082*c806b0b8SDudley Du } else { 1083*c806b0b8SDudley Du error = -EBUSY; /* Still running in bootloader mode. */ 1084*c806b0b8SDudley Du } 1085*c806b0b8SDudley Du 1086*c806b0b8SDudley Du mutex_unlock(&cyapa->state_sync_lock); 1087*c806b0b8SDudley Du return error < 0 ? error : count; 1088*c806b0b8SDudley Du } 1089*c806b0b8SDudley Du 1090*c806b0b8SDudley Du static ssize_t cyapa_show_baseline(struct device *dev, 1091*c806b0b8SDudley Du struct device_attribute *attr, char *buf) 1092*c806b0b8SDudley Du { 1093*c806b0b8SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 1094*c806b0b8SDudley Du ssize_t error; 1095*c806b0b8SDudley Du 1096*c806b0b8SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 1097*c806b0b8SDudley Du if (error) 1098*c806b0b8SDudley Du return error; 1099*c806b0b8SDudley Du 1100*c806b0b8SDudley Du if (cyapa->operational) { 1101*c806b0b8SDudley Du cyapa_enable_irq_for_cmd(cyapa); 1102*c806b0b8SDudley Du error = cyapa->ops->show_baseline(dev, attr, buf); 1103*c806b0b8SDudley Du cyapa_disable_irq_for_cmd(cyapa); 1104*c806b0b8SDudley Du } else { 1105*c806b0b8SDudley Du error = -EBUSY; /* Still running in bootloader mode. */ 1106*c806b0b8SDudley Du } 1107*c806b0b8SDudley Du 1108*c806b0b8SDudley Du mutex_unlock(&cyapa->state_sync_lock); 1109*c806b0b8SDudley Du return error; 1110*c806b0b8SDudley Du } 1111*c806b0b8SDudley Du 1112*c806b0b8SDudley Du static char *cyapa_state_to_string(struct cyapa *cyapa) 1113*c806b0b8SDudley Du { 1114*c806b0b8SDudley Du switch (cyapa->state) { 1115*c806b0b8SDudley Du case CYAPA_STATE_BL_BUSY: 1116*c806b0b8SDudley Du return "bootloader busy"; 1117*c806b0b8SDudley Du case CYAPA_STATE_BL_IDLE: 1118*c806b0b8SDudley Du return "bootloader idle"; 1119*c806b0b8SDudley Du case CYAPA_STATE_BL_ACTIVE: 1120*c806b0b8SDudley Du return "bootloader active"; 1121*c806b0b8SDudley Du case CYAPA_STATE_GEN5_BL: 1122*c806b0b8SDudley Du return "bootloader"; 1123*c806b0b8SDudley Du case CYAPA_STATE_OP: 1124*c806b0b8SDudley Du case CYAPA_STATE_GEN5_APP: 1125*c806b0b8SDudley Du return "operational"; /* Normal valid state. */ 1126*c806b0b8SDudley Du default: 1127*c806b0b8SDudley Du return "invalid mode"; 1128*c806b0b8SDudley Du } 1129*c806b0b8SDudley Du } 1130*c806b0b8SDudley Du 1131*c806b0b8SDudley Du static ssize_t cyapa_show_mode(struct device *dev, 1132*c806b0b8SDudley Du struct device_attribute *attr, char *buf) 1133*c806b0b8SDudley Du { 1134*c806b0b8SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 1135*c806b0b8SDudley Du int size; 1136*c806b0b8SDudley Du int error; 1137*c806b0b8SDudley Du 1138*c806b0b8SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 1139*c806b0b8SDudley Du if (error) 1140*c806b0b8SDudley Du return error; 1141*c806b0b8SDudley Du 1142*c806b0b8SDudley Du size = scnprintf(buf, PAGE_SIZE, "gen%d %s\n", 1143*c806b0b8SDudley Du cyapa->gen, cyapa_state_to_string(cyapa)); 1144*c806b0b8SDudley Du 1145*c806b0b8SDudley Du mutex_unlock(&cyapa->state_sync_lock); 1146*c806b0b8SDudley Du return size; 1147*c806b0b8SDudley Du } 1148*c806b0b8SDudley Du 1149*c806b0b8SDudley Du static DEVICE_ATTR(firmware_version, S_IRUGO, cyapa_show_fm_ver, NULL); 1150*c806b0b8SDudley Du static DEVICE_ATTR(product_id, S_IRUGO, cyapa_show_product_id, NULL); 1151*c806b0b8SDudley Du static DEVICE_ATTR(update_fw, S_IWUSR, NULL, cyapa_update_fw_store); 1152*c806b0b8SDudley Du static DEVICE_ATTR(baseline, S_IRUGO, cyapa_show_baseline, NULL); 1153*c806b0b8SDudley Du static DEVICE_ATTR(calibrate, S_IWUSR, NULL, cyapa_calibrate_store); 1154*c806b0b8SDudley Du static DEVICE_ATTR(mode, S_IRUGO, cyapa_show_mode, NULL); 1155*c806b0b8SDudley Du 1156*c806b0b8SDudley Du static struct attribute *cyapa_sysfs_entries[] = { 1157*c806b0b8SDudley Du &dev_attr_firmware_version.attr, 1158*c806b0b8SDudley Du &dev_attr_product_id.attr, 1159*c806b0b8SDudley Du &dev_attr_update_fw.attr, 1160*c806b0b8SDudley Du &dev_attr_baseline.attr, 1161*c806b0b8SDudley Du &dev_attr_calibrate.attr, 1162*c806b0b8SDudley Du &dev_attr_mode.attr, 1163*c806b0b8SDudley Du NULL, 1164*c806b0b8SDudley Du }; 1165*c806b0b8SDudley Du 1166*c806b0b8SDudley Du static const struct attribute_group cyapa_sysfs_group = { 1167*c806b0b8SDudley Du .attrs = cyapa_sysfs_entries, 1168*c806b0b8SDudley Du }; 1169*c806b0b8SDudley Du 1170*c806b0b8SDudley Du static void cyapa_remove_sysfs_group(void *data) 1171*c806b0b8SDudley Du { 1172*c806b0b8SDudley Du struct cyapa *cyapa = data; 1173*c806b0b8SDudley Du 1174*c806b0b8SDudley Du sysfs_remove_group(&cyapa->client->dev.kobj, &cyapa_sysfs_group); 1175*c806b0b8SDudley Du } 1176*c806b0b8SDudley Du 1177d7e34d12SBenson Leung static int cyapa_probe(struct i2c_client *client, 1178d7e34d12SBenson Leung const struct i2c_device_id *dev_id) 1179d7e34d12SBenson Leung { 1180d7e34d12SBenson Leung struct device *dev = &client->dev; 1181b1cfa7b4SDudley Du struct cyapa *cyapa; 1182b1cfa7b4SDudley Du u8 adapter_func; 11839f1cd857SDudley Du union i2c_smbus_data dummy; 1184b1cfa7b4SDudley Du int error; 1185d7e34d12SBenson Leung 11866ddaf744SBenson Leung adapter_func = cyapa_check_adapter_functionality(client); 11876ddaf744SBenson Leung if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) { 11886ddaf744SBenson Leung dev_err(dev, "not a supported I2C/SMBus adapter\n"); 11896ddaf744SBenson Leung return -EIO; 11906ddaf744SBenson Leung } 11916ddaf744SBenson Leung 11929f1cd857SDudley Du /* Make sure there is something at this address */ 11939f1cd857SDudley Du if (i2c_smbus_xfer(client->adapter, client->addr, 0, 11949f1cd857SDudley Du I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) 11959f1cd857SDudley Du return -ENODEV; 11969f1cd857SDudley Du 1197b1cfa7b4SDudley Du cyapa = devm_kzalloc(dev, sizeof(struct cyapa), GFP_KERNEL); 1198b1cfa7b4SDudley Du if (!cyapa) 1199d7e34d12SBenson Leung return -ENOMEM; 1200d7e34d12SBenson Leung 12016ddaf744SBenson Leung /* i2c isn't supported, use smbus */ 12026ddaf744SBenson Leung if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS) 12036ddaf744SBenson Leung cyapa->smbus = true; 1204b1cfa7b4SDudley Du 12059f1cd857SDudley Du cyapa->client = client; 12069f1cd857SDudley Du i2c_set_clientdata(client, cyapa); 12079f1cd857SDudley Du sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr, 12089f1cd857SDudley Du client->addr); 1209b1cfa7b4SDudley Du 12109f1cd857SDudley Du error = cyapa_initialize(cyapa); 1211b1cfa7b4SDudley Du if (error) { 12129f1cd857SDudley Du dev_err(dev, "failed to detect and initialize tp device.\n"); 1213b1cfa7b4SDudley Du return error; 1214d7e34d12SBenson Leung } 1215d7e34d12SBenson Leung 1216*c806b0b8SDudley Du error = sysfs_create_group(&client->dev.kobj, &cyapa_sysfs_group); 1217*c806b0b8SDudley Du if (error) { 1218*c806b0b8SDudley Du dev_err(dev, "failed to create sysfs entries: %d\n", error); 1219*c806b0b8SDudley Du return error; 1220*c806b0b8SDudley Du } 1221*c806b0b8SDudley Du 1222*c806b0b8SDudley Du error = devm_add_action(dev, cyapa_remove_sysfs_group, cyapa); 1223*c806b0b8SDudley Du if (error) { 1224*c806b0b8SDudley Du cyapa_remove_sysfs_group(cyapa); 1225*c806b0b8SDudley Du dev_err(dev, "failed to add sysfs cleanup action: %d\n", error); 1226*c806b0b8SDudley Du return error; 1227*c806b0b8SDudley Du } 1228*c806b0b8SDudley Du 122922e7db81SDudley Du error = cyapa_prepare_wakeup_controls(cyapa); 123022e7db81SDudley Du if (error) { 123122e7db81SDudley Du dev_err(dev, "failed to prepare wakeup controls: %d\n", error); 123222e7db81SDudley Du return error; 123322e7db81SDudley Du } 123422e7db81SDudley Du 123567286508SDudley Du error = cyapa_start_runtime(cyapa); 123667286508SDudley Du if (error) { 123767286508SDudley Du dev_err(dev, "failed to start pm_runtime: %d\n", error); 123867286508SDudley Du return error; 123967286508SDudley Du } 124067286508SDudley Du 1241b1cfa7b4SDudley Du error = devm_request_threaded_irq(dev, client->irq, 1242b1cfa7b4SDudley Du NULL, cyapa_irq, 1243d7e34d12SBenson Leung IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 1244b1cfa7b4SDudley Du "cyapa", cyapa); 1245b1cfa7b4SDudley Du if (error) { 1246823a11fdSDudley Du dev_err(dev, "failed to request threaded irq: %d\n", error); 1247b1cfa7b4SDudley Du return error; 1248d7e34d12SBenson Leung } 1249d7e34d12SBenson Leung 1250b1cfa7b4SDudley Du /* Disable IRQ until the device is opened */ 1251b1cfa7b4SDudley Du disable_irq(client->irq); 1252d7e34d12SBenson Leung 12539f1cd857SDudley Du /* 12549f1cd857SDudley Du * Register the device in the input subsystem when it's operational. 12559f1cd857SDudley Du * Otherwise, keep in this driver, so it can be be recovered or updated 12569f1cd857SDudley Du * through the sysfs mode and update_fw interfaces by user or apps. 12579f1cd857SDudley Du */ 12589f1cd857SDudley Du if (cyapa->operational) { 12599f1cd857SDudley Du error = cyapa_create_input_dev(cyapa); 1260b1cfa7b4SDudley Du if (error) { 12619f1cd857SDudley Du dev_err(dev, "create input_dev instance failed: %d\n", 12629f1cd857SDudley Du error); 1263b1cfa7b4SDudley Du return error; 1264d7e34d12SBenson Leung } 12659f1cd857SDudley Du } 1266d7e34d12SBenson Leung 1267d7e34d12SBenson Leung return 0; 1268d7e34d12SBenson Leung } 1269d7e34d12SBenson Leung 1270572081a4SJingoo Han static int __maybe_unused cyapa_suspend(struct device *dev) 1271d7e34d12SBenson Leung { 1272b1cfa7b4SDudley Du struct i2c_client *client = to_i2c_client(dev); 1273b1cfa7b4SDudley Du struct cyapa *cyapa = i2c_get_clientdata(client); 1274d7e34d12SBenson Leung u8 power_mode; 1275b1cfa7b4SDudley Du int error; 1276d7e34d12SBenson Leung 12779f1cd857SDudley Du error = mutex_lock_interruptible(&cyapa->state_sync_lock); 1278b1cfa7b4SDudley Du if (error) 1279b1cfa7b4SDudley Du return error; 1280b1cfa7b4SDudley Du 128167286508SDudley Du /* 128267286508SDudley Du * Runtime PM is enable only when device is in operational mode and 128367286508SDudley Du * users in use, so need check it before disable it to 128467286508SDudley Du * avoid unbalance warning. 128567286508SDudley Du */ 128667286508SDudley Du if (pm_runtime_enabled(dev)) 128767286508SDudley Du pm_runtime_disable(dev); 1288b1cfa7b4SDudley Du disable_irq(client->irq); 1289d7e34d12SBenson Leung 1290d7e34d12SBenson Leung /* 1291d7e34d12SBenson Leung * Set trackpad device to idle mode if wakeup is allowed, 1292d7e34d12SBenson Leung * otherwise turn off. 1293d7e34d12SBenson Leung */ 12949f1cd857SDudley Du if (cyapa->operational) { 12959f1cd857SDudley Du power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode 1296d7e34d12SBenson Leung : PWR_MODE_OFF; 12979f1cd857SDudley Du error = cyapa->ops->set_power_mode(cyapa, power_mode, 12989f1cd857SDudley Du cyapa->suspend_sleep_time); 1299b1cfa7b4SDudley Du if (error) 13009f1cd857SDudley Du dev_err(dev, "suspend set power mode failed: %d\n", 13019f1cd857SDudley Du error); 13029f1cd857SDudley Du } 1303d7e34d12SBenson Leung 1304d7e34d12SBenson Leung if (device_may_wakeup(dev)) 1305f68a95cdSDudley Du cyapa->irq_wake = (enable_irq_wake(client->irq) == 0); 1306b1cfa7b4SDudley Du 13079f1cd857SDudley Du mutex_unlock(&cyapa->state_sync_lock); 1308d7e34d12SBenson Leung return 0; 1309d7e34d12SBenson Leung } 1310d7e34d12SBenson Leung 1311572081a4SJingoo Han static int __maybe_unused cyapa_resume(struct device *dev) 1312d7e34d12SBenson Leung { 1313b1cfa7b4SDudley Du struct i2c_client *client = to_i2c_client(dev); 1314b1cfa7b4SDudley Du struct cyapa *cyapa = i2c_get_clientdata(client); 1315b1cfa7b4SDudley Du int error; 1316b1cfa7b4SDudley Du 13179f1cd857SDudley Du mutex_lock(&cyapa->state_sync_lock); 1318d7e34d12SBenson Leung 13199f1cd857SDudley Du if (device_may_wakeup(dev) && cyapa->irq_wake) { 1320f68a95cdSDudley Du disable_irq_wake(client->irq); 13219f1cd857SDudley Du cyapa->irq_wake = false; 13229f1cd857SDudley Du } 1323d7e34d12SBenson Leung 132467286508SDudley Du /* Update device states and runtime PM states. */ 13259f1cd857SDudley Du error = cyapa_reinitialize(cyapa); 1326b1cfa7b4SDudley Du if (error) 13279f1cd857SDudley Du dev_warn(dev, "failed to reinitialize TP device: %d\n", error); 1328d7e34d12SBenson Leung 1329f68a95cdSDudley Du enable_irq(client->irq); 1330b1cfa7b4SDudley Du 13319f1cd857SDudley Du mutex_unlock(&cyapa->state_sync_lock); 1332d7e34d12SBenson Leung return 0; 1333d7e34d12SBenson Leung } 1334d7e34d12SBenson Leung 133567286508SDudley Du static int __maybe_unused cyapa_runtime_suspend(struct device *dev) 133667286508SDudley Du { 133767286508SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 133867286508SDudley Du int error; 133967286508SDudley Du 134067286508SDudley Du error = cyapa->ops->set_power_mode(cyapa, 134167286508SDudley Du cyapa->runtime_suspend_power_mode, 134267286508SDudley Du cyapa->runtime_suspend_sleep_time); 134367286508SDudley Du if (error) 134467286508SDudley Du dev_warn(dev, "runtime suspend failed: %d\n", error); 134567286508SDudley Du 134667286508SDudley Du return 0; 134767286508SDudley Du } 134867286508SDudley Du 134967286508SDudley Du static int __maybe_unused cyapa_runtime_resume(struct device *dev) 135067286508SDudley Du { 135167286508SDudley Du struct cyapa *cyapa = dev_get_drvdata(dev); 135267286508SDudley Du int error; 135367286508SDudley Du 135467286508SDudley Du error = cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0); 135567286508SDudley Du if (error) 135667286508SDudley Du dev_warn(dev, "runtime resume failed: %d\n", error); 135767286508SDudley Du 135867286508SDudley Du return 0; 135967286508SDudley Du } 136067286508SDudley Du 136167286508SDudley Du static const struct dev_pm_ops cyapa_pm_ops = { 136267286508SDudley Du SET_SYSTEM_SLEEP_PM_OPS(cyapa_suspend, cyapa_resume) 136367286508SDudley Du SET_RUNTIME_PM_OPS(cyapa_runtime_suspend, cyapa_runtime_resume, NULL) 136467286508SDudley Du }; 1365d7e34d12SBenson Leung 1366d7e34d12SBenson Leung static const struct i2c_device_id cyapa_id_table[] = { 1367d7e34d12SBenson Leung { "cyapa", 0 }, 1368d7e34d12SBenson Leung { }, 1369d7e34d12SBenson Leung }; 1370d7e34d12SBenson Leung MODULE_DEVICE_TABLE(i2c, cyapa_id_table); 1371d7e34d12SBenson Leung 1372d7e34d12SBenson Leung static struct i2c_driver cyapa_driver = { 1373d7e34d12SBenson Leung .driver = { 1374d7e34d12SBenson Leung .name = "cyapa", 1375d7e34d12SBenson Leung .owner = THIS_MODULE, 1376d7e34d12SBenson Leung .pm = &cyapa_pm_ops, 1377d7e34d12SBenson Leung }, 1378d7e34d12SBenson Leung 1379d7e34d12SBenson Leung .probe = cyapa_probe, 1380d7e34d12SBenson Leung .id_table = cyapa_id_table, 1381d7e34d12SBenson Leung }; 1382d7e34d12SBenson Leung 1383d7e34d12SBenson Leung module_i2c_driver(cyapa_driver); 1384d7e34d12SBenson Leung 1385d7e34d12SBenson Leung MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver"); 1386d7e34d12SBenson Leung MODULE_AUTHOR("Dudley Du <dudl@cypress.com>"); 1387d7e34d12SBenson Leung MODULE_LICENSE("GPL"); 1388