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