xref: /linux/drivers/input/mouse/cyapa.c (revision c806b0b8)
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 = &reg,
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