18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012-2017 Hideep, Inc.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/of.h>
88c2ecf20Sopenharmony_ci#include <linux/firmware.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/acpi.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
168c2ecf20Sopenharmony_ci#include <linux/input.h>
178c2ecf20Sopenharmony_ci#include <linux/input/mt.h>
188c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h>
198c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
208c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define HIDEEP_TS_NAME			"HiDeep Touchscreen"
238c2ecf20Sopenharmony_ci#define HIDEEP_I2C_NAME			"hideep_ts"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define HIDEEP_MT_MAX			10
268c2ecf20Sopenharmony_ci#define HIDEEP_KEY_MAX			3
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* count(2) + touch data(100) + key data(6) */
298c2ecf20Sopenharmony_ci#define HIDEEP_MAX_EVENT		108UL
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define HIDEEP_TOUCH_EVENT_INDEX	2
328c2ecf20Sopenharmony_ci#define HIDEEP_KEY_EVENT_INDEX		102
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Touch & key event */
358c2ecf20Sopenharmony_ci#define HIDEEP_EVENT_ADDR		0x240
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* command list */
388c2ecf20Sopenharmony_ci#define HIDEEP_RESET_CMD		0x9800
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* event bit */
418c2ecf20Sopenharmony_ci#define HIDEEP_MT_RELEASED		BIT(4)
428c2ecf20Sopenharmony_ci#define HIDEEP_KEY_PRESSED		BIT(7)
438c2ecf20Sopenharmony_ci#define HIDEEP_KEY_FIRST_PRESSED	BIT(8)
448c2ecf20Sopenharmony_ci#define HIDEEP_KEY_PRESSED_MASK		(HIDEEP_KEY_PRESSED | \
458c2ecf20Sopenharmony_ci					 HIDEEP_KEY_FIRST_PRESSED)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define HIDEEP_KEY_IDX_MASK		0x0f
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* For NVM */
508c2ecf20Sopenharmony_ci#define HIDEEP_YRAM_BASE		0x40000000
518c2ecf20Sopenharmony_ci#define HIDEEP_PERIPHERAL_BASE		0x50000000
528c2ecf20Sopenharmony_ci#define HIDEEP_ESI_BASE			(HIDEEP_PERIPHERAL_BASE + 0x00000000)
538c2ecf20Sopenharmony_ci#define HIDEEP_FLASH_BASE		(HIDEEP_PERIPHERAL_BASE + 0x01000000)
548c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_BASE		(HIDEEP_PERIPHERAL_BASE + 0x02000000)
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_MOD_CON		(HIDEEP_SYSCON_BASE + 0x0000)
578c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_SPC_CON		(HIDEEP_SYSCON_BASE + 0x0004)
588c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_CLK_CON		(HIDEEP_SYSCON_BASE + 0x0008)
598c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_CLK_ENA		(HIDEEP_SYSCON_BASE + 0x000C)
608c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_RST_CON		(HIDEEP_SYSCON_BASE + 0x0010)
618c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_WDT_CON		(HIDEEP_SYSCON_BASE + 0x0014)
628c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_WDT_CNT		(HIDEEP_SYSCON_BASE + 0x0018)
638c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_PWR_CON		(HIDEEP_SYSCON_BASE + 0x0020)
648c2ecf20Sopenharmony_ci#define HIDEEP_SYSCON_PGM_ID		(HIDEEP_SYSCON_BASE + 0x00F4)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define HIDEEP_FLASH_CON		(HIDEEP_FLASH_BASE + 0x0000)
678c2ecf20Sopenharmony_ci#define HIDEEP_FLASH_STA		(HIDEEP_FLASH_BASE + 0x0004)
688c2ecf20Sopenharmony_ci#define HIDEEP_FLASH_CFG		(HIDEEP_FLASH_BASE + 0x0008)
698c2ecf20Sopenharmony_ci#define HIDEEP_FLASH_TIM		(HIDEEP_FLASH_BASE + 0x000C)
708c2ecf20Sopenharmony_ci#define HIDEEP_FLASH_CACHE_CFG		(HIDEEP_FLASH_BASE + 0x0010)
718c2ecf20Sopenharmony_ci#define HIDEEP_FLASH_PIO_SIG		(HIDEEP_FLASH_BASE + 0x400000)
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define HIDEEP_ESI_TX_INVALID		(HIDEEP_ESI_BASE + 0x0008)
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define HIDEEP_PERASE			0x00040000
768c2ecf20Sopenharmony_ci#define HIDEEP_WRONLY			0x00100000
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define HIDEEP_NVM_MASK_OFS		0x0000000C
798c2ecf20Sopenharmony_ci#define HIDEEP_NVM_DEFAULT_PAGE		0
808c2ecf20Sopenharmony_ci#define HIDEEP_NVM_SFR_WPAGE		1
818c2ecf20Sopenharmony_ci#define HIDEEP_NVM_SFR_RPAGE		2
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define HIDEEP_PIO_SIG			0x00400000
848c2ecf20Sopenharmony_ci#define HIDEEP_PROT_MODE		0x03400000
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define HIDEEP_NVM_PAGE_SIZE		128
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#define HIDEEP_DWZ_INFO			0x000002C0
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistruct hideep_event {
918c2ecf20Sopenharmony_ci	__le16 x;
928c2ecf20Sopenharmony_ci	__le16 y;
938c2ecf20Sopenharmony_ci	__le16 z;
948c2ecf20Sopenharmony_ci	u8 w;
958c2ecf20Sopenharmony_ci	u8 flag;
968c2ecf20Sopenharmony_ci	u8 type;
978c2ecf20Sopenharmony_ci	u8 index;
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistruct dwz_info {
1018c2ecf20Sopenharmony_ci	__be32 code_start;
1028c2ecf20Sopenharmony_ci	u8 code_crc[12];
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	__be32 c_code_start;
1058c2ecf20Sopenharmony_ci	__be16 gen_ver;
1068c2ecf20Sopenharmony_ci	__be16 c_code_len;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	__be32 vr_start;
1098c2ecf20Sopenharmony_ci	__be16 rsv0;
1108c2ecf20Sopenharmony_ci	__be16 vr_len;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	__be32 ft_start;
1138c2ecf20Sopenharmony_ci	__be16 vr_version;
1148c2ecf20Sopenharmony_ci	__be16 ft_len;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	__be16 core_ver;
1178c2ecf20Sopenharmony_ci	__be16 boot_ver;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	__be16 release_ver;
1208c2ecf20Sopenharmony_ci	__be16 custom_ver;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	u8 factory_id;
1238c2ecf20Sopenharmony_ci	u8 panel_type;
1248c2ecf20Sopenharmony_ci	u8 model_name[6];
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	__be16 extra_option;
1278c2ecf20Sopenharmony_ci	__be16 product_code;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	__be16 vendor_id;
1308c2ecf20Sopenharmony_ci	__be16 product_id;
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistruct pgm_packet {
1348c2ecf20Sopenharmony_ci	struct {
1358c2ecf20Sopenharmony_ci		u8 unused[3];
1368c2ecf20Sopenharmony_ci		u8 len;
1378c2ecf20Sopenharmony_ci		__be32 addr;
1388c2ecf20Sopenharmony_ci	} header;
1398c2ecf20Sopenharmony_ci	__be32 payload[HIDEEP_NVM_PAGE_SIZE / sizeof(__be32)];
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define HIDEEP_XFER_BUF_SIZE	sizeof(struct pgm_packet)
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistruct hideep_ts {
1458c2ecf20Sopenharmony_ci	struct i2c_client *client;
1468c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
1478c2ecf20Sopenharmony_ci	struct regmap *reg;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	struct touchscreen_properties prop;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	struct gpio_desc *reset_gpio;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	struct regulator *vcc_vdd;
1548c2ecf20Sopenharmony_ci	struct regulator *vcc_vid;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	struct mutex dev_mutex;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	u32 tch_count;
1598c2ecf20Sopenharmony_ci	u32 lpm_count;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/*
1628c2ecf20Sopenharmony_ci	 * Data buffer to read packet from the device (contacts and key
1638c2ecf20Sopenharmony_ci	 * states). We align it on double-word boundary to keep word-sized
1648c2ecf20Sopenharmony_ci	 * fields in contact data and double-word-sized fields in program
1658c2ecf20Sopenharmony_ci	 * packet aligned.
1668c2ecf20Sopenharmony_ci	 */
1678c2ecf20Sopenharmony_ci	u8 xfer_buf[HIDEEP_XFER_BUF_SIZE] __aligned(4);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	int key_num;
1708c2ecf20Sopenharmony_ci	u32 key_codes[HIDEEP_KEY_MAX];
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	struct dwz_info dwz_info;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	unsigned int fw_size;
1758c2ecf20Sopenharmony_ci	u32 nvm_mask;
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int hideep_pgm_w_mem(struct hideep_ts *ts, u32 addr,
1798c2ecf20Sopenharmony_ci			    const __be32 *data, size_t count)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct pgm_packet *packet = (void *)ts->xfer_buf;
1828c2ecf20Sopenharmony_ci	size_t len = count * sizeof(*data);
1838c2ecf20Sopenharmony_ci	struct i2c_msg msg = {
1848c2ecf20Sopenharmony_ci		.addr	= ts->client->addr,
1858c2ecf20Sopenharmony_ci		.len	= len + sizeof(packet->header.len) +
1868c2ecf20Sopenharmony_ci				sizeof(packet->header.addr),
1878c2ecf20Sopenharmony_ci		.buf	= &packet->header.len,
1888c2ecf20Sopenharmony_ci	};
1898c2ecf20Sopenharmony_ci	int ret;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (len > HIDEEP_NVM_PAGE_SIZE)
1928c2ecf20Sopenharmony_ci		return -EINVAL;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	packet->header.len = 0x80 | (count - 1);
1958c2ecf20Sopenharmony_ci	packet->header.addr = cpu_to_be32(addr);
1968c2ecf20Sopenharmony_ci	memcpy(packet->payload, data, len);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	ret = i2c_transfer(ts->client->adapter, &msg, 1);
1998c2ecf20Sopenharmony_ci	if (ret != 1)
2008c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EIO;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int hideep_pgm_r_mem(struct hideep_ts *ts, u32 addr,
2068c2ecf20Sopenharmony_ci			    __be32 *data, size_t count)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct pgm_packet *packet = (void *)ts->xfer_buf;
2098c2ecf20Sopenharmony_ci	size_t len = count * sizeof(*data);
2108c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
2118c2ecf20Sopenharmony_ci		{
2128c2ecf20Sopenharmony_ci			.addr	= ts->client->addr,
2138c2ecf20Sopenharmony_ci			.len	= sizeof(packet->header.len) +
2148c2ecf20Sopenharmony_ci					sizeof(packet->header.addr),
2158c2ecf20Sopenharmony_ci			.buf	= &packet->header.len,
2168c2ecf20Sopenharmony_ci		},
2178c2ecf20Sopenharmony_ci		{
2188c2ecf20Sopenharmony_ci			.addr	= ts->client->addr,
2198c2ecf20Sopenharmony_ci			.flags	= I2C_M_RD,
2208c2ecf20Sopenharmony_ci			.len	= len,
2218c2ecf20Sopenharmony_ci			.buf	= (u8 *)data,
2228c2ecf20Sopenharmony_ci		},
2238c2ecf20Sopenharmony_ci	};
2248c2ecf20Sopenharmony_ci	int ret;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (len > HIDEEP_NVM_PAGE_SIZE)
2278c2ecf20Sopenharmony_ci		return -EINVAL;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	packet->header.len = count - 1;
2308c2ecf20Sopenharmony_ci	packet->header.addr = cpu_to_be32(addr);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
2338c2ecf20Sopenharmony_ci	if (ret != ARRAY_SIZE(msg))
2348c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EIO;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int hideep_pgm_r_reg(struct hideep_ts *ts, u32 addr, u32 *val)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	__be32 data;
2428c2ecf20Sopenharmony_ci	int error;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	error = hideep_pgm_r_mem(ts, addr, &data, 1);
2458c2ecf20Sopenharmony_ci	if (error) {
2468c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev,
2478c2ecf20Sopenharmony_ci			"read of register %#08x failed: %d\n",
2488c2ecf20Sopenharmony_ci			addr, error);
2498c2ecf20Sopenharmony_ci		return error;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	*val = be32_to_cpu(data);
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int hideep_pgm_w_reg(struct hideep_ts *ts, u32 addr, u32 val)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	__be32 data = cpu_to_be32(val);
2598c2ecf20Sopenharmony_ci	int error;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	error = hideep_pgm_w_mem(ts, addr, &data, 1);
2628c2ecf20Sopenharmony_ci	if (error) {
2638c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev,
2648c2ecf20Sopenharmony_ci			"write to register %#08x (%#08x) failed: %d\n",
2658c2ecf20Sopenharmony_ci			addr, val, error);
2668c2ecf20Sopenharmony_ci		return error;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci#define SW_RESET_IN_PGM(clk)					\
2738c2ecf20Sopenharmony_ci{								\
2748c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CNT, (clk));	\
2758c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x03);	\
2768c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x01);	\
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci#define SET_FLASH_PIO(ce)					\
2808c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON,			\
2818c2ecf20Sopenharmony_ci			 0x01 | ((ce) << 1))
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci#define SET_PIO_SIG(x, y)					\
2848c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_PIO_SIG + (x), (y))
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci#define SET_FLASH_HWCONTROL()					\
2878c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x00)
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci#define NVM_W_SFR(x, y)						\
2908c2ecf20Sopenharmony_ci{								\
2918c2ecf20Sopenharmony_ci	SET_FLASH_PIO(1);					\
2928c2ecf20Sopenharmony_ci	SET_PIO_SIG(x, y);					\
2938c2ecf20Sopenharmony_ci	SET_FLASH_PIO(0);					\
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic void hideep_pgm_set(struct hideep_ts *ts)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x00);
2998c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_SPC_CON, 0x00);
3008c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_ENA, 0xFF);
3018c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_CON, 0x01);
3028c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_PWR_CON, 0x01);
3038c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_TIM, 0x03);
3048c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CACHE_CFG, 0x00);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int hideep_pgm_get_pattern(struct hideep_ts *ts, u32 *pattern)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	u16 p1 = 0xAF39;
3108c2ecf20Sopenharmony_ci	u16 p2 = 0xDF9D;
3118c2ecf20Sopenharmony_ci	int error;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	error = regmap_bulk_write(ts->reg, p1, &p2, 1);
3148c2ecf20Sopenharmony_ci	if (error) {
3158c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev,
3168c2ecf20Sopenharmony_ci			"%s: regmap_bulk_write() failed with %d\n",
3178c2ecf20Sopenharmony_ci			__func__, error);
3188c2ecf20Sopenharmony_ci		return error;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	usleep_range(1000, 1100);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* flush invalid Tx load register */
3248c2ecf20Sopenharmony_ci	error = hideep_pgm_w_reg(ts, HIDEEP_ESI_TX_INVALID, 0x01);
3258c2ecf20Sopenharmony_ci	if (error)
3268c2ecf20Sopenharmony_ci		return error;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	error = hideep_pgm_r_reg(ts, HIDEEP_SYSCON_PGM_ID, pattern);
3298c2ecf20Sopenharmony_ci	if (error)
3308c2ecf20Sopenharmony_ci		return error;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return 0;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int hideep_enter_pgm(struct hideep_ts *ts)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	int retry_count = 10;
3388c2ecf20Sopenharmony_ci	u32 pattern;
3398c2ecf20Sopenharmony_ci	int error;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	while (retry_count--) {
3428c2ecf20Sopenharmony_ci		error = hideep_pgm_get_pattern(ts, &pattern);
3438c2ecf20Sopenharmony_ci		if (error) {
3448c2ecf20Sopenharmony_ci			dev_err(&ts->client->dev,
3458c2ecf20Sopenharmony_ci				"hideep_pgm_get_pattern failed: %d\n", error);
3468c2ecf20Sopenharmony_ci		} else if (pattern != 0x39AF9DDF) {
3478c2ecf20Sopenharmony_ci			dev_err(&ts->client->dev, "%s: bad pattern: %#08x\n",
3488c2ecf20Sopenharmony_ci				__func__, pattern);
3498c2ecf20Sopenharmony_ci		} else {
3508c2ecf20Sopenharmony_ci			dev_dbg(&ts->client->dev, "found magic code");
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci			hideep_pgm_set(ts);
3538c2ecf20Sopenharmony_ci			usleep_range(1000, 1100);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci			return 0;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	dev_err(&ts->client->dev, "failed to  enter pgm mode\n");
3608c2ecf20Sopenharmony_ci	SW_RESET_IN_PGM(1000);
3618c2ecf20Sopenharmony_ci	return -EIO;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic int hideep_nvm_unlock(struct hideep_ts *ts)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	u32 unmask_code;
3678c2ecf20Sopenharmony_ci	int error;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_SFR_RPAGE);
3708c2ecf20Sopenharmony_ci	error = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);
3718c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_DEFAULT_PAGE);
3728c2ecf20Sopenharmony_ci	if (error)
3738c2ecf20Sopenharmony_ci		return error;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* make it unprotected code */
3768c2ecf20Sopenharmony_ci	unmask_code &= ~HIDEEP_PROT_MODE;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* compare unmask code */
3798c2ecf20Sopenharmony_ci	if (unmask_code != ts->nvm_mask)
3808c2ecf20Sopenharmony_ci		dev_warn(&ts->client->dev,
3818c2ecf20Sopenharmony_ci			 "read mask code different %#08x vs %#08x",
3828c2ecf20Sopenharmony_ci			 unmask_code, ts->nvm_mask);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_SFR_WPAGE);
3858c2ecf20Sopenharmony_ci	SET_FLASH_PIO(0);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	NVM_W_SFR(HIDEEP_NVM_MASK_OFS, ts->nvm_mask);
3888c2ecf20Sopenharmony_ci	SET_FLASH_HWCONTROL();
3898c2ecf20Sopenharmony_ci	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_DEFAULT_PAGE);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int hideep_check_status(struct hideep_ts *ts)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	int time_out = 100;
3978c2ecf20Sopenharmony_ci	int status;
3988c2ecf20Sopenharmony_ci	int error;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	while (time_out--) {
4018c2ecf20Sopenharmony_ci		error = hideep_pgm_r_reg(ts, HIDEEP_FLASH_STA, &status);
4028c2ecf20Sopenharmony_ci		if (!error && status)
4038c2ecf20Sopenharmony_ci			return 0;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		usleep_range(1000, 1100);
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int hideep_program_page(struct hideep_ts *ts, u32 addr,
4128c2ecf20Sopenharmony_ci			       const __be32 *ucode, size_t xfer_count)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	u32 val;
4158c2ecf20Sopenharmony_ci	int error;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	error = hideep_check_status(ts);
4188c2ecf20Sopenharmony_ci	if (error)
4198c2ecf20Sopenharmony_ci		return -EBUSY;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	addr &= ~(HIDEEP_NVM_PAGE_SIZE - 1);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	SET_FLASH_PIO(0);
4248c2ecf20Sopenharmony_ci	SET_FLASH_PIO(1);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* erase page */
4278c2ecf20Sopenharmony_ci	SET_PIO_SIG(HIDEEP_PERASE | addr, 0xFFFFFFFF);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	SET_FLASH_PIO(0);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	error = hideep_check_status(ts);
4328c2ecf20Sopenharmony_ci	if (error)
4338c2ecf20Sopenharmony_ci		return -EBUSY;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* write page */
4368c2ecf20Sopenharmony_ci	SET_FLASH_PIO(1);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	val = be32_to_cpu(ucode[0]);
4398c2ecf20Sopenharmony_ci	SET_PIO_SIG(HIDEEP_WRONLY | addr, val);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	hideep_pgm_w_mem(ts, HIDEEP_FLASH_PIO_SIG | HIDEEP_WRONLY,
4428c2ecf20Sopenharmony_ci			 ucode, xfer_count);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	val = be32_to_cpu(ucode[xfer_count - 1]);
4458c2ecf20Sopenharmony_ci	SET_PIO_SIG(124, val);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	SET_FLASH_PIO(0);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	usleep_range(1000, 1100);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	error = hideep_check_status(ts);
4528c2ecf20Sopenharmony_ci	if (error)
4538c2ecf20Sopenharmony_ci		return -EBUSY;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	SET_FLASH_HWCONTROL();
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return 0;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic int hideep_program_nvm(struct hideep_ts *ts,
4618c2ecf20Sopenharmony_ci			      const __be32 *ucode, size_t ucode_len)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct pgm_packet *packet_r = (void *)ts->xfer_buf;
4648c2ecf20Sopenharmony_ci	__be32 *current_ucode = packet_r->payload;
4658c2ecf20Sopenharmony_ci	size_t xfer_len;
4668c2ecf20Sopenharmony_ci	size_t xfer_count;
4678c2ecf20Sopenharmony_ci	u32 addr = 0;
4688c2ecf20Sopenharmony_ci	int error;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci       error = hideep_nvm_unlock(ts);
4718c2ecf20Sopenharmony_ci       if (error)
4728c2ecf20Sopenharmony_ci               return error;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	while (ucode_len > 0) {
4758c2ecf20Sopenharmony_ci		xfer_len = min_t(size_t, ucode_len, HIDEEP_NVM_PAGE_SIZE);
4768c2ecf20Sopenharmony_ci		xfer_count = xfer_len / sizeof(*ucode);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		error = hideep_pgm_r_mem(ts, 0x00000000 + addr,
4798c2ecf20Sopenharmony_ci					 current_ucode, xfer_count);
4808c2ecf20Sopenharmony_ci		if (error) {
4818c2ecf20Sopenharmony_ci			dev_err(&ts->client->dev,
4828c2ecf20Sopenharmony_ci				"%s: failed to read page at offset %#08x: %d\n",
4838c2ecf20Sopenharmony_ci				__func__, addr, error);
4848c2ecf20Sopenharmony_ci			return error;
4858c2ecf20Sopenharmony_ci		}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		/* See if the page needs updating */
4888c2ecf20Sopenharmony_ci		if (memcmp(ucode, current_ucode, xfer_len)) {
4898c2ecf20Sopenharmony_ci			error = hideep_program_page(ts, addr,
4908c2ecf20Sopenharmony_ci						    ucode, xfer_count);
4918c2ecf20Sopenharmony_ci			if (error) {
4928c2ecf20Sopenharmony_ci				dev_err(&ts->client->dev,
4938c2ecf20Sopenharmony_ci					"%s: iwrite failure @%#08x: %d\n",
4948c2ecf20Sopenharmony_ci					__func__, addr, error);
4958c2ecf20Sopenharmony_ci				return error;
4968c2ecf20Sopenharmony_ci			}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci			usleep_range(1000, 1100);
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		ucode += xfer_count;
5028c2ecf20Sopenharmony_ci		addr += xfer_len;
5038c2ecf20Sopenharmony_ci		ucode_len -= xfer_len;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	return 0;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic int hideep_verify_nvm(struct hideep_ts *ts,
5108c2ecf20Sopenharmony_ci			     const __be32 *ucode, size_t ucode_len)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct pgm_packet *packet_r = (void *)ts->xfer_buf;
5138c2ecf20Sopenharmony_ci	__be32 *current_ucode = packet_r->payload;
5148c2ecf20Sopenharmony_ci	size_t xfer_len;
5158c2ecf20Sopenharmony_ci	size_t xfer_count;
5168c2ecf20Sopenharmony_ci	u32 addr = 0;
5178c2ecf20Sopenharmony_ci	int i;
5188c2ecf20Sopenharmony_ci	int error;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	while (ucode_len > 0) {
5218c2ecf20Sopenharmony_ci		xfer_len = min_t(size_t, ucode_len, HIDEEP_NVM_PAGE_SIZE);
5228c2ecf20Sopenharmony_ci		xfer_count = xfer_len / sizeof(*ucode);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci		error = hideep_pgm_r_mem(ts, 0x00000000 + addr,
5258c2ecf20Sopenharmony_ci					 current_ucode, xfer_count);
5268c2ecf20Sopenharmony_ci		if (error) {
5278c2ecf20Sopenharmony_ci			dev_err(&ts->client->dev,
5288c2ecf20Sopenharmony_ci				"%s: failed to read page at offset %#08x: %d\n",
5298c2ecf20Sopenharmony_ci				__func__, addr, error);
5308c2ecf20Sopenharmony_ci			return error;
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		if (memcmp(ucode, current_ucode, xfer_len)) {
5348c2ecf20Sopenharmony_ci			const u8 *ucode_bytes = (const u8 *)ucode;
5358c2ecf20Sopenharmony_ci			const u8 *current_bytes = (const u8 *)current_ucode;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci			for (i = 0; i < xfer_len; i++)
5388c2ecf20Sopenharmony_ci				if (ucode_bytes[i] != current_bytes[i])
5398c2ecf20Sopenharmony_ci					dev_err(&ts->client->dev,
5408c2ecf20Sopenharmony_ci						"%s: mismatch @%#08x: (%#02x vs %#02x)\n",
5418c2ecf20Sopenharmony_ci						__func__, addr + i,
5428c2ecf20Sopenharmony_ci						ucode_bytes[i],
5438c2ecf20Sopenharmony_ci						current_bytes[i]);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci			return -EIO;
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		ucode += xfer_count;
5498c2ecf20Sopenharmony_ci		addr += xfer_len;
5508c2ecf20Sopenharmony_ci		ucode_len -= xfer_len;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return 0;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic int hideep_load_dwz(struct hideep_ts *ts)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	u16 product_code;
5598c2ecf20Sopenharmony_ci	int error;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	error = hideep_enter_pgm(ts);
5628c2ecf20Sopenharmony_ci	if (error)
5638c2ecf20Sopenharmony_ci		return error;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	msleep(50);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	error = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO,
5688c2ecf20Sopenharmony_ci				 (void *)&ts->dwz_info,
5698c2ecf20Sopenharmony_ci				 sizeof(ts->dwz_info) / sizeof(__be32));
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	SW_RESET_IN_PGM(10);
5728c2ecf20Sopenharmony_ci	msleep(50);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (error) {
5758c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev,
5768c2ecf20Sopenharmony_ci			"failed to fetch DWZ data: %d\n", error);
5778c2ecf20Sopenharmony_ci		return error;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	product_code = be16_to_cpu(ts->dwz_info.product_code);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	switch (product_code & 0xF0) {
5838c2ecf20Sopenharmony_ci	case 0x40:
5848c2ecf20Sopenharmony_ci		dev_dbg(&ts->client->dev, "used crimson IC");
5858c2ecf20Sopenharmony_ci		ts->fw_size = 1024 * 48;
5868c2ecf20Sopenharmony_ci		ts->nvm_mask = 0x00310000;
5878c2ecf20Sopenharmony_ci		break;
5888c2ecf20Sopenharmony_ci	case 0x60:
5898c2ecf20Sopenharmony_ci		dev_dbg(&ts->client->dev, "used lime IC");
5908c2ecf20Sopenharmony_ci		ts->fw_size = 1024 * 64;
5918c2ecf20Sopenharmony_ci		ts->nvm_mask = 0x0030027B;
5928c2ecf20Sopenharmony_ci		break;
5938c2ecf20Sopenharmony_ci	default:
5948c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev, "product code is wrong: %#04x",
5958c2ecf20Sopenharmony_ci			product_code);
5968c2ecf20Sopenharmony_ci		return -EINVAL;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	dev_dbg(&ts->client->dev, "firmware release version: %#04x",
6008c2ecf20Sopenharmony_ci		be16_to_cpu(ts->dwz_info.release_ver));
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return 0;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int hideep_flash_firmware(struct hideep_ts *ts,
6068c2ecf20Sopenharmony_ci				 const __be32 *ucode, size_t ucode_len)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	int retry_cnt = 3;
6098c2ecf20Sopenharmony_ci	int error;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	while (retry_cnt--) {
6128c2ecf20Sopenharmony_ci		error = hideep_program_nvm(ts, ucode, ucode_len);
6138c2ecf20Sopenharmony_ci		if (!error) {
6148c2ecf20Sopenharmony_ci			error = hideep_verify_nvm(ts, ucode, ucode_len);
6158c2ecf20Sopenharmony_ci			if (!error)
6168c2ecf20Sopenharmony_ci				return 0;
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	return error;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic int hideep_update_firmware(struct hideep_ts *ts,
6248c2ecf20Sopenharmony_ci				  const __be32 *ucode, size_t ucode_len)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	int error, error2;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	dev_dbg(&ts->client->dev, "starting firmware update");
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* enter program mode */
6318c2ecf20Sopenharmony_ci	error = hideep_enter_pgm(ts);
6328c2ecf20Sopenharmony_ci	if (error)
6338c2ecf20Sopenharmony_ci		return error;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	error = hideep_flash_firmware(ts, ucode, ucode_len);
6368c2ecf20Sopenharmony_ci	if (error)
6378c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev,
6388c2ecf20Sopenharmony_ci			"firmware update failed: %d\n", error);
6398c2ecf20Sopenharmony_ci	else
6408c2ecf20Sopenharmony_ci		dev_dbg(&ts->client->dev, "firmware updated successfully\n");
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	SW_RESET_IN_PGM(1000);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	error2 = hideep_load_dwz(ts);
6458c2ecf20Sopenharmony_ci	if (error2)
6468c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev,
6478c2ecf20Sopenharmony_ci			"failed to load dwz after firmware update: %d\n",
6488c2ecf20Sopenharmony_ci			error2);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	return error ?: error2;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic int hideep_power_on(struct hideep_ts *ts)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	int error = 0;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	error = regulator_enable(ts->vcc_vdd);
6588c2ecf20Sopenharmony_ci	if (error)
6598c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev,
6608c2ecf20Sopenharmony_ci			"failed to enable 'vdd' regulator: %d", error);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	usleep_range(999, 1000);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	error = regulator_enable(ts->vcc_vid);
6658c2ecf20Sopenharmony_ci	if (error)
6668c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev,
6678c2ecf20Sopenharmony_ci			"failed to enable 'vcc_vid' regulator: %d",
6688c2ecf20Sopenharmony_ci			error);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	msleep(30);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (ts->reset_gpio) {
6738c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(ts->reset_gpio, 0);
6748c2ecf20Sopenharmony_ci	} else {
6758c2ecf20Sopenharmony_ci		error = regmap_write(ts->reg, HIDEEP_RESET_CMD, 0x01);
6768c2ecf20Sopenharmony_ci		if (error)
6778c2ecf20Sopenharmony_ci			dev_err(&ts->client->dev,
6788c2ecf20Sopenharmony_ci				"failed to send 'reset' command: %d\n", error);
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	msleep(50);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	return error;
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic void hideep_power_off(void *data)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct hideep_ts *ts = data;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (ts->reset_gpio)
6918c2ecf20Sopenharmony_ci		gpiod_set_value(ts->reset_gpio, 1);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	regulator_disable(ts->vcc_vid);
6948c2ecf20Sopenharmony_ci	regulator_disable(ts->vcc_vdd);
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci#define __GET_MT_TOOL_TYPE(type) ((type) == 0x01 ? MT_TOOL_FINGER : MT_TOOL_PEN)
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic void hideep_report_slot(struct input_dev *input,
7008c2ecf20Sopenharmony_ci			       const struct hideep_event *event)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	input_mt_slot(input, event->index & 0x0f);
7038c2ecf20Sopenharmony_ci	input_mt_report_slot_state(input,
7048c2ecf20Sopenharmony_ci				   __GET_MT_TOOL_TYPE(event->type),
7058c2ecf20Sopenharmony_ci				   !(event->flag & HIDEEP_MT_RELEASED));
7068c2ecf20Sopenharmony_ci	if (!(event->flag & HIDEEP_MT_RELEASED)) {
7078c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_MT_POSITION_X,
7088c2ecf20Sopenharmony_ci				 le16_to_cpup(&event->x));
7098c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_MT_POSITION_Y,
7108c2ecf20Sopenharmony_ci				 le16_to_cpup(&event->y));
7118c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_MT_PRESSURE,
7128c2ecf20Sopenharmony_ci				 le16_to_cpup(&event->z));
7138c2ecf20Sopenharmony_ci		input_report_abs(input, ABS_MT_TOUCH_MAJOR, event->w);
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic void hideep_parse_and_report(struct hideep_ts *ts)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	const struct hideep_event *events =
7208c2ecf20Sopenharmony_ci			(void *)&ts->xfer_buf[HIDEEP_TOUCH_EVENT_INDEX];
7218c2ecf20Sopenharmony_ci	const u8 *keys = &ts->xfer_buf[HIDEEP_KEY_EVENT_INDEX];
7228c2ecf20Sopenharmony_ci	int touch_count = ts->xfer_buf[0];
7238c2ecf20Sopenharmony_ci	int key_count = ts->xfer_buf[1] & 0x0f;
7248c2ecf20Sopenharmony_ci	int lpm_count = ts->xfer_buf[1] & 0xf0;
7258c2ecf20Sopenharmony_ci	int i;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* get touch event count */
7288c2ecf20Sopenharmony_ci	dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x",
7298c2ecf20Sopenharmony_ci		touch_count, key_count, lpm_count);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	touch_count = min(touch_count, HIDEEP_MT_MAX);
7328c2ecf20Sopenharmony_ci	for (i = 0; i < touch_count; i++)
7338c2ecf20Sopenharmony_ci		hideep_report_slot(ts->input_dev, events + i);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	key_count = min(key_count, HIDEEP_KEY_MAX);
7368c2ecf20Sopenharmony_ci	for (i = 0; i < key_count; i++) {
7378c2ecf20Sopenharmony_ci		u8 key_data = keys[i * 2];
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		input_report_key(ts->input_dev,
7408c2ecf20Sopenharmony_ci				 ts->key_codes[key_data & HIDEEP_KEY_IDX_MASK],
7418c2ecf20Sopenharmony_ci				 key_data & HIDEEP_KEY_PRESSED_MASK);
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	input_mt_sync_frame(ts->input_dev);
7458c2ecf20Sopenharmony_ci	input_sync(ts->input_dev);
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic irqreturn_t hideep_irq(int irq, void *handle)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	struct hideep_ts *ts = handle;
7518c2ecf20Sopenharmony_ci	int error;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	BUILD_BUG_ON(HIDEEP_MAX_EVENT > HIDEEP_XFER_BUF_SIZE);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	error = regmap_bulk_read(ts->reg, HIDEEP_EVENT_ADDR,
7568c2ecf20Sopenharmony_ci				 ts->xfer_buf, HIDEEP_MAX_EVENT / 2);
7578c2ecf20Sopenharmony_ci	if (error) {
7588c2ecf20Sopenharmony_ci		dev_err(&ts->client->dev, "failed to read events: %d\n", error);
7598c2ecf20Sopenharmony_ci		goto out;
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	hideep_parse_and_report(ts);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ciout:
7658c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_cistatic int hideep_get_axis_info(struct hideep_ts *ts)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	__le16 val[2];
7718c2ecf20Sopenharmony_ci	int error;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	error = regmap_bulk_read(ts->reg, 0x28, val, ARRAY_SIZE(val));
7748c2ecf20Sopenharmony_ci	if (error)
7758c2ecf20Sopenharmony_ci		return error;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	ts->prop.max_x = le16_to_cpup(val);
7788c2ecf20Sopenharmony_ci	ts->prop.max_y = le16_to_cpup(val + 1);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	dev_dbg(&ts->client->dev, "X: %d, Y: %d",
7818c2ecf20Sopenharmony_ci		ts->prop.max_x, ts->prop.max_y);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	return 0;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic int hideep_init_input(struct hideep_ts *ts)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	struct device *dev = &ts->client->dev;
7898c2ecf20Sopenharmony_ci	int i;
7908c2ecf20Sopenharmony_ci	int error;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	ts->input_dev = devm_input_allocate_device(dev);
7938c2ecf20Sopenharmony_ci	if (!ts->input_dev) {
7948c2ecf20Sopenharmony_ci		dev_err(dev, "failed to allocate input device\n");
7958c2ecf20Sopenharmony_ci		return -ENOMEM;
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	ts->input_dev->name = HIDEEP_TS_NAME;
7998c2ecf20Sopenharmony_ci	ts->input_dev->id.bustype = BUS_I2C;
8008c2ecf20Sopenharmony_ci	input_set_drvdata(ts->input_dev, ts);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_X);
8038c2ecf20Sopenharmony_ci	input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_Y);
8048c2ecf20Sopenharmony_ci	input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 65535, 0, 0);
8058c2ecf20Sopenharmony_ci	input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
8068c2ecf20Sopenharmony_ci	input_set_abs_params(ts->input_dev, ABS_MT_TOOL_TYPE,
8078c2ecf20Sopenharmony_ci			     0, MT_TOOL_MAX, 0, 0);
8088c2ecf20Sopenharmony_ci	touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
8118c2ecf20Sopenharmony_ci		error = hideep_get_axis_info(ts);
8128c2ecf20Sopenharmony_ci		if (error)
8138c2ecf20Sopenharmony_ci			return error;
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	error = input_mt_init_slots(ts->input_dev, HIDEEP_MT_MAX,
8178c2ecf20Sopenharmony_ci				    INPUT_MT_DIRECT);
8188c2ecf20Sopenharmony_ci	if (error)
8198c2ecf20Sopenharmony_ci		return error;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	ts->key_num = device_property_count_u32(dev, "linux,keycodes");
8228c2ecf20Sopenharmony_ci	if (ts->key_num > HIDEEP_KEY_MAX) {
8238c2ecf20Sopenharmony_ci		dev_err(dev, "too many keys defined: %d\n",
8248c2ecf20Sopenharmony_ci			ts->key_num);
8258c2ecf20Sopenharmony_ci		return -EINVAL;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (ts->key_num <= 0) {
8298c2ecf20Sopenharmony_ci		dev_dbg(dev,
8308c2ecf20Sopenharmony_ci			"missing or malformed 'linux,keycodes' property\n");
8318c2ecf20Sopenharmony_ci	} else {
8328c2ecf20Sopenharmony_ci		error = device_property_read_u32_array(dev, "linux,keycodes",
8338c2ecf20Sopenharmony_ci						       ts->key_codes,
8348c2ecf20Sopenharmony_ci						       ts->key_num);
8358c2ecf20Sopenharmony_ci		if (error) {
8368c2ecf20Sopenharmony_ci			dev_dbg(dev, "failed to read keymap: %d", error);
8378c2ecf20Sopenharmony_ci			return error;
8388c2ecf20Sopenharmony_ci		}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci		if (ts->key_num) {
8418c2ecf20Sopenharmony_ci			ts->input_dev->keycode = ts->key_codes;
8428c2ecf20Sopenharmony_ci			ts->input_dev->keycodesize = sizeof(ts->key_codes[0]);
8438c2ecf20Sopenharmony_ci			ts->input_dev->keycodemax = ts->key_num;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci			for (i = 0; i < ts->key_num; i++)
8468c2ecf20Sopenharmony_ci				input_set_capability(ts->input_dev, EV_KEY,
8478c2ecf20Sopenharmony_ci					ts->key_codes[i]);
8488c2ecf20Sopenharmony_ci		}
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	error = input_register_device(ts->input_dev);
8528c2ecf20Sopenharmony_ci	if (error) {
8538c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register input device: %d", error);
8548c2ecf20Sopenharmony_ci		return error;
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	return 0;
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic ssize_t hideep_update_fw(struct device *dev,
8618c2ecf20Sopenharmony_ci				struct device_attribute *attr,
8628c2ecf20Sopenharmony_ci				const char *buf, size_t count)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
8658c2ecf20Sopenharmony_ci	struct hideep_ts *ts = i2c_get_clientdata(client);
8668c2ecf20Sopenharmony_ci	const struct firmware *fw_entry;
8678c2ecf20Sopenharmony_ci	char *fw_name;
8688c2ecf20Sopenharmony_ci	int mode;
8698c2ecf20Sopenharmony_ci	int error;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	error = kstrtoint(buf, 0, &mode);
8728c2ecf20Sopenharmony_ci	if (error)
8738c2ecf20Sopenharmony_ci		return error;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin",
8768c2ecf20Sopenharmony_ci			    be16_to_cpu(ts->dwz_info.product_id));
8778c2ecf20Sopenharmony_ci	if (!fw_name)
8788c2ecf20Sopenharmony_ci		return -ENOMEM;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	error = request_firmware(&fw_entry, fw_name, dev);
8818c2ecf20Sopenharmony_ci	if (error) {
8828c2ecf20Sopenharmony_ci		dev_err(dev, "failed to request firmware %s: %d",
8838c2ecf20Sopenharmony_ci			fw_name, error);
8848c2ecf20Sopenharmony_ci		goto out_free_fw_name;
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	if (fw_entry->size % sizeof(__be32)) {
8888c2ecf20Sopenharmony_ci		dev_err(dev, "invalid firmware size %zu\n", fw_entry->size);
8898c2ecf20Sopenharmony_ci		error = -EINVAL;
8908c2ecf20Sopenharmony_ci		goto out_release_fw;
8918c2ecf20Sopenharmony_ci	}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	if (fw_entry->size > ts->fw_size) {
8948c2ecf20Sopenharmony_ci		dev_err(dev, "fw size (%zu) is too big (memory size %d)\n",
8958c2ecf20Sopenharmony_ci			fw_entry->size, ts->fw_size);
8968c2ecf20Sopenharmony_ci		error = -EFBIG;
8978c2ecf20Sopenharmony_ci		goto out_release_fw;
8988c2ecf20Sopenharmony_ci	}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	mutex_lock(&ts->dev_mutex);
9018c2ecf20Sopenharmony_ci	disable_irq(client->irq);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	error = hideep_update_firmware(ts, (const __be32 *)fw_entry->data,
9048c2ecf20Sopenharmony_ci				       fw_entry->size);
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	enable_irq(client->irq);
9078c2ecf20Sopenharmony_ci	mutex_unlock(&ts->dev_mutex);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ciout_release_fw:
9108c2ecf20Sopenharmony_ci	release_firmware(fw_entry);
9118c2ecf20Sopenharmony_ciout_free_fw_name:
9128c2ecf20Sopenharmony_ci	kfree(fw_name);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	return error ?: count;
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_cistatic ssize_t hideep_fw_version_show(struct device *dev,
9188c2ecf20Sopenharmony_ci				      struct device_attribute *attr, char *buf)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
9218c2ecf20Sopenharmony_ci	struct hideep_ts *ts = i2c_get_clientdata(client);
9228c2ecf20Sopenharmony_ci	ssize_t len;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	mutex_lock(&ts->dev_mutex);
9258c2ecf20Sopenharmony_ci	len = scnprintf(buf, PAGE_SIZE, "%04x\n",
9268c2ecf20Sopenharmony_ci			be16_to_cpu(ts->dwz_info.release_ver));
9278c2ecf20Sopenharmony_ci	mutex_unlock(&ts->dev_mutex);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	return len;
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_cistatic ssize_t hideep_product_id_show(struct device *dev,
9338c2ecf20Sopenharmony_ci				      struct device_attribute *attr, char *buf)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
9368c2ecf20Sopenharmony_ci	struct hideep_ts *ts = i2c_get_clientdata(client);
9378c2ecf20Sopenharmony_ci	ssize_t len;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	mutex_lock(&ts->dev_mutex);
9408c2ecf20Sopenharmony_ci	len = scnprintf(buf, PAGE_SIZE, "%04x\n",
9418c2ecf20Sopenharmony_ci			be16_to_cpu(ts->dwz_info.product_id));
9428c2ecf20Sopenharmony_ci	mutex_unlock(&ts->dev_mutex);
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	return len;
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL);
9488c2ecf20Sopenharmony_cistatic DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL);
9498c2ecf20Sopenharmony_cistatic DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic struct attribute *hideep_ts_sysfs_entries[] = {
9528c2ecf20Sopenharmony_ci	&dev_attr_version.attr,
9538c2ecf20Sopenharmony_ci	&dev_attr_product_id.attr,
9548c2ecf20Sopenharmony_ci	&dev_attr_update_fw.attr,
9558c2ecf20Sopenharmony_ci	NULL,
9568c2ecf20Sopenharmony_ci};
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic const struct attribute_group hideep_ts_attr_group = {
9598c2ecf20Sopenharmony_ci	.attrs = hideep_ts_sysfs_entries,
9608c2ecf20Sopenharmony_ci};
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_cistatic int __maybe_unused hideep_suspend(struct device *dev)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
9658c2ecf20Sopenharmony_ci	struct hideep_ts *ts = i2c_get_clientdata(client);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	disable_irq(client->irq);
9688c2ecf20Sopenharmony_ci	hideep_power_off(ts);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	return 0;
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_cistatic int __maybe_unused hideep_resume(struct device *dev)
9748c2ecf20Sopenharmony_ci{
9758c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
9768c2ecf20Sopenharmony_ci	struct hideep_ts *ts = i2c_get_clientdata(client);
9778c2ecf20Sopenharmony_ci	int error;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	error = hideep_power_on(ts);
9808c2ecf20Sopenharmony_ci	if (error) {
9818c2ecf20Sopenharmony_ci		dev_err(&client->dev, "power on failed");
9828c2ecf20Sopenharmony_ci		return error;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	enable_irq(client->irq);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	return 0;
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic const struct regmap_config hideep_regmap_config = {
9938c2ecf20Sopenharmony_ci	.reg_bits = 16,
9948c2ecf20Sopenharmony_ci	.reg_format_endian = REGMAP_ENDIAN_LITTLE,
9958c2ecf20Sopenharmony_ci	.val_bits = 16,
9968c2ecf20Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_LITTLE,
9978c2ecf20Sopenharmony_ci	.max_register = 0xffff,
9988c2ecf20Sopenharmony_ci};
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_cistatic int hideep_probe(struct i2c_client *client,
10018c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
10028c2ecf20Sopenharmony_ci{
10038c2ecf20Sopenharmony_ci	struct hideep_ts *ts;
10048c2ecf20Sopenharmony_ci	int error;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	/* check i2c bus */
10078c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
10088c2ecf20Sopenharmony_ci		dev_err(&client->dev, "check i2c device error");
10098c2ecf20Sopenharmony_ci		return -ENODEV;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	if (client->irq <= 0) {
10138c2ecf20Sopenharmony_ci		dev_err(&client->dev, "missing irq: %d\n", client->irq);
10148c2ecf20Sopenharmony_ci		return -EINVAL;
10158c2ecf20Sopenharmony_ci	}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
10188c2ecf20Sopenharmony_ci	if (!ts)
10198c2ecf20Sopenharmony_ci		return -ENOMEM;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	ts->client = client;
10228c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, ts);
10238c2ecf20Sopenharmony_ci	mutex_init(&ts->dev_mutex);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	ts->reg = devm_regmap_init_i2c(client, &hideep_regmap_config);
10268c2ecf20Sopenharmony_ci	if (IS_ERR(ts->reg)) {
10278c2ecf20Sopenharmony_ci		error = PTR_ERR(ts->reg);
10288c2ecf20Sopenharmony_ci		dev_err(&client->dev,
10298c2ecf20Sopenharmony_ci			"failed to initialize regmap: %d\n", error);
10308c2ecf20Sopenharmony_ci		return error;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	ts->vcc_vdd = devm_regulator_get(&client->dev, "vdd");
10348c2ecf20Sopenharmony_ci	if (IS_ERR(ts->vcc_vdd))
10358c2ecf20Sopenharmony_ci		return PTR_ERR(ts->vcc_vdd);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	ts->vcc_vid = devm_regulator_get(&client->dev, "vid");
10388c2ecf20Sopenharmony_ci	if (IS_ERR(ts->vcc_vid))
10398c2ecf20Sopenharmony_ci		return PTR_ERR(ts->vcc_vid);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	ts->reset_gpio = devm_gpiod_get_optional(&client->dev,
10428c2ecf20Sopenharmony_ci						 "reset", GPIOD_OUT_HIGH);
10438c2ecf20Sopenharmony_ci	if (IS_ERR(ts->reset_gpio))
10448c2ecf20Sopenharmony_ci		return PTR_ERR(ts->reset_gpio);
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	error = hideep_power_on(ts);
10478c2ecf20Sopenharmony_ci	if (error) {
10488c2ecf20Sopenharmony_ci		dev_err(&client->dev, "power on failed: %d\n", error);
10498c2ecf20Sopenharmony_ci		return error;
10508c2ecf20Sopenharmony_ci	}
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	error = devm_add_action_or_reset(&client->dev, hideep_power_off, ts);
10538c2ecf20Sopenharmony_ci	if (error)
10548c2ecf20Sopenharmony_ci		return error;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	error = hideep_load_dwz(ts);
10578c2ecf20Sopenharmony_ci	if (error) {
10588c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to load dwz: %d", error);
10598c2ecf20Sopenharmony_ci		return error;
10608c2ecf20Sopenharmony_ci	}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	error = hideep_init_input(ts);
10638c2ecf20Sopenharmony_ci	if (error)
10648c2ecf20Sopenharmony_ci		return error;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	error = devm_request_threaded_irq(&client->dev, client->irq,
10678c2ecf20Sopenharmony_ci					  NULL, hideep_irq, IRQF_ONESHOT,
10688c2ecf20Sopenharmony_ci					  client->name, ts);
10698c2ecf20Sopenharmony_ci	if (error) {
10708c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to request irq %d: %d\n",
10718c2ecf20Sopenharmony_ci			client->irq, error);
10728c2ecf20Sopenharmony_ci		return error;
10738c2ecf20Sopenharmony_ci	}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	error = devm_device_add_group(&client->dev, &hideep_ts_attr_group);
10768c2ecf20Sopenharmony_ci	if (error) {
10778c2ecf20Sopenharmony_ci		dev_err(&client->dev,
10788c2ecf20Sopenharmony_ci			"failed to add sysfs attributes: %d\n", error);
10798c2ecf20Sopenharmony_ci		return error;
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	return 0;
10838c2ecf20Sopenharmony_ci}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_cistatic const struct i2c_device_id hideep_i2c_id[] = {
10868c2ecf20Sopenharmony_ci	{ HIDEEP_I2C_NAME, 0 },
10878c2ecf20Sopenharmony_ci	{ }
10888c2ecf20Sopenharmony_ci};
10898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, hideep_i2c_id);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
10928c2ecf20Sopenharmony_cistatic const struct acpi_device_id hideep_acpi_id[] = {
10938c2ecf20Sopenharmony_ci	{ "HIDP0001", 0 },
10948c2ecf20Sopenharmony_ci	{ }
10958c2ecf20Sopenharmony_ci};
10968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, hideep_acpi_id);
10978c2ecf20Sopenharmony_ci#endif
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
11008c2ecf20Sopenharmony_cistatic const struct of_device_id hideep_match_table[] = {
11018c2ecf20Sopenharmony_ci	{ .compatible = "hideep,hideep-ts" },
11028c2ecf20Sopenharmony_ci	{ }
11038c2ecf20Sopenharmony_ci};
11048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hideep_match_table);
11058c2ecf20Sopenharmony_ci#endif
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_cistatic struct i2c_driver hideep_driver = {
11088c2ecf20Sopenharmony_ci	.driver = {
11098c2ecf20Sopenharmony_ci		.name			= HIDEEP_I2C_NAME,
11108c2ecf20Sopenharmony_ci		.of_match_table		= of_match_ptr(hideep_match_table),
11118c2ecf20Sopenharmony_ci		.acpi_match_table	= ACPI_PTR(hideep_acpi_id),
11128c2ecf20Sopenharmony_ci		.pm			= &hideep_pm_ops,
11138c2ecf20Sopenharmony_ci	},
11148c2ecf20Sopenharmony_ci	.id_table	= hideep_i2c_id,
11158c2ecf20Sopenharmony_ci	.probe		= hideep_probe,
11168c2ecf20Sopenharmony_ci};
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_cimodule_i2c_driver(hideep_driver);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller");
11218c2ecf20Sopenharmony_ciMODULE_AUTHOR("anthony.kim@hideep.com");
11228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1123