cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cyapa_gen6.c (21037B)


      1/*
      2 * Cypress APA trackpad with I2C interface
      3 *
      4 * Author: Dudley Du <dudl@cypress.com>
      5 *
      6 * Copyright (C) 2015 Cypress Semiconductor, Inc.
      7 *
      8 * This file is subject to the terms and conditions of the GNU General Public
      9 * License.  See the file COPYING in the main directory of this archive for
     10 * more details.
     11 */
     12
     13#include <linux/delay.h>
     14#include <linux/i2c.h>
     15#include <linux/input.h>
     16#include <linux/input/mt.h>
     17#include <linux/mutex.h>
     18#include <linux/completion.h>
     19#include <linux/slab.h>
     20#include <asm/unaligned.h>
     21#include <linux/crc-itu-t.h>
     22#include "cyapa.h"
     23
     24
     25#define GEN6_ENABLE_CMD_IRQ	0x41
     26#define GEN6_DISABLE_CMD_IRQ	0x42
     27#define GEN6_ENABLE_DEV_IRQ	0x43
     28#define GEN6_DISABLE_DEV_IRQ	0x44
     29
     30#define GEN6_POWER_MODE_ACTIVE		0x01
     31#define GEN6_POWER_MODE_LP_MODE1	0x02
     32#define GEN6_POWER_MODE_LP_MODE2	0x03
     33#define GEN6_POWER_MODE_BTN_ONLY	0x04
     34
     35#define GEN6_SET_POWER_MODE_INTERVAL	0x47
     36#define GEN6_GET_POWER_MODE_INTERVAL	0x48
     37
     38#define GEN6_MAX_RX_NUM 14
     39#define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC	0x00
     40#define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM		0x12
     41
     42
     43struct pip_app_cmd_head {
     44	__le16 addr;
     45	__le16 length;
     46	u8 report_id;
     47	u8 resv;  /* Reserved, must be 0 */
     48	u8 cmd_code;  /* bit7: resv, set to 0; bit6~0: command code.*/
     49} __packed;
     50
     51struct pip_app_resp_head {
     52	__le16 length;
     53	u8 report_id;
     54	u8 resv;  /* Reserved, must be 0 */
     55	u8 cmd_code;  /* bit7: TGL; bit6~0: command code.*/
     56	/*
     57	 * The value of data_status can be the first byte of data or
     58	 * the command status or the unsupported command code depending on the
     59	 * requested command code.
     60	*/
     61	u8 data_status;
     62} __packed;
     63
     64struct pip_fixed_info {
     65	u8 silicon_id_high;
     66	u8 silicon_id_low;
     67	u8 family_id;
     68};
     69
     70static u8 pip_get_bl_info[] = {
     71	0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
     72	0x00, 0x00, 0x70, 0x9E, 0x17
     73};
     74
     75static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa,
     76		u8 *buf, int len)
     77{
     78	if (len != PIP_HID_DESCRIPTOR_SIZE)
     79		return false;
     80
     81	if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID ||
     82		buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
     83		return true;
     84
     85	return false;
     86}
     87
     88static int cyapa_get_pip_fixed_info(struct cyapa *cyapa,
     89		struct pip_fixed_info *pip_info, bool is_bootloader)
     90{
     91	u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
     92	int resp_len;
     93	u16 product_family;
     94	int error;
     95
     96	if (is_bootloader) {
     97		/* Read Bootloader Information to determine Gen5 or Gen6. */
     98		resp_len = sizeof(resp_data);
     99		error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
    100				pip_get_bl_info, sizeof(pip_get_bl_info),
    101				resp_data, &resp_len,
    102				2000, cyapa_sort_tsg_pip_bl_resp_data,
    103				false);
    104		if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH)
    105			return error ? error : -EIO;
    106
    107		pip_info->family_id = resp_data[8];
    108		pip_info->silicon_id_low = resp_data[10];
    109		pip_info->silicon_id_high = resp_data[11];
    110
    111		return 0;
    112	}
    113
    114	/* Get App System Information to determine Gen5 or Gen6. */
    115	resp_len = sizeof(resp_data);
    116	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
    117			pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
    118			resp_data, &resp_len,
    119			2000, cyapa_pip_sort_system_info_data, false);
    120	if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH)
    121		return error ? error : -EIO;
    122
    123	product_family = get_unaligned_le16(&resp_data[7]);
    124	if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
    125		PIP_PRODUCT_FAMILY_TRACKPAD)
    126		return -EINVAL;
    127
    128	pip_info->family_id = resp_data[19];
    129	pip_info->silicon_id_low = resp_data[21];
    130	pip_info->silicon_id_high = resp_data[22];
    131
    132	return 0;
    133
    134}
    135
    136int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
    137{
    138	u8 cmd[] = { 0x01, 0x00};
    139	struct pip_fixed_info pip_info;
    140	u8 resp_data[PIP_HID_DESCRIPTOR_SIZE];
    141	int resp_len;
    142	bool is_bootloader;
    143	int error;
    144
    145	cyapa->state = CYAPA_STATE_NO_DEVICE;
    146
    147	/* Try to wake from it deep sleep state if it is. */
    148	cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
    149
    150	/* Empty the buffer queue to get fresh data with later commands. */
    151	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
    152
    153	/*
    154	 * Read description info from trackpad device to determine running in
    155	 * APP mode or Bootloader mode.
    156	 */
    157	resp_len = PIP_HID_DESCRIPTOR_SIZE;
    158	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
    159			cmd, sizeof(cmd),
    160			resp_data, &resp_len,
    161			300,
    162			cyapa_sort_pip_hid_descriptor_data,
    163			false);
    164	if (error)
    165		return error;
    166
    167	if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
    168		is_bootloader = true;
    169	else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID)
    170		is_bootloader = false;
    171	else
    172		return -EAGAIN;
    173
    174	/* Get PIP fixed information to determine Gen5 or Gen6. */
    175	memset(&pip_info, 0, sizeof(struct pip_fixed_info));
    176	error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader);
    177	if (error)
    178		return error;
    179
    180	if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) {
    181		cyapa->gen = CYAPA_GEN6;
    182		cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL
    183					     : CYAPA_STATE_GEN6_APP;
    184	} else if (pip_info.family_id == 0x91 &&
    185		   pip_info.silicon_id_high == 0x02) {
    186		cyapa->gen = CYAPA_GEN5;
    187		cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL
    188					     : CYAPA_STATE_GEN5_APP;
    189	}
    190
    191	return 0;
    192}
    193
    194static int cyapa_gen6_read_sys_info(struct cyapa *cyapa)
    195{
    196	u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
    197	int resp_len;
    198	u16 product_family;
    199	u8 rotat_align;
    200	int error;
    201
    202	/* Get App System Information to determine Gen5 or Gen6. */
    203	resp_len = sizeof(resp_data);
    204	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
    205			pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
    206			resp_data, &resp_len,
    207			2000, cyapa_pip_sort_system_info_data, false);
    208	if (error || resp_len < sizeof(resp_data))
    209		return error ? error : -EIO;
    210
    211	product_family = get_unaligned_le16(&resp_data[7]);
    212	if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
    213		PIP_PRODUCT_FAMILY_TRACKPAD)
    214		return -EINVAL;
    215
    216	cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) &
    217			      PIP_BL_PLATFORM_VER_MASK;
    218	cyapa->fw_maj_ver = resp_data[9];
    219	cyapa->fw_min_ver = resp_data[10];
    220
    221	cyapa->electrodes_x = resp_data[33];
    222	cyapa->electrodes_y = resp_data[34];
    223
    224	cyapa->physical_size_x =  get_unaligned_le16(&resp_data[35]) / 100;
    225	cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100;
    226
    227	cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]);
    228	cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]);
    229
    230	cyapa->max_z = get_unaligned_le16(&resp_data[43]);
    231
    232	cyapa->x_origin = resp_data[45] & 0x01;
    233	cyapa->y_origin = resp_data[46] & 0x01;
    234
    235	cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
    236
    237	memcpy(&cyapa->product_id[0], &resp_data[51], 5);
    238	cyapa->product_id[5] = '-';
    239	memcpy(&cyapa->product_id[6], &resp_data[56], 6);
    240	cyapa->product_id[12] = '-';
    241	memcpy(&cyapa->product_id[13], &resp_data[62], 2);
    242	cyapa->product_id[15] = '\0';
    243
    244	/* Get the number of Rx electrodes. */
    245	rotat_align = resp_data[68];
    246	cyapa->electrodes_rx =
    247		rotat_align ? cyapa->electrodes_y : cyapa->electrodes_x;
    248	cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
    249
    250	if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
    251		!cyapa->physical_size_x || !cyapa->physical_size_y ||
    252		!cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
    253		return -EINVAL;
    254
    255	return 0;
    256}
    257
    258static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
    259{
    260	u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
    261	int resp_len;
    262	int error;
    263
    264	resp_len = sizeof(resp_data);
    265	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
    266			pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
    267			resp_data, &resp_len,
    268			500, cyapa_sort_tsg_pip_bl_resp_data, false);
    269	if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
    270		!PIP_CMD_COMPLETE_SUCCESS(resp_data))
    271		return error ? error : -EIO;
    272
    273	cyapa->fw_maj_ver = resp_data[8];
    274	cyapa->fw_min_ver = resp_data[9];
    275
    276	cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
    277			      PIP_BL_PLATFORM_VER_MASK;
    278
    279	memcpy(&cyapa->product_id[0], &resp_data[13], 5);
    280	cyapa->product_id[5] = '-';
    281	memcpy(&cyapa->product_id[6], &resp_data[18], 6);
    282	cyapa->product_id[12] = '-';
    283	memcpy(&cyapa->product_id[13], &resp_data[24], 2);
    284	cyapa->product_id[15] = '\0';
    285
    286	return 0;
    287
    288}
    289
    290static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
    291{
    292	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
    293	u8 resp_data[6];
    294	int resp_len;
    295	int error;
    296
    297	resp_len = sizeof(resp_data);
    298	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
    299			resp_data, &resp_len,
    300			500, cyapa_sort_tsg_pip_app_resp_data, false);
    301	if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
    302			!PIP_CMD_COMPLETE_SUCCESS(resp_data)
    303			)
    304		return error < 0 ? error : -EINVAL;
    305
    306	return 0;
    307}
    308
    309static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
    310{
    311	int error;
    312
    313	cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
    314	error = cyapa_pip_set_proximity(cyapa, enable);
    315	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
    316
    317	return error;
    318}
    319
    320static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
    321{
    322	u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
    323	u8 resp_data[6];
    324	int resp_len;
    325	int error;
    326
    327	resp_len = sizeof(resp_data);
    328	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
    329			resp_data, &resp_len,
    330			500, cyapa_sort_tsg_pip_app_resp_data, false);
    331	if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
    332		return error < 0 ? error : -EINVAL;
    333
    334	/* New power state applied in device not match the set power state. */
    335	if (resp_data[5] != power_mode)
    336		return -EAGAIN;
    337
    338	return 0;
    339}
    340
    341static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
    342		struct gen6_interval_setting *interval_setting)
    343{
    344	struct gen6_set_interval_cmd {
    345		__le16 addr;
    346		__le16 length;
    347		u8 report_id;
    348		u8 rsvd;  /* Reserved, must be 0 */
    349		u8 cmd_code;
    350		__le16 active_interval;
    351		__le16 lp1_interval;
    352		__le16 lp2_interval;
    353	} __packed set_interval_cmd;
    354	u8 resp_data[11];
    355	int resp_len;
    356	int error;
    357
    358	memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
    359	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
    360	put_unaligned_le16(sizeof(set_interval_cmd) - 2,
    361			   &set_interval_cmd.length);
    362	set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
    363	set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
    364	put_unaligned_le16(interval_setting->active_interval,
    365			   &set_interval_cmd.active_interval);
    366	put_unaligned_le16(interval_setting->lp1_interval,
    367			   &set_interval_cmd.lp1_interval);
    368	put_unaligned_le16(interval_setting->lp2_interval,
    369			   &set_interval_cmd.lp2_interval);
    370
    371	resp_len = sizeof(resp_data);
    372	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
    373			(u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
    374			resp_data, &resp_len,
    375			500, cyapa_sort_tsg_pip_app_resp_data, false);
    376	if (error ||
    377		!VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
    378		return error < 0 ? error : -EINVAL;
    379
    380	/* Get the real set intervals from response. */
    381	interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
    382	interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
    383	interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
    384
    385	return 0;
    386}
    387
    388static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
    389		struct gen6_interval_setting *interval_setting)
    390{
    391	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
    392		     GEN6_GET_POWER_MODE_INTERVAL };
    393	u8 resp_data[11];
    394	int resp_len;
    395	int error;
    396
    397	resp_len = sizeof(resp_data);
    398	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
    399			resp_data, &resp_len,
    400			500, cyapa_sort_tsg_pip_app_resp_data, false);
    401	if (error ||
    402		!VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
    403		return error < 0 ? error : -EINVAL;
    404
    405	interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
    406	interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
    407	interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
    408
    409	return 0;
    410}
    411
    412static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
    413{
    414	u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
    415
    416	if (state == PIP_DEEP_SLEEP_STATE_ON)
    417		/*
    418		 * Send ping command to notify device prepare for wake up
    419		 * when it's in deep sleep mode. At this time, device will
    420		 * response nothing except an I2C NAK.
    421		 */
    422		cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
    423
    424	return cyapa_pip_deep_sleep(cyapa, state);
    425}
    426
    427static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
    428		u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage)
    429{
    430	struct device *dev = &cyapa->client->dev;
    431	struct gen6_interval_setting *interval_setting =
    432			&cyapa->gen6_interval_setting;
    433	u8 lp_mode;
    434	int error;
    435
    436	if (cyapa->state != CYAPA_STATE_GEN6_APP)
    437		return 0;
    438
    439	if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
    440		/*
    441		 * Assume TP in deep sleep mode when driver is loaded,
    442		 * avoid driver unload and reload command IO issue caused by TP
    443		 * has been set into deep sleep mode when unloading.
    444		 */
    445		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
    446	}
    447
    448	if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
    449		PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
    450		PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
    451
    452	if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
    453		if (power_mode == PWR_MODE_OFF ||
    454			power_mode == PWR_MODE_FULL_ACTIVE ||
    455			power_mode == PWR_MODE_BTN_ONLY ||
    456			PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
    457			/* Has in correct power mode state, early return. */
    458			return 0;
    459		}
    460	}
    461
    462	if (power_mode == PWR_MODE_OFF) {
    463		cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
    464
    465		error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
    466		if (error) {
    467			dev_err(dev, "enter deep sleep fail: %d\n", error);
    468			return error;
    469		}
    470
    471		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
    472		return 0;
    473	}
    474
    475	/*
    476	 * When trackpad in power off mode, it cannot change to other power
    477	 * state directly, must be wake up from sleep firstly, then
    478	 * continue to do next power sate change.
    479	 */
    480	if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
    481		error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
    482		if (error) {
    483			dev_err(dev, "deep sleep wake fail: %d\n", error);
    484			return error;
    485		}
    486	}
    487
    488	/*
    489	 * Disable device assert interrupts for command response to avoid
    490	 * disturbing system suspending or hibernating process.
    491	 */
    492	cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
    493
    494	if (power_mode == PWR_MODE_FULL_ACTIVE) {
    495		error = cyapa_gen6_change_power_state(cyapa,
    496				GEN6_POWER_MODE_ACTIVE);
    497		if (error) {
    498			dev_err(dev, "change to active fail: %d\n", error);
    499			goto out;
    500		}
    501
    502		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
    503
    504		/* Sync the interval setting from device. */
    505		cyapa_gen6_get_interval_setting(cyapa, interval_setting);
    506
    507	} else if (power_mode == PWR_MODE_BTN_ONLY) {
    508		error = cyapa_gen6_change_power_state(cyapa,
    509				GEN6_POWER_MODE_BTN_ONLY);
    510		if (error) {
    511			dev_err(dev, "fail to button only mode: %d\n", error);
    512			goto out;
    513		}
    514
    515		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
    516	} else {
    517		/*
    518		 * Gen6 internally supports to 2 low power scan interval time,
    519		 * so can help to switch power mode quickly.
    520		 * such as runtime suspend and system suspend.
    521		 */
    522		if (interval_setting->lp1_interval == sleep_time) {
    523			lp_mode = GEN6_POWER_MODE_LP_MODE1;
    524		} else if (interval_setting->lp2_interval == sleep_time) {
    525			lp_mode = GEN6_POWER_MODE_LP_MODE2;
    526		} else {
    527			if (interval_setting->lp1_interval == 0) {
    528				interval_setting->lp1_interval = sleep_time;
    529				lp_mode = GEN6_POWER_MODE_LP_MODE1;
    530			} else {
    531				interval_setting->lp2_interval = sleep_time;
    532				lp_mode = GEN6_POWER_MODE_LP_MODE2;
    533			}
    534			cyapa_gen6_set_interval_setting(cyapa,
    535							interval_setting);
    536		}
    537
    538		error = cyapa_gen6_change_power_state(cyapa, lp_mode);
    539		if (error) {
    540			dev_err(dev, "set power state to 0x%02x failed: %d\n",
    541				lp_mode, error);
    542			goto out;
    543		}
    544
    545		PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
    546		PIP_DEV_SET_PWR_STATE(cyapa,
    547			cyapa_sleep_time_to_pwr_cmd(sleep_time));
    548	}
    549
    550out:
    551	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
    552	return error;
    553}
    554
    555static int cyapa_gen6_initialize(struct cyapa *cyapa)
    556{
    557	return 0;
    558}
    559
    560static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
    561		u16 read_offset, u16 read_len, u8 data_id,
    562		u8 *data, int *data_buf_lens)
    563{
    564	struct retrieve_data_struct_cmd {
    565		struct pip_app_cmd_head head;
    566		__le16 read_offset;
    567		__le16 read_length;
    568		u8 data_id;
    569	} __packed cmd;
    570	u8 resp_data[GEN6_MAX_RX_NUM + 10];
    571	int resp_len;
    572	int error;
    573
    574	memset(&cmd, 0, sizeof(cmd));
    575	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
    576	put_unaligned_le16(sizeof(cmd) - 2, &cmd.head.length);
    577	cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
    578	cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
    579	put_unaligned_le16(read_offset, &cmd.read_offset);
    580	put_unaligned_le16(read_len, &cmd.read_length);
    581	cmd.data_id = data_id;
    582
    583	resp_len = sizeof(resp_data);
    584	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
    585				(u8 *)&cmd, sizeof(cmd),
    586				resp_data, &resp_len,
    587				500, cyapa_sort_tsg_pip_app_resp_data,
    588				true);
    589	if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
    590		resp_data[6] != data_id ||
    591		!VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
    592		return (error < 0) ? error : -EAGAIN;
    593
    594	read_len = get_unaligned_le16(&resp_data[7]);
    595	if (*data_buf_lens < read_len) {
    596		*data_buf_lens = read_len;
    597		return -ENOBUFS;
    598	}
    599
    600	memcpy(data, &resp_data[10], read_len);
    601	*data_buf_lens = read_len;
    602	return 0;
    603}
    604
    605static ssize_t cyapa_gen6_show_baseline(struct device *dev,
    606		struct device_attribute *attr, char *buf)
    607{
    608	struct cyapa *cyapa = dev_get_drvdata(dev);
    609	u8 data[GEN6_MAX_RX_NUM];
    610	int data_len;
    611	int size = 0;
    612	int i;
    613	int error;
    614	int resume_error;
    615
    616	if (!cyapa_is_pip_app_mode(cyapa))
    617		return -EBUSY;
    618
    619	/* 1. Suspend Scanning*/
    620	error = cyapa_pip_suspend_scanning(cyapa);
    621	if (error)
    622		return error;
    623
    624	/* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
    625	data_len = sizeof(data);
    626	error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
    627			GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
    628			data, &data_len);
    629	if (error)
    630		goto resume_scanning;
    631
    632	size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ",
    633			data[0],  /* RX Attenuator Mutual */
    634			data[1],  /* IDAC Mutual */
    635			data[2],  /* RX Attenuator Self RX */
    636			data[3],  /* IDAC Self RX */
    637			data[4],  /* RX Attenuator Self TX */
    638			data[5]	  /* IDAC Self TX */
    639			);
    640
    641	/* 3. Read Attenuator Trim. */
    642	data_len = sizeof(data);
    643	error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
    644			GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
    645			data, &data_len);
    646	if (error)
    647		goto resume_scanning;
    648
    649	/* set attenuator trim values. */
    650	for (i = 0; i < data_len; i++)
    651		size += scnprintf(buf + size, PAGE_SIZE - size,	"%d ", data[i]);
    652	size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
    653
    654resume_scanning:
    655	/* 4. Resume Scanning*/
    656	resume_error = cyapa_pip_resume_scanning(cyapa);
    657	if (resume_error || error) {
    658		memset(buf, 0, PAGE_SIZE);
    659		return resume_error ? resume_error : error;
    660	}
    661
    662	return size;
    663}
    664
    665static int cyapa_gen6_operational_check(struct cyapa *cyapa)
    666{
    667	struct device *dev = &cyapa->client->dev;
    668	int error;
    669
    670	if (cyapa->gen != CYAPA_GEN6)
    671		return -ENODEV;
    672
    673	switch (cyapa->state) {
    674	case CYAPA_STATE_GEN6_BL:
    675		error = cyapa_pip_bl_exit(cyapa);
    676		if (error) {
    677			/* Try to update trackpad product information. */
    678			cyapa_gen6_bl_read_app_info(cyapa);
    679			goto out;
    680		}
    681
    682		cyapa->state = CYAPA_STATE_GEN6_APP;
    683		fallthrough;
    684
    685	case CYAPA_STATE_GEN6_APP:
    686		/*
    687		 * If trackpad device in deep sleep mode,
    688		 * the app command will fail.
    689		 * So always try to reset trackpad device to full active when
    690		 * the device state is required.
    691		 */
    692		error = cyapa_gen6_set_power_mode(cyapa,
    693				PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
    694		if (error)
    695			dev_warn(dev, "%s: failed to set power active mode.\n",
    696				__func__);
    697
    698		/* By default, the trackpad proximity function is enabled. */
    699		error = cyapa_pip_set_proximity(cyapa, true);
    700		if (error)
    701			dev_warn(dev, "%s: failed to enable proximity.\n",
    702				__func__);
    703
    704		/* Get trackpad product information. */
    705		error = cyapa_gen6_read_sys_info(cyapa);
    706		if (error)
    707			goto out;
    708		/* Only support product ID starting with CYTRA */
    709		if (memcmp(cyapa->product_id, product_id,
    710				strlen(product_id)) != 0) {
    711			dev_err(dev, "%s: unknown product ID (%s)\n",
    712				__func__, cyapa->product_id);
    713			error = -EINVAL;
    714		}
    715		break;
    716	default:
    717		error = -EINVAL;
    718	}
    719
    720out:
    721	return error;
    722}
    723
    724const struct cyapa_dev_ops cyapa_gen6_ops = {
    725	.check_fw = cyapa_pip_check_fw,
    726	.bl_enter = cyapa_pip_bl_enter,
    727	.bl_initiate = cyapa_pip_bl_initiate,
    728	.update_fw = cyapa_pip_do_fw_update,
    729	.bl_activate = cyapa_pip_bl_activate,
    730	.bl_deactivate = cyapa_pip_bl_deactivate,
    731
    732	.show_baseline = cyapa_gen6_show_baseline,
    733	.calibrate_store = cyapa_pip_do_calibrate,
    734
    735	.initialize = cyapa_gen6_initialize,
    736
    737	.state_parse = cyapa_pip_state_parse,
    738	.operational_check = cyapa_gen6_operational_check,
    739
    740	.irq_handler = cyapa_pip_irq_handler,
    741	.irq_cmd_handler = cyapa_pip_irq_cmd_handler,
    742	.sort_empty_output_data = cyapa_empty_pip_output_data,
    743	.set_power_mode = cyapa_gen6_set_power_mode,
    744
    745	.set_proximity = cyapa_gen6_set_proximity,
    746};