18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Raydium touchscreen I2C driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012-2014, Raydium Semiconductor Corporation. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Raydium reserves the right to make changes without further notice 88c2ecf20Sopenharmony_ci * to the materials described herein. Raydium does not assume any 98c2ecf20Sopenharmony_ci * liability arising out of the application described herein. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Contact Raydium Semiconductor Corporation at www.rad-ic.com 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/acpi.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/firmware.h> 178c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/input.h> 208c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/of.h> 248c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Slave I2C mode */ 298c2ecf20Sopenharmony_ci#define RM_BOOT_BLDR 0x02 308c2ecf20Sopenharmony_ci#define RM_BOOT_MAIN 0x03 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* I2C bootoloader commands */ 338c2ecf20Sopenharmony_ci#define RM_CMD_BOOT_PAGE_WRT 0x0B /* send bl page write */ 348c2ecf20Sopenharmony_ci#define RM_CMD_BOOT_WRT 0x11 /* send bl write */ 358c2ecf20Sopenharmony_ci#define RM_CMD_BOOT_ACK 0x22 /* send ack*/ 368c2ecf20Sopenharmony_ci#define RM_CMD_BOOT_CHK 0x33 /* send data check */ 378c2ecf20Sopenharmony_ci#define RM_CMD_BOOT_READ 0x44 /* send wait bl data ready*/ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define RM_BOOT_RDY 0xFF /* bl data ready */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* I2C main commands */ 428c2ecf20Sopenharmony_ci#define RM_CMD_QUERY_BANK 0x2B 438c2ecf20Sopenharmony_ci#define RM_CMD_DATA_BANK 0x4D 448c2ecf20Sopenharmony_ci#define RM_CMD_ENTER_SLEEP 0x4E 458c2ecf20Sopenharmony_ci#define RM_CMD_BANK_SWITCH 0xAA 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define RM_RESET_MSG_ADDR 0x40000004 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define RM_MAX_READ_SIZE 56 508c2ecf20Sopenharmony_ci#define RM_PACKET_CRC_SIZE 2 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* Touch relative info */ 538c2ecf20Sopenharmony_ci#define RM_MAX_RETRIES 3 548c2ecf20Sopenharmony_ci#define RM_RETRY_DELAY_MS 20 558c2ecf20Sopenharmony_ci#define RM_MAX_TOUCH_NUM 10 568c2ecf20Sopenharmony_ci#define RM_BOOT_DELAY_MS 100 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Offsets in contact data */ 598c2ecf20Sopenharmony_ci#define RM_CONTACT_STATE_POS 0 608c2ecf20Sopenharmony_ci#define RM_CONTACT_X_POS 1 618c2ecf20Sopenharmony_ci#define RM_CONTACT_Y_POS 3 628c2ecf20Sopenharmony_ci#define RM_CONTACT_PRESSURE_POS 5 638c2ecf20Sopenharmony_ci#define RM_CONTACT_WIDTH_X_POS 6 648c2ecf20Sopenharmony_ci#define RM_CONTACT_WIDTH_Y_POS 7 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Bootloader relative info */ 678c2ecf20Sopenharmony_ci#define RM_BL_WRT_CMD_SIZE 3 /* bl flash wrt cmd size */ 688c2ecf20Sopenharmony_ci#define RM_BL_WRT_PKG_SIZE 32 /* bl wrt pkg size */ 698c2ecf20Sopenharmony_ci#define RM_BL_WRT_LEN (RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE) 708c2ecf20Sopenharmony_ci#define RM_FW_PAGE_SIZE 128 718c2ecf20Sopenharmony_ci#define RM_MAX_FW_RETRIES 30 728c2ecf20Sopenharmony_ci#define RM_MAX_FW_SIZE 0xD000 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define RM_POWERON_DELAY_USEC 500 758c2ecf20Sopenharmony_ci#define RM_RESET_DELAY_MSEC 50 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cienum raydium_bl_cmd { 788c2ecf20Sopenharmony_ci BL_HEADER = 0, 798c2ecf20Sopenharmony_ci BL_PAGE_STR, 808c2ecf20Sopenharmony_ci BL_PKG_IDX, 818c2ecf20Sopenharmony_ci BL_DATA_STR, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cienum raydium_bl_ack { 858c2ecf20Sopenharmony_ci RAYDIUM_ACK_NULL = 0, 868c2ecf20Sopenharmony_ci RAYDIUM_WAIT_READY, 878c2ecf20Sopenharmony_ci RAYDIUM_PATH_READY, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cienum raydium_boot_mode { 918c2ecf20Sopenharmony_ci RAYDIUM_TS_MAIN = 0, 928c2ecf20Sopenharmony_ci RAYDIUM_TS_BLDR, 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Response to RM_CMD_DATA_BANK request */ 968c2ecf20Sopenharmony_cistruct raydium_data_info { 978c2ecf20Sopenharmony_ci __le32 data_bank_addr; 988c2ecf20Sopenharmony_ci u8 pkg_size; 998c2ecf20Sopenharmony_ci u8 tp_info_size; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct raydium_info { 1038c2ecf20Sopenharmony_ci __le32 hw_ver; /*device version */ 1048c2ecf20Sopenharmony_ci u8 main_ver; 1058c2ecf20Sopenharmony_ci u8 sub_ver; 1068c2ecf20Sopenharmony_ci __le16 ft_ver; /* test version */ 1078c2ecf20Sopenharmony_ci u8 x_num; 1088c2ecf20Sopenharmony_ci u8 y_num; 1098c2ecf20Sopenharmony_ci __le16 x_max; 1108c2ecf20Sopenharmony_ci __le16 y_max; 1118c2ecf20Sopenharmony_ci u8 x_res; /* units/mm */ 1128c2ecf20Sopenharmony_ci u8 y_res; /* units/mm */ 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* struct raydium_data - represents state of Raydium touchscreen device */ 1168c2ecf20Sopenharmony_cistruct raydium_data { 1178c2ecf20Sopenharmony_ci struct i2c_client *client; 1188c2ecf20Sopenharmony_ci struct input_dev *input; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci struct regulator *avdd; 1218c2ecf20Sopenharmony_ci struct regulator *vccio; 1228c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci struct raydium_info info; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci struct mutex sysfs_mutex; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci u8 *report_data; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci u32 data_bank_addr; 1318c2ecf20Sopenharmony_ci u8 report_size; 1328c2ecf20Sopenharmony_ci u8 contact_size; 1338c2ecf20Sopenharmony_ci u8 pkg_size; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci enum raydium_boot_mode boot_mode; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci bool wake_irq_enabled; 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * Header to be sent for RM_CMD_BANK_SWITCH command. This is used by 1428c2ecf20Sopenharmony_ci * raydium_i2c_{read|send} below. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_cistruct __packed raydium_bank_switch_header { 1458c2ecf20Sopenharmony_ci u8 cmd; 1468c2ecf20Sopenharmony_ci __be32 be_addr; 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int raydium_i2c_xfer(struct i2c_client *client, u32 addr, 1508c2ecf20Sopenharmony_ci struct i2c_msg *xfer, size_t xfer_count) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int ret; 1538c2ecf20Sopenharmony_ci /* 1548c2ecf20Sopenharmony_ci * If address is greater than 255, then RM_CMD_BANK_SWITCH needs to be 1558c2ecf20Sopenharmony_ci * sent first. Else, skip the header i.e. xfer[0]. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci int xfer_start_idx = (addr > 0xff) ? 0 : 1; 1588c2ecf20Sopenharmony_ci xfer_count -= xfer_start_idx; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, &xfer[xfer_start_idx], xfer_count); 1618c2ecf20Sopenharmony_ci if (likely(ret == xfer_count)) 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return ret < 0 ? ret : -EIO; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int raydium_i2c_send(struct i2c_client *client, 1688c2ecf20Sopenharmony_ci u32 addr, const void *data, size_t len) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int tries = 0; 1718c2ecf20Sopenharmony_ci int error; 1728c2ecf20Sopenharmony_ci u8 *tx_buf; 1738c2ecf20Sopenharmony_ci u8 reg_addr = addr & 0xff; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci tx_buf = kmalloc(len + 1, GFP_KERNEL); 1768c2ecf20Sopenharmony_ci if (!tx_buf) 1778c2ecf20Sopenharmony_ci return -ENOMEM; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci tx_buf[0] = reg_addr; 1808c2ecf20Sopenharmony_ci memcpy(tx_buf + 1, data, len); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci do { 1838c2ecf20Sopenharmony_ci struct raydium_bank_switch_header header = { 1848c2ecf20Sopenharmony_ci .cmd = RM_CMD_BANK_SWITCH, 1858c2ecf20Sopenharmony_ci .be_addr = cpu_to_be32(addr), 1868c2ecf20Sopenharmony_ci }; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * Perform as a single i2c_transfer transaction to ensure that 1908c2ecf20Sopenharmony_ci * no other I2C transactions are initiated on the bus to any 1918c2ecf20Sopenharmony_ci * other device in between. Initiating transacations to other 1928c2ecf20Sopenharmony_ci * devices after RM_CMD_BANK_SWITCH is sent is known to cause 1938c2ecf20Sopenharmony_ci * issues. This is also why regmap infrastructure cannot be used 1948c2ecf20Sopenharmony_ci * for this driver. Regmap handles page(bank) switch and reads 1958c2ecf20Sopenharmony_ci * as separate i2c_transfer() operations. This can result in 1968c2ecf20Sopenharmony_ci * problems if the Raydium device is on a shared I2C bus. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci struct i2c_msg xfer[] = { 1998c2ecf20Sopenharmony_ci { 2008c2ecf20Sopenharmony_ci .addr = client->addr, 2018c2ecf20Sopenharmony_ci .len = sizeof(header), 2028c2ecf20Sopenharmony_ci .buf = (u8 *)&header, 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci { 2058c2ecf20Sopenharmony_ci .addr = client->addr, 2068c2ecf20Sopenharmony_ci .len = len + 1, 2078c2ecf20Sopenharmony_ci .buf = tx_buf, 2088c2ecf20Sopenharmony_ci }, 2098c2ecf20Sopenharmony_ci }; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci error = raydium_i2c_xfer(client, addr, xfer, ARRAY_SIZE(xfer)); 2128c2ecf20Sopenharmony_ci if (likely(!error)) 2138c2ecf20Sopenharmony_ci goto out; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci msleep(RM_RETRY_DELAY_MS); 2168c2ecf20Sopenharmony_ci } while (++tries < RM_MAX_RETRIES); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s failed: %d\n", __func__, error); 2198c2ecf20Sopenharmony_ciout: 2208c2ecf20Sopenharmony_ci kfree(tx_buf); 2218c2ecf20Sopenharmony_ci return error; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int raydium_i2c_read(struct i2c_client *client, 2258c2ecf20Sopenharmony_ci u32 addr, void *data, size_t len) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci int error; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci while (len) { 2308c2ecf20Sopenharmony_ci u8 reg_addr = addr & 0xff; 2318c2ecf20Sopenharmony_ci struct raydium_bank_switch_header header = { 2328c2ecf20Sopenharmony_ci .cmd = RM_CMD_BANK_SWITCH, 2338c2ecf20Sopenharmony_ci .be_addr = cpu_to_be32(addr), 2348c2ecf20Sopenharmony_ci }; 2358c2ecf20Sopenharmony_ci size_t xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * Perform as a single i2c_transfer transaction to ensure that 2398c2ecf20Sopenharmony_ci * no other I2C transactions are initiated on the bus to any 2408c2ecf20Sopenharmony_ci * other device in between. Initiating transacations to other 2418c2ecf20Sopenharmony_ci * devices after RM_CMD_BANK_SWITCH is sent is known to cause 2428c2ecf20Sopenharmony_ci * issues. This is also why regmap infrastructure cannot be used 2438c2ecf20Sopenharmony_ci * for this driver. Regmap handles page(bank) switch and writes 2448c2ecf20Sopenharmony_ci * as separate i2c_transfer() operations. This can result in 2458c2ecf20Sopenharmony_ci * problems if the Raydium device is on a shared I2C bus. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci struct i2c_msg xfer[] = { 2488c2ecf20Sopenharmony_ci { 2498c2ecf20Sopenharmony_ci .addr = client->addr, 2508c2ecf20Sopenharmony_ci .len = sizeof(header), 2518c2ecf20Sopenharmony_ci .buf = (u8 *)&header, 2528c2ecf20Sopenharmony_ci }, 2538c2ecf20Sopenharmony_ci { 2548c2ecf20Sopenharmony_ci .addr = client->addr, 2558c2ecf20Sopenharmony_ci .len = 1, 2568c2ecf20Sopenharmony_ci .buf = ®_addr, 2578c2ecf20Sopenharmony_ci }, 2588c2ecf20Sopenharmony_ci { 2598c2ecf20Sopenharmony_ci .addr = client->addr, 2608c2ecf20Sopenharmony_ci .len = xfer_len, 2618c2ecf20Sopenharmony_ci .buf = data, 2628c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci }; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci error = raydium_i2c_xfer(client, addr, xfer, ARRAY_SIZE(xfer)); 2678c2ecf20Sopenharmony_ci if (unlikely(error)) 2688c2ecf20Sopenharmony_ci return error; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci len -= xfer_len; 2718c2ecf20Sopenharmony_ci data += xfer_len; 2728c2ecf20Sopenharmony_ci addr += xfer_len; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int raydium_i2c_sw_reset(struct i2c_client *client) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci const u8 soft_rst_cmd = 0x01; 2818c2ecf20Sopenharmony_ci int error; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci error = raydium_i2c_send(client, RM_RESET_MSG_ADDR, &soft_rst_cmd, 2848c2ecf20Sopenharmony_ci sizeof(soft_rst_cmd)); 2858c2ecf20Sopenharmony_ci if (error) { 2868c2ecf20Sopenharmony_ci dev_err(&client->dev, "software reset failed: %d\n", error); 2878c2ecf20Sopenharmony_ci return error; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci msleep(RM_RESET_DELAY_MSEC); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int raydium_i2c_query_ts_info(struct raydium_data *ts) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct i2c_client *client = ts->client; 2988c2ecf20Sopenharmony_ci struct raydium_data_info data_info; 2998c2ecf20Sopenharmony_ci __le32 query_bank_addr; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci int error, retry_cnt; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) { 3048c2ecf20Sopenharmony_ci error = raydium_i2c_read(client, RM_CMD_DATA_BANK, 3058c2ecf20Sopenharmony_ci &data_info, sizeof(data_info)); 3068c2ecf20Sopenharmony_ci if (error) 3078c2ecf20Sopenharmony_ci continue; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * Warn user if we already allocated memory for reports and 3118c2ecf20Sopenharmony_ci * then the size changed (due to firmware update?) and keep 3128c2ecf20Sopenharmony_ci * old size instead. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci if (ts->report_data && ts->pkg_size != data_info.pkg_size) { 3158c2ecf20Sopenharmony_ci dev_warn(&client->dev, 3168c2ecf20Sopenharmony_ci "report size changes, was: %d, new: %d\n", 3178c2ecf20Sopenharmony_ci ts->pkg_size, data_info.pkg_size); 3188c2ecf20Sopenharmony_ci } else { 3198c2ecf20Sopenharmony_ci ts->pkg_size = data_info.pkg_size; 3208c2ecf20Sopenharmony_ci ts->report_size = ts->pkg_size - RM_PACKET_CRC_SIZE; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ts->contact_size = data_info.tp_info_size; 3248c2ecf20Sopenharmony_ci ts->data_bank_addr = le32_to_cpu(data_info.data_bank_addr); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci dev_dbg(&client->dev, 3278c2ecf20Sopenharmony_ci "data_bank_addr: %#08x, report_size: %d, contact_size: %d\n", 3288c2ecf20Sopenharmony_ci ts->data_bank_addr, ts->report_size, ts->contact_size); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci error = raydium_i2c_read(client, RM_CMD_QUERY_BANK, 3318c2ecf20Sopenharmony_ci &query_bank_addr, 3328c2ecf20Sopenharmony_ci sizeof(query_bank_addr)); 3338c2ecf20Sopenharmony_ci if (error) 3348c2ecf20Sopenharmony_ci continue; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci error = raydium_i2c_read(client, le32_to_cpu(query_bank_addr), 3378c2ecf20Sopenharmony_ci &ts->info, sizeof(ts->info)); 3388c2ecf20Sopenharmony_ci if (error) 3398c2ecf20Sopenharmony_ci continue; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to query device parameters: %d\n", error); 3458c2ecf20Sopenharmony_ci return error; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int raydium_i2c_check_fw_status(struct raydium_data *ts) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct i2c_client *client = ts->client; 3518c2ecf20Sopenharmony_ci static const u8 bl_ack = 0x62; 3528c2ecf20Sopenharmony_ci static const u8 main_ack = 0x66; 3538c2ecf20Sopenharmony_ci u8 buf[4]; 3548c2ecf20Sopenharmony_ci int error; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf)); 3578c2ecf20Sopenharmony_ci if (!error) { 3588c2ecf20Sopenharmony_ci if (buf[0] == bl_ack) 3598c2ecf20Sopenharmony_ci ts->boot_mode = RAYDIUM_TS_BLDR; 3608c2ecf20Sopenharmony_ci else if (buf[0] == main_ack) 3618c2ecf20Sopenharmony_ci ts->boot_mode = RAYDIUM_TS_MAIN; 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return error; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int raydium_i2c_initialize(struct raydium_data *ts) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct i2c_client *client = ts->client; 3718c2ecf20Sopenharmony_ci int error, retry_cnt; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) { 3748c2ecf20Sopenharmony_ci /* Wait for Hello packet */ 3758c2ecf20Sopenharmony_ci msleep(RM_BOOT_DELAY_MS); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci error = raydium_i2c_check_fw_status(ts); 3788c2ecf20Sopenharmony_ci if (error) { 3798c2ecf20Sopenharmony_ci dev_err(&client->dev, 3808c2ecf20Sopenharmony_ci "failed to read 'hello' packet: %d\n", error); 3818c2ecf20Sopenharmony_ci continue; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (ts->boot_mode == RAYDIUM_TS_BLDR || 3858c2ecf20Sopenharmony_ci ts->boot_mode == RAYDIUM_TS_MAIN) { 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (error) 3918c2ecf20Sopenharmony_ci ts->boot_mode = RAYDIUM_TS_BLDR; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (ts->boot_mode == RAYDIUM_TS_BLDR) { 3948c2ecf20Sopenharmony_ci ts->info.hw_ver = cpu_to_le32(0xffffffffUL); 3958c2ecf20Sopenharmony_ci ts->info.main_ver = 0xff; 3968c2ecf20Sopenharmony_ci ts->info.sub_ver = 0xff; 3978c2ecf20Sopenharmony_ci } else { 3988c2ecf20Sopenharmony_ci raydium_i2c_query_ts_info(ts); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return error; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int raydium_i2c_bl_chk_state(struct i2c_client *client, 4058c2ecf20Sopenharmony_ci enum raydium_bl_ack state) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 }; 4088c2ecf20Sopenharmony_ci u8 rbuf[sizeof(ack_ok)]; 4098c2ecf20Sopenharmony_ci u8 retry; 4108c2ecf20Sopenharmony_ci int error; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci for (retry = 0; retry < RM_MAX_FW_RETRIES; retry++) { 4138c2ecf20Sopenharmony_ci switch (state) { 4148c2ecf20Sopenharmony_ci case RAYDIUM_ACK_NULL: 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci case RAYDIUM_WAIT_READY: 4188c2ecf20Sopenharmony_ci error = raydium_i2c_read(client, RM_CMD_BOOT_CHK, 4198c2ecf20Sopenharmony_ci &rbuf[0], 1); 4208c2ecf20Sopenharmony_ci if (!error && rbuf[0] == RM_BOOT_RDY) 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci case RAYDIUM_PATH_READY: 4268c2ecf20Sopenharmony_ci error = raydium_i2c_read(client, RM_CMD_BOOT_CHK, 4278c2ecf20Sopenharmony_ci rbuf, sizeof(rbuf)); 4288c2ecf20Sopenharmony_ci if (!error && !memcmp(rbuf, ack_ok, sizeof(ack_ok))) 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s: invalid target state %d\n", 4358c2ecf20Sopenharmony_ci __func__, state); 4368c2ecf20Sopenharmony_ci return -EINVAL; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci msleep(20); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int raydium_i2c_write_object(struct i2c_client *client, 4468c2ecf20Sopenharmony_ci const void *data, size_t len, 4478c2ecf20Sopenharmony_ci enum raydium_bl_ack state) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci int error; 4508c2ecf20Sopenharmony_ci static const u8 cmd[] = { 0xFF, 0x39 }; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len); 4538c2ecf20Sopenharmony_ci if (error) { 4548c2ecf20Sopenharmony_ci dev_err(&client->dev, "WRT obj command failed: %d\n", 4558c2ecf20Sopenharmony_ci error); 4568c2ecf20Sopenharmony_ci return error; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, cmd, sizeof(cmd)); 4608c2ecf20Sopenharmony_ci if (error) { 4618c2ecf20Sopenharmony_ci dev_err(&client->dev, "Ack obj command failed: %d\n", error); 4628c2ecf20Sopenharmony_ci return error; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci error = raydium_i2c_bl_chk_state(client, state); 4668c2ecf20Sopenharmony_ci if (error) { 4678c2ecf20Sopenharmony_ci dev_err(&client->dev, "BL check state failed: %d\n", error); 4688c2ecf20Sopenharmony_ci return error; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int raydium_i2c_boot_trigger(struct i2c_client *client) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci static const u8 cmd[7][6] = { 4768c2ecf20Sopenharmony_ci { 0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7 }, 4778c2ecf20Sopenharmony_ci { 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 }, 4788c2ecf20Sopenharmony_ci { 0x08, 0x04, 0x09, 0x00, 0x50, 0x00 }, 4798c2ecf20Sopenharmony_ci { 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 }, 4808c2ecf20Sopenharmony_ci { 0x08, 0x0C, 0x09, 0x00, 0x50, 0x00 }, 4818c2ecf20Sopenharmony_ci { 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 }, 4828c2ecf20Sopenharmony_ci { 0x02, 0xA2, 0x00, 0x00, 0x00, 0x00 }, 4838c2ecf20Sopenharmony_ci }; 4848c2ecf20Sopenharmony_ci int i; 4858c2ecf20Sopenharmony_ci int error; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci for (i = 0; i < 7; i++) { 4888c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]), 4898c2ecf20Sopenharmony_ci RAYDIUM_WAIT_READY); 4908c2ecf20Sopenharmony_ci if (error) { 4918c2ecf20Sopenharmony_ci dev_err(&client->dev, 4928c2ecf20Sopenharmony_ci "boot trigger failed at step %d: %d\n", 4938c2ecf20Sopenharmony_ci i, error); 4948c2ecf20Sopenharmony_ci return error; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int raydium_i2c_fw_trigger(struct i2c_client *client) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci static const u8 cmd[5][11] = { 5048c2ecf20Sopenharmony_ci { 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0 }, 5058c2ecf20Sopenharmony_ci { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 }, 5068c2ecf20Sopenharmony_ci { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 }, 5078c2ecf20Sopenharmony_ci { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 }, 5088c2ecf20Sopenharmony_ci { 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 }, 5098c2ecf20Sopenharmony_ci }; 5108c2ecf20Sopenharmony_ci int i; 5118c2ecf20Sopenharmony_ci int error; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 5148c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]), 5158c2ecf20Sopenharmony_ci RAYDIUM_ACK_NULL); 5168c2ecf20Sopenharmony_ci if (error) { 5178c2ecf20Sopenharmony_ci dev_err(&client->dev, 5188c2ecf20Sopenharmony_ci "fw trigger failed at step %d: %d\n", 5198c2ecf20Sopenharmony_ci i, error); 5208c2ecf20Sopenharmony_ci return error; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return 0; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic int raydium_i2c_check_path(struct i2c_client *client) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci static const u8 cmd[] = { 0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00 }; 5308c2ecf20Sopenharmony_ci int error; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, cmd, sizeof(cmd), 5338c2ecf20Sopenharmony_ci RAYDIUM_PATH_READY); 5348c2ecf20Sopenharmony_ci if (error) { 5358c2ecf20Sopenharmony_ci dev_err(&client->dev, "check path command failed: %d\n", error); 5368c2ecf20Sopenharmony_ci return error; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic int raydium_i2c_enter_bl(struct i2c_client *client) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci static const u8 cal_cmd[] = { 0x00, 0x01, 0x52 }; 5458c2ecf20Sopenharmony_ci int error; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd), 5488c2ecf20Sopenharmony_ci RAYDIUM_ACK_NULL); 5498c2ecf20Sopenharmony_ci if (error) { 5508c2ecf20Sopenharmony_ci dev_err(&client->dev, "enter bl command failed: %d\n", error); 5518c2ecf20Sopenharmony_ci return error; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci msleep(RM_BOOT_DELAY_MS); 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int raydium_i2c_leave_bl(struct i2c_client *client) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci static const u8 leave_cmd[] = { 0x05, 0x00 }; 5618c2ecf20Sopenharmony_ci int error; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, leave_cmd, sizeof(leave_cmd), 5648c2ecf20Sopenharmony_ci RAYDIUM_ACK_NULL); 5658c2ecf20Sopenharmony_ci if (error) { 5668c2ecf20Sopenharmony_ci dev_err(&client->dev, "leave bl command failed: %d\n", error); 5678c2ecf20Sopenharmony_ci return error; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci msleep(RM_BOOT_DELAY_MS); 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int raydium_i2c_write_checksum(struct i2c_client *client, 5758c2ecf20Sopenharmony_ci size_t length, u16 checksum) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci u8 checksum_cmd[] = { 0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00 }; 5788c2ecf20Sopenharmony_ci int error; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci put_unaligned_le16(length, &checksum_cmd[3]); 5818c2ecf20Sopenharmony_ci put_unaligned_le16(checksum, &checksum_cmd[5]); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, 5848c2ecf20Sopenharmony_ci checksum_cmd, sizeof(checksum_cmd), 5858c2ecf20Sopenharmony_ci RAYDIUM_ACK_NULL); 5868c2ecf20Sopenharmony_ci if (error) { 5878c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to write checksum: %d\n", 5888c2ecf20Sopenharmony_ci error); 5898c2ecf20Sopenharmony_ci return error; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic int raydium_i2c_disable_watch_dog(struct i2c_client *client) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci static const u8 cmd[] = { 0x0A, 0xAA }; 5988c2ecf20Sopenharmony_ci int error; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, cmd, sizeof(cmd), 6018c2ecf20Sopenharmony_ci RAYDIUM_WAIT_READY); 6028c2ecf20Sopenharmony_ci if (error) { 6038c2ecf20Sopenharmony_ci dev_err(&client->dev, "disable watchdog command failed: %d\n", 6048c2ecf20Sopenharmony_ci error); 6058c2ecf20Sopenharmony_ci return error; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return 0; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic int raydium_i2c_fw_write_page(struct i2c_client *client, 6128c2ecf20Sopenharmony_ci u16 page_idx, const void *data, size_t len) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci u8 buf[RM_BL_WRT_LEN]; 6158c2ecf20Sopenharmony_ci size_t xfer_len; 6168c2ecf20Sopenharmony_ci int error; 6178c2ecf20Sopenharmony_ci int i; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci BUILD_BUG_ON((RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE) != 0); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci for (i = 0; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE; i++) { 6228c2ecf20Sopenharmony_ci buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT; 6238c2ecf20Sopenharmony_ci buf[BL_PAGE_STR] = page_idx ? 0xff : 0; 6248c2ecf20Sopenharmony_ci buf[BL_PKG_IDX] = i + 1; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE); 6278c2ecf20Sopenharmony_ci memcpy(&buf[BL_DATA_STR], data, xfer_len); 6288c2ecf20Sopenharmony_ci if (len < RM_BL_WRT_PKG_SIZE) 6298c2ecf20Sopenharmony_ci memset(&buf[BL_DATA_STR + xfer_len], 0xff, 6308c2ecf20Sopenharmony_ci RM_BL_WRT_PKG_SIZE - xfer_len); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN, 6338c2ecf20Sopenharmony_ci RAYDIUM_WAIT_READY); 6348c2ecf20Sopenharmony_ci if (error) { 6358c2ecf20Sopenharmony_ci dev_err(&client->dev, 6368c2ecf20Sopenharmony_ci "page write command failed for page %d, chunk %d: %d\n", 6378c2ecf20Sopenharmony_ci page_idx, i, error); 6388c2ecf20Sopenharmony_ci return error; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci data += xfer_len; 6428c2ecf20Sopenharmony_ci len -= xfer_len; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return error; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic u16 raydium_calc_chksum(const u8 *buf, u16 len) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci u16 checksum = 0; 6518c2ecf20Sopenharmony_ci u16 i; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 6548c2ecf20Sopenharmony_ci checksum += buf[i]; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return checksum; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic int raydium_i2c_do_update_firmware(struct raydium_data *ts, 6608c2ecf20Sopenharmony_ci const struct firmware *fw) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct i2c_client *client = ts->client; 6638c2ecf20Sopenharmony_ci const void *data; 6648c2ecf20Sopenharmony_ci size_t data_len; 6658c2ecf20Sopenharmony_ci size_t len; 6668c2ecf20Sopenharmony_ci int page_nr; 6678c2ecf20Sopenharmony_ci int i; 6688c2ecf20Sopenharmony_ci int error; 6698c2ecf20Sopenharmony_ci u16 fw_checksum; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (fw->size == 0 || fw->size > RM_MAX_FW_SIZE) { 6728c2ecf20Sopenharmony_ci dev_err(&client->dev, "Invalid firmware length\n"); 6738c2ecf20Sopenharmony_ci return -EINVAL; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci error = raydium_i2c_check_fw_status(ts); 6778c2ecf20Sopenharmony_ci if (error) { 6788c2ecf20Sopenharmony_ci dev_err(&client->dev, "Unable to access IC %d\n", error); 6798c2ecf20Sopenharmony_ci return error; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (ts->boot_mode == RAYDIUM_TS_MAIN) { 6838c2ecf20Sopenharmony_ci for (i = 0; i < RM_MAX_RETRIES; i++) { 6848c2ecf20Sopenharmony_ci error = raydium_i2c_enter_bl(client); 6858c2ecf20Sopenharmony_ci if (!error) { 6868c2ecf20Sopenharmony_ci error = raydium_i2c_check_fw_status(ts); 6878c2ecf20Sopenharmony_ci if (error) { 6888c2ecf20Sopenharmony_ci dev_err(&client->dev, 6898c2ecf20Sopenharmony_ci "unable to access IC: %d\n", 6908c2ecf20Sopenharmony_ci error); 6918c2ecf20Sopenharmony_ci return error; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (ts->boot_mode == RAYDIUM_TS_BLDR) 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (ts->boot_mode == RAYDIUM_TS_MAIN) { 7008c2ecf20Sopenharmony_ci dev_err(&client->dev, 7018c2ecf20Sopenharmony_ci "failed to jump to boot loader: %d\n", 7028c2ecf20Sopenharmony_ci error); 7038c2ecf20Sopenharmony_ci return -EIO; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci error = raydium_i2c_disable_watch_dog(client); 7088c2ecf20Sopenharmony_ci if (error) 7098c2ecf20Sopenharmony_ci return error; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci error = raydium_i2c_check_path(client); 7128c2ecf20Sopenharmony_ci if (error) 7138c2ecf20Sopenharmony_ci return error; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci error = raydium_i2c_boot_trigger(client); 7168c2ecf20Sopenharmony_ci if (error) { 7178c2ecf20Sopenharmony_ci dev_err(&client->dev, "send boot trigger fail: %d\n", error); 7188c2ecf20Sopenharmony_ci return error; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci msleep(RM_BOOT_DELAY_MS); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci data = fw->data; 7248c2ecf20Sopenharmony_ci data_len = fw->size; 7258c2ecf20Sopenharmony_ci page_nr = 0; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci while (data_len) { 7288c2ecf20Sopenharmony_ci len = min_t(size_t, data_len, RM_FW_PAGE_SIZE); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci error = raydium_i2c_fw_write_page(client, page_nr++, data, len); 7318c2ecf20Sopenharmony_ci if (error) 7328c2ecf20Sopenharmony_ci return error; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci msleep(20); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci data += len; 7378c2ecf20Sopenharmony_ci data_len -= len; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci error = raydium_i2c_leave_bl(client); 7418c2ecf20Sopenharmony_ci if (error) { 7428c2ecf20Sopenharmony_ci dev_err(&client->dev, 7438c2ecf20Sopenharmony_ci "failed to leave boot loader: %d\n", error); 7448c2ecf20Sopenharmony_ci return error; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "left boot loader mode\n"); 7488c2ecf20Sopenharmony_ci msleep(RM_BOOT_DELAY_MS); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci error = raydium_i2c_check_fw_status(ts); 7518c2ecf20Sopenharmony_ci if (error) { 7528c2ecf20Sopenharmony_ci dev_err(&client->dev, 7538c2ecf20Sopenharmony_ci "failed to check fw status after write: %d\n", 7548c2ecf20Sopenharmony_ci error); 7558c2ecf20Sopenharmony_ci return error; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (ts->boot_mode != RAYDIUM_TS_MAIN) { 7598c2ecf20Sopenharmony_ci dev_err(&client->dev, 7608c2ecf20Sopenharmony_ci "failed to switch to main fw after writing firmware: %d\n", 7618c2ecf20Sopenharmony_ci error); 7628c2ecf20Sopenharmony_ci return -EINVAL; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci error = raydium_i2c_fw_trigger(client); 7668c2ecf20Sopenharmony_ci if (error) { 7678c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to trigger fw: %d\n", error); 7688c2ecf20Sopenharmony_ci return error; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci fw_checksum = raydium_calc_chksum(fw->data, fw->size); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci error = raydium_i2c_write_checksum(client, fw->size, fw_checksum); 7748c2ecf20Sopenharmony_ci if (error) 7758c2ecf20Sopenharmony_ci return error; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic int raydium_i2c_fw_update(struct raydium_data *ts) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci struct i2c_client *client = ts->client; 7838c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 7848c2ecf20Sopenharmony_ci char *fw_file; 7858c2ecf20Sopenharmony_ci int error; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci fw_file = kasprintf(GFP_KERNEL, "raydium_%#04x.fw", 7888c2ecf20Sopenharmony_ci le32_to_cpu(ts->info.hw_ver)); 7898c2ecf20Sopenharmony_ci if (!fw_file) 7908c2ecf20Sopenharmony_ci return -ENOMEM; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "firmware name: %s\n", fw_file); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci error = request_firmware(&fw, fw_file, &client->dev); 7958c2ecf20Sopenharmony_ci if (error) { 7968c2ecf20Sopenharmony_ci dev_err(&client->dev, "Unable to open firmware %s\n", fw_file); 7978c2ecf20Sopenharmony_ci goto out_free_fw_file; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci disable_irq(client->irq); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci error = raydium_i2c_do_update_firmware(ts, fw); 8038c2ecf20Sopenharmony_ci if (error) { 8048c2ecf20Sopenharmony_ci dev_err(&client->dev, "firmware update failed: %d\n", error); 8058c2ecf20Sopenharmony_ci ts->boot_mode = RAYDIUM_TS_BLDR; 8068c2ecf20Sopenharmony_ci goto out_enable_irq; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci error = raydium_i2c_initialize(ts); 8108c2ecf20Sopenharmony_ci if (error) { 8118c2ecf20Sopenharmony_ci dev_err(&client->dev, 8128c2ecf20Sopenharmony_ci "failed to initialize device after firmware update: %d\n", 8138c2ecf20Sopenharmony_ci error); 8148c2ecf20Sopenharmony_ci ts->boot_mode = RAYDIUM_TS_BLDR; 8158c2ecf20Sopenharmony_ci goto out_enable_irq; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci ts->boot_mode = RAYDIUM_TS_MAIN; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ciout_enable_irq: 8218c2ecf20Sopenharmony_ci enable_irq(client->irq); 8228c2ecf20Sopenharmony_ci msleep(100); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci release_firmware(fw); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ciout_free_fw_file: 8278c2ecf20Sopenharmony_ci kfree(fw_file); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci return error; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic void raydium_mt_event(struct raydium_data *ts) 8338c2ecf20Sopenharmony_ci{ 8348c2ecf20Sopenharmony_ci int i; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci for (i = 0; i < ts->report_size / ts->contact_size; i++) { 8378c2ecf20Sopenharmony_ci u8 *contact = &ts->report_data[ts->contact_size * i]; 8388c2ecf20Sopenharmony_ci bool state = contact[RM_CONTACT_STATE_POS]; 8398c2ecf20Sopenharmony_ci u8 wx, wy; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci input_mt_slot(ts->input, i); 8428c2ecf20Sopenharmony_ci input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (!state) 8458c2ecf20Sopenharmony_ci continue; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci input_report_abs(ts->input, ABS_MT_POSITION_X, 8488c2ecf20Sopenharmony_ci get_unaligned_le16(&contact[RM_CONTACT_X_POS])); 8498c2ecf20Sopenharmony_ci input_report_abs(ts->input, ABS_MT_POSITION_Y, 8508c2ecf20Sopenharmony_ci get_unaligned_le16(&contact[RM_CONTACT_Y_POS])); 8518c2ecf20Sopenharmony_ci input_report_abs(ts->input, ABS_MT_PRESSURE, 8528c2ecf20Sopenharmony_ci contact[RM_CONTACT_PRESSURE_POS]); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci wx = contact[RM_CONTACT_WIDTH_X_POS]; 8558c2ecf20Sopenharmony_ci wy = contact[RM_CONTACT_WIDTH_Y_POS]; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy)); 8588c2ecf20Sopenharmony_ci input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy)); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci input_mt_sync_frame(ts->input); 8628c2ecf20Sopenharmony_ci input_sync(ts->input); 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic irqreturn_t raydium_i2c_irq(int irq, void *_dev) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct raydium_data *ts = _dev; 8688c2ecf20Sopenharmony_ci int error; 8698c2ecf20Sopenharmony_ci u16 fw_crc; 8708c2ecf20Sopenharmony_ci u16 calc_crc; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (ts->boot_mode != RAYDIUM_TS_MAIN) 8738c2ecf20Sopenharmony_ci goto out; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci error = raydium_i2c_read(ts->client, ts->data_bank_addr, 8768c2ecf20Sopenharmony_ci ts->report_data, ts->pkg_size); 8778c2ecf20Sopenharmony_ci if (error) 8788c2ecf20Sopenharmony_ci goto out; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci fw_crc = get_unaligned_le16(&ts->report_data[ts->report_size]); 8818c2ecf20Sopenharmony_ci calc_crc = raydium_calc_chksum(ts->report_data, ts->report_size); 8828c2ecf20Sopenharmony_ci if (unlikely(fw_crc != calc_crc)) { 8838c2ecf20Sopenharmony_ci dev_warn(&ts->client->dev, 8848c2ecf20Sopenharmony_ci "%s: invalid packet crc %#04x vs %#04x\n", 8858c2ecf20Sopenharmony_ci __func__, calc_crc, fw_crc); 8868c2ecf20Sopenharmony_ci goto out; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci raydium_mt_event(ts); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ciout: 8928c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic ssize_t raydium_i2c_fw_ver_show(struct device *dev, 8968c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 8998c2ecf20Sopenharmony_ci struct raydium_data *ts = i2c_get_clientdata(client); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver); 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic ssize_t raydium_i2c_hw_ver_show(struct device *dev, 9058c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 9088c2ecf20Sopenharmony_ci struct raydium_data *ts = i2c_get_clientdata(client); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return sprintf(buf, "%#04x\n", le32_to_cpu(ts->info.hw_ver)); 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic ssize_t raydium_i2c_boot_mode_show(struct device *dev, 9148c2ecf20Sopenharmony_ci struct device_attribute *attr, 9158c2ecf20Sopenharmony_ci char *buf) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 9188c2ecf20Sopenharmony_ci struct raydium_data *ts = i2c_get_clientdata(client); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", 9218c2ecf20Sopenharmony_ci ts->boot_mode == RAYDIUM_TS_MAIN ? 9228c2ecf20Sopenharmony_ci "Normal" : "Recovery"); 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic ssize_t raydium_i2c_update_fw_store(struct device *dev, 9268c2ecf20Sopenharmony_ci struct device_attribute *attr, 9278c2ecf20Sopenharmony_ci const char *buf, size_t count) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 9308c2ecf20Sopenharmony_ci struct raydium_data *ts = i2c_get_clientdata(client); 9318c2ecf20Sopenharmony_ci int error; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci error = mutex_lock_interruptible(&ts->sysfs_mutex); 9348c2ecf20Sopenharmony_ci if (error) 9358c2ecf20Sopenharmony_ci return error; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci error = raydium_i2c_fw_update(ts); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci mutex_unlock(&ts->sysfs_mutex); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci return error ?: count; 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic ssize_t raydium_i2c_calibrate_store(struct device *dev, 9458c2ecf20Sopenharmony_ci struct device_attribute *attr, 9468c2ecf20Sopenharmony_ci const char *buf, size_t count) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 9498c2ecf20Sopenharmony_ci struct raydium_data *ts = i2c_get_clientdata(client); 9508c2ecf20Sopenharmony_ci static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E }; 9518c2ecf20Sopenharmony_ci int error; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci error = mutex_lock_interruptible(&ts->sysfs_mutex); 9548c2ecf20Sopenharmony_ci if (error) 9558c2ecf20Sopenharmony_ci return error; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd), 9588c2ecf20Sopenharmony_ci RAYDIUM_WAIT_READY); 9598c2ecf20Sopenharmony_ci if (error) 9608c2ecf20Sopenharmony_ci dev_err(&client->dev, "calibrate command failed: %d\n", error); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci mutex_unlock(&ts->sysfs_mutex); 9638c2ecf20Sopenharmony_ci return error ?: count; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL); 9678c2ecf20Sopenharmony_cistatic DEVICE_ATTR(hw_version, S_IRUGO, raydium_i2c_hw_ver_show, NULL); 9688c2ecf20Sopenharmony_cistatic DEVICE_ATTR(boot_mode, S_IRUGO, raydium_i2c_boot_mode_show, NULL); 9698c2ecf20Sopenharmony_cistatic DEVICE_ATTR(update_fw, S_IWUSR, NULL, raydium_i2c_update_fw_store); 9708c2ecf20Sopenharmony_cistatic DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_i2c_calibrate_store); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic struct attribute *raydium_i2c_attributes[] = { 9738c2ecf20Sopenharmony_ci &dev_attr_update_fw.attr, 9748c2ecf20Sopenharmony_ci &dev_attr_boot_mode.attr, 9758c2ecf20Sopenharmony_ci &dev_attr_fw_version.attr, 9768c2ecf20Sopenharmony_ci &dev_attr_hw_version.attr, 9778c2ecf20Sopenharmony_ci &dev_attr_calibrate.attr, 9788c2ecf20Sopenharmony_ci NULL 9798c2ecf20Sopenharmony_ci}; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic const struct attribute_group raydium_i2c_attribute_group = { 9828c2ecf20Sopenharmony_ci .attrs = raydium_i2c_attributes, 9838c2ecf20Sopenharmony_ci}; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int raydium_i2c_power_on(struct raydium_data *ts) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci int error; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (!ts->reset_gpio) 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ts->reset_gpio, 1); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci error = regulator_enable(ts->avdd); 9958c2ecf20Sopenharmony_ci if (error) { 9968c2ecf20Sopenharmony_ci dev_err(&ts->client->dev, 9978c2ecf20Sopenharmony_ci "failed to enable avdd regulator: %d\n", error); 9988c2ecf20Sopenharmony_ci goto release_reset_gpio; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci error = regulator_enable(ts->vccio); 10028c2ecf20Sopenharmony_ci if (error) { 10038c2ecf20Sopenharmony_ci regulator_disable(ts->avdd); 10048c2ecf20Sopenharmony_ci dev_err(&ts->client->dev, 10058c2ecf20Sopenharmony_ci "failed to enable vccio regulator: %d\n", error); 10068c2ecf20Sopenharmony_ci goto release_reset_gpio; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci udelay(RM_POWERON_DELAY_USEC); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cirelease_reset_gpio: 10128c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ts->reset_gpio, 0); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci if (error) 10158c2ecf20Sopenharmony_ci return error; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci msleep(RM_RESET_DELAY_MSEC); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci return 0; 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic void raydium_i2c_power_off(void *_data) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct raydium_data *ts = _data; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (ts->reset_gpio) { 10278c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ts->reset_gpio, 1); 10288c2ecf20Sopenharmony_ci regulator_disable(ts->vccio); 10298c2ecf20Sopenharmony_ci regulator_disable(ts->avdd); 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic int raydium_i2c_probe(struct i2c_client *client, 10348c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci union i2c_smbus_data dummy; 10378c2ecf20Sopenharmony_ci struct raydium_data *ts; 10388c2ecf20Sopenharmony_ci int error; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 10418c2ecf20Sopenharmony_ci dev_err(&client->dev, 10428c2ecf20Sopenharmony_ci "i2c check functionality error (need I2C_FUNC_I2C)\n"); 10438c2ecf20Sopenharmony_ci return -ENXIO; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); 10478c2ecf20Sopenharmony_ci if (!ts) 10488c2ecf20Sopenharmony_ci return -ENOMEM; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci mutex_init(&ts->sysfs_mutex); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci ts->client = client; 10538c2ecf20Sopenharmony_ci i2c_set_clientdata(client, ts); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci ts->avdd = devm_regulator_get(&client->dev, "avdd"); 10568c2ecf20Sopenharmony_ci if (IS_ERR(ts->avdd)) { 10578c2ecf20Sopenharmony_ci error = PTR_ERR(ts->avdd); 10588c2ecf20Sopenharmony_ci if (error != -EPROBE_DEFER) 10598c2ecf20Sopenharmony_ci dev_err(&client->dev, 10608c2ecf20Sopenharmony_ci "Failed to get 'avdd' regulator: %d\n", error); 10618c2ecf20Sopenharmony_ci return error; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci ts->vccio = devm_regulator_get(&client->dev, "vccio"); 10658c2ecf20Sopenharmony_ci if (IS_ERR(ts->vccio)) { 10668c2ecf20Sopenharmony_ci error = PTR_ERR(ts->vccio); 10678c2ecf20Sopenharmony_ci if (error != -EPROBE_DEFER) 10688c2ecf20Sopenharmony_ci dev_err(&client->dev, 10698c2ecf20Sopenharmony_ci "Failed to get 'vccio' regulator: %d\n", error); 10708c2ecf20Sopenharmony_ci return error; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", 10748c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 10758c2ecf20Sopenharmony_ci if (IS_ERR(ts->reset_gpio)) { 10768c2ecf20Sopenharmony_ci error = PTR_ERR(ts->reset_gpio); 10778c2ecf20Sopenharmony_ci if (error != -EPROBE_DEFER) 10788c2ecf20Sopenharmony_ci dev_err(&client->dev, 10798c2ecf20Sopenharmony_ci "failed to get reset gpio: %d\n", error); 10808c2ecf20Sopenharmony_ci return error; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci error = raydium_i2c_power_on(ts); 10848c2ecf20Sopenharmony_ci if (error) 10858c2ecf20Sopenharmony_ci return error; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci error = devm_add_action(&client->dev, raydium_i2c_power_off, ts); 10888c2ecf20Sopenharmony_ci if (error) { 10898c2ecf20Sopenharmony_ci dev_err(&client->dev, 10908c2ecf20Sopenharmony_ci "failed to install power off action: %d\n", error); 10918c2ecf20Sopenharmony_ci raydium_i2c_power_off(ts); 10928c2ecf20Sopenharmony_ci return error; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* Make sure there is something at this address */ 10968c2ecf20Sopenharmony_ci if (i2c_smbus_xfer(client->adapter, client->addr, 0, 10978c2ecf20Sopenharmony_ci I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) { 10988c2ecf20Sopenharmony_ci dev_err(&client->dev, "nothing at this address\n"); 10998c2ecf20Sopenharmony_ci return -ENXIO; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci error = raydium_i2c_initialize(ts); 11038c2ecf20Sopenharmony_ci if (error) { 11048c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to initialize: %d\n", error); 11058c2ecf20Sopenharmony_ci return error; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci ts->report_data = devm_kmalloc(&client->dev, 11098c2ecf20Sopenharmony_ci ts->pkg_size, GFP_KERNEL); 11108c2ecf20Sopenharmony_ci if (!ts->report_data) 11118c2ecf20Sopenharmony_ci return -ENOMEM; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci ts->input = devm_input_allocate_device(&client->dev); 11148c2ecf20Sopenharmony_ci if (!ts->input) { 11158c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to allocate input device\n"); 11168c2ecf20Sopenharmony_ci return -ENOMEM; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci ts->input->name = "Raydium Touchscreen"; 11208c2ecf20Sopenharmony_ci ts->input->id.bustype = BUS_I2C; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci input_set_abs_params(ts->input, ABS_MT_POSITION_X, 11238c2ecf20Sopenharmony_ci 0, le16_to_cpu(ts->info.x_max), 0, 0); 11248c2ecf20Sopenharmony_ci input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 11258c2ecf20Sopenharmony_ci 0, le16_to_cpu(ts->info.y_max), 0, 0); 11268c2ecf20Sopenharmony_ci input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res); 11278c2ecf20Sopenharmony_ci input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 11308c2ecf20Sopenharmony_ci input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci error = input_mt_init_slots(ts->input, RM_MAX_TOUCH_NUM, 11338c2ecf20Sopenharmony_ci INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 11348c2ecf20Sopenharmony_ci if (error) { 11358c2ecf20Sopenharmony_ci dev_err(&client->dev, 11368c2ecf20Sopenharmony_ci "failed to initialize MT slots: %d\n", error); 11378c2ecf20Sopenharmony_ci return error; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci error = input_register_device(ts->input); 11418c2ecf20Sopenharmony_ci if (error) { 11428c2ecf20Sopenharmony_ci dev_err(&client->dev, 11438c2ecf20Sopenharmony_ci "unable to register input device: %d\n", error); 11448c2ecf20Sopenharmony_ci return error; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci error = devm_request_threaded_irq(&client->dev, client->irq, 11488c2ecf20Sopenharmony_ci NULL, raydium_i2c_irq, 11498c2ecf20Sopenharmony_ci IRQF_ONESHOT, client->name, ts); 11508c2ecf20Sopenharmony_ci if (error) { 11518c2ecf20Sopenharmony_ci dev_err(&client->dev, "Failed to register interrupt\n"); 11528c2ecf20Sopenharmony_ci return error; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci error = devm_device_add_group(&client->dev, 11568c2ecf20Sopenharmony_ci &raydium_i2c_attribute_group); 11578c2ecf20Sopenharmony_ci if (error) { 11588c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to create sysfs attributes: %d\n", 11598c2ecf20Sopenharmony_ci error); 11608c2ecf20Sopenharmony_ci return error; 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic void __maybe_unused raydium_enter_sleep(struct i2c_client *client) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f }; 11698c2ecf20Sopenharmony_ci int error; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci error = raydium_i2c_send(client, RM_CMD_ENTER_SLEEP, 11728c2ecf20Sopenharmony_ci sleep_cmd, sizeof(sleep_cmd)); 11738c2ecf20Sopenharmony_ci if (error) 11748c2ecf20Sopenharmony_ci dev_err(&client->dev, 11758c2ecf20Sopenharmony_ci "sleep command failed: %d\n", error); 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic int __maybe_unused raydium_i2c_suspend(struct device *dev) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 11818c2ecf20Sopenharmony_ci struct raydium_data *ts = i2c_get_clientdata(client); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Sleep is not available in BLDR recovery mode */ 11848c2ecf20Sopenharmony_ci if (ts->boot_mode != RAYDIUM_TS_MAIN) 11858c2ecf20Sopenharmony_ci return -EBUSY; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci disable_irq(client->irq); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 11908c2ecf20Sopenharmony_ci raydium_enter_sleep(client); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0); 11938c2ecf20Sopenharmony_ci } else { 11948c2ecf20Sopenharmony_ci raydium_i2c_power_off(ts); 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci return 0; 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistatic int __maybe_unused raydium_i2c_resume(struct device *dev) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 12038c2ecf20Sopenharmony_ci struct raydium_data *ts = i2c_get_clientdata(client); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 12068c2ecf20Sopenharmony_ci if (ts->wake_irq_enabled) 12078c2ecf20Sopenharmony_ci disable_irq_wake(client->irq); 12088c2ecf20Sopenharmony_ci raydium_i2c_sw_reset(client); 12098c2ecf20Sopenharmony_ci } else { 12108c2ecf20Sopenharmony_ci raydium_i2c_power_on(ts); 12118c2ecf20Sopenharmony_ci raydium_i2c_initialize(ts); 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci enable_irq(client->irq); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci return 0; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops, 12208c2ecf20Sopenharmony_ci raydium_i2c_suspend, raydium_i2c_resume); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic const struct i2c_device_id raydium_i2c_id[] = { 12238c2ecf20Sopenharmony_ci { "raydium_i2c" , 0 }, 12248c2ecf20Sopenharmony_ci { "rm32380", 0 }, 12258c2ecf20Sopenharmony_ci { /* sentinel */ } 12268c2ecf20Sopenharmony_ci}; 12278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, raydium_i2c_id); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 12308c2ecf20Sopenharmony_cistatic const struct acpi_device_id raydium_acpi_id[] = { 12318c2ecf20Sopenharmony_ci { "RAYD0001", 0 }, 12328c2ecf20Sopenharmony_ci { /* sentinel */ } 12338c2ecf20Sopenharmony_ci}; 12348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, raydium_acpi_id); 12358c2ecf20Sopenharmony_ci#endif 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 12388c2ecf20Sopenharmony_cistatic const struct of_device_id raydium_of_match[] = { 12398c2ecf20Sopenharmony_ci { .compatible = "raydium,rm32380", }, 12408c2ecf20Sopenharmony_ci { /* sentinel */ } 12418c2ecf20Sopenharmony_ci}; 12428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, raydium_of_match); 12438c2ecf20Sopenharmony_ci#endif 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic struct i2c_driver raydium_i2c_driver = { 12468c2ecf20Sopenharmony_ci .probe = raydium_i2c_probe, 12478c2ecf20Sopenharmony_ci .id_table = raydium_i2c_id, 12488c2ecf20Sopenharmony_ci .driver = { 12498c2ecf20Sopenharmony_ci .name = "raydium_ts", 12508c2ecf20Sopenharmony_ci .pm = &raydium_i2c_pm_ops, 12518c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(raydium_acpi_id), 12528c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(raydium_of_match), 12538c2ecf20Sopenharmony_ci }, 12548c2ecf20Sopenharmony_ci}; 12558c2ecf20Sopenharmony_cimodule_i2c_driver(raydium_i2c_driver); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Raydium"); 12588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Raydium I2c Touchscreen driver"); 12598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1260