1119 lines · 25.4 KB · C
RawDownload
/* * Copyright (C) 2012-2017 Hideep, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 * as published by the Free Software Foudation. */ #include <linux/module.h>#include <linux/of.h>#include <linux/firmware.h>#include <linux/delay.h>#include <linux/gpio/consumer.h>#include <linux/i2c.h>#include <linux/acpi.h>#include <linux/interrupt.h>#include <linux/regmap.h>#include <linux/sysfs.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/input/touchscreen.h>#include <linux/regulator/consumer.h>#include <asm/unaligned.h> #define HIDEEP_TS_NAME			"HiDeep Touchscreen"#define HIDEEP_I2C_NAME			"hideep_ts" #define HIDEEP_MT_MAX			10#define HIDEEP_KEY_MAX			3 /* count(2) + touch data(100) + key data(6) */#define HIDEEP_MAX_EVENT		108UL #define HIDEEP_TOUCH_EVENT_INDEX	2#define HIDEEP_KEY_EVENT_INDEX		102 /* Touch & key event */#define HIDEEP_EVENT_ADDR		0x240 /* command list */#define HIDEEP_RESET_CMD		0x9800 /* event bit */#define HIDEEP_MT_RELEASED		BIT(4)#define HIDEEP_KEY_PRESSED		BIT(7)#define HIDEEP_KEY_FIRST_PRESSED	BIT(8)#define HIDEEP_KEY_PRESSED_MASK		(HIDEEP_KEY_PRESSED | \					 HIDEEP_KEY_FIRST_PRESSED) #define HIDEEP_KEY_IDX_MASK		0x0f /* For NVM */#define HIDEEP_YRAM_BASE		0x40000000#define HIDEEP_PERIPHERAL_BASE		0x50000000#define HIDEEP_ESI_BASE			(HIDEEP_PERIPHERAL_BASE + 0x00000000)#define HIDEEP_FLASH_BASE		(HIDEEP_PERIPHERAL_BASE + 0x01000000)#define HIDEEP_SYSCON_BASE		(HIDEEP_PERIPHERAL_BASE + 0x02000000) #define HIDEEP_SYSCON_MOD_CON		(HIDEEP_SYSCON_BASE + 0x0000)#define HIDEEP_SYSCON_SPC_CON		(HIDEEP_SYSCON_BASE + 0x0004)#define HIDEEP_SYSCON_CLK_CON		(HIDEEP_SYSCON_BASE + 0x0008)#define HIDEEP_SYSCON_CLK_ENA		(HIDEEP_SYSCON_BASE + 0x000C)#define HIDEEP_SYSCON_RST_CON		(HIDEEP_SYSCON_BASE + 0x0010)#define HIDEEP_SYSCON_WDT_CON		(HIDEEP_SYSCON_BASE + 0x0014)#define HIDEEP_SYSCON_WDT_CNT		(HIDEEP_SYSCON_BASE + 0x0018)#define HIDEEP_SYSCON_PWR_CON		(HIDEEP_SYSCON_BASE + 0x0020)#define HIDEEP_SYSCON_PGM_ID		(HIDEEP_SYSCON_BASE + 0x00F4) #define HIDEEP_FLASH_CON		(HIDEEP_FLASH_BASE + 0x0000)#define HIDEEP_FLASH_STA		(HIDEEP_FLASH_BASE + 0x0004)#define HIDEEP_FLASH_CFG		(HIDEEP_FLASH_BASE + 0x0008)#define HIDEEP_FLASH_TIM		(HIDEEP_FLASH_BASE + 0x000C)#define HIDEEP_FLASH_CACHE_CFG		(HIDEEP_FLASH_BASE + 0x0010)#define HIDEEP_FLASH_PIO_SIG		(HIDEEP_FLASH_BASE + 0x400000) #define HIDEEP_ESI_TX_INVALID		(HIDEEP_ESI_BASE + 0x0008) #define HIDEEP_PERASE			0x00040000#define HIDEEP_WRONLY			0x00100000 #define HIDEEP_NVM_MASK_OFS		0x0000000C#define HIDEEP_NVM_DEFAULT_PAGE		0#define HIDEEP_NVM_SFR_WPAGE		1#define HIDEEP_NVM_SFR_RPAGE		2 #define HIDEEP_PIO_SIG			0x00400000#define HIDEEP_PROT_MODE		0x03400000 #define HIDEEP_NVM_PAGE_SIZE		128 #define HIDEEP_DWZ_INFO			0x000002C0 struct hideep_event {	__le16 x;	__le16 y;	__le16 z;	u8 w;	u8 flag;	u8 type;	u8 index;}; struct dwz_info {	__be32 code_start;	u8 code_crc[12]; 	__be32 c_code_start;	__be16 gen_ver;	__be16 c_code_len; 	__be32 vr_start;	__be16 rsv0;	__be16 vr_len; 	__be32 ft_start;	__be16 vr_version;	__be16 ft_len; 	__be16 core_ver;	__be16 boot_ver; 	__be16 release_ver;	__be16 custom_ver; 	u8 factory_id;	u8 panel_type;	u8 model_name[6]; 	__be16 extra_option;	__be16 product_code; 	__be16 vendor_id;	__be16 product_id;}; struct pgm_packet {	struct {		u8 unused[3];		u8 len;		__be32 addr;	} header;	__be32 payload[HIDEEP_NVM_PAGE_SIZE / sizeof(__be32)];}; #define HIDEEP_XFER_BUF_SIZE	sizeof(struct pgm_packet) struct hideep_ts {	struct i2c_client *client;	struct input_dev *input_dev;	struct regmap *reg; 	struct touchscreen_properties prop; 	struct gpio_desc *reset_gpio; 	struct regulator *vcc_vdd;	struct regulator *vcc_vid; 	struct mutex dev_mutex; 	u32 tch_count;	u32 lpm_count; 	/*	 * Data buffer to read packet from the device (contacts and key	 * states). We align it on double-word boundary to keep word-sized	 * fields in contact data and double-word-sized fields in program	 * packet aligned.	 */	u8 xfer_buf[HIDEEP_XFER_BUF_SIZE] __aligned(4); 	int key_num;	u32 key_codes[HIDEEP_KEY_MAX]; 	struct dwz_info dwz_info; 	unsigned int fw_size;	u32 nvm_mask;}; static int hideep_pgm_w_mem(struct hideep_ts *ts, u32 addr,			    const __be32 *data, size_t count){	struct pgm_packet *packet = (void *)ts->xfer_buf;	size_t len = count * sizeof(*data);	struct i2c_msg msg = {		.addr	= ts->client->addr,		.len	= len + sizeof(packet->header.len) +				sizeof(packet->header.addr),		.buf	= &packet->header.len,	};	int ret; 	if (len > HIDEEP_NVM_PAGE_SIZE)		return -EINVAL; 	packet->header.len = 0x80 | (count - 1);	packet->header.addr = cpu_to_be32(addr);	memcpy(packet->payload, data, len); 	ret = i2c_transfer(ts->client->adapter, &msg, 1);	if (ret != 1)		return ret < 0 ? ret : -EIO; 	return 0;} static int hideep_pgm_r_mem(struct hideep_ts *ts, u32 addr,			    __be32 *data, size_t count){	struct pgm_packet *packet = (void *)ts->xfer_buf;	size_t len = count * sizeof(*data);	struct i2c_msg msg[] = {		{			.addr	= ts->client->addr,			.len	= sizeof(packet->header.len) +					sizeof(packet->header.addr),			.buf	= &packet->header.len,		},		{			.addr	= ts->client->addr,			.flags	= I2C_M_RD,			.len	= len,			.buf	= (u8 *)data,		},	};	int ret; 	if (len > HIDEEP_NVM_PAGE_SIZE)		return -EINVAL; 	packet->header.len = count - 1;	packet->header.addr = cpu_to_be32(addr); 	ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));	if (ret != ARRAY_SIZE(msg))		return ret < 0 ? ret : -EIO; 	return 0;} static int hideep_pgm_r_reg(struct hideep_ts *ts, u32 addr, u32 *val){	__be32 data;	int error; 	error = hideep_pgm_r_mem(ts, addr, &data, 1);	if (error) {		dev_err(&ts->client->dev,			"read of register %#08x failed: %d\n",			addr, error);		return error;	} 	*val = be32_to_cpu(data);	return 0;} static int hideep_pgm_w_reg(struct hideep_ts *ts, u32 addr, u32 val){	__be32 data = cpu_to_be32(val);	int error; 	error = hideep_pgm_w_mem(ts, addr, &data, 1);	if (error) {		dev_err(&ts->client->dev,			"write to register %#08x (%#08x) failed: %d\n",			addr, val, error);		return error;	} 	return 0;} #define SW_RESET_IN_PGM(clk)					\{								\	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CNT, (clk));	\	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x03);	\	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x01);	\} #define SET_FLASH_PIO(ce)					\	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON,			\			 0x01 | ((ce) << 1)) #define SET_PIO_SIG(x, y)					\	hideep_pgm_w_reg(ts, HIDEEP_FLASH_PIO_SIG + (x), (y)) #define SET_FLASH_HWCONTROL()					\	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x00) #define NVM_W_SFR(x, y)						\{								\	SET_FLASH_PIO(1);					\	SET_PIO_SIG(x, y);					\	SET_FLASH_PIO(0);					\} static void hideep_pgm_set(struct hideep_ts *ts){	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x00);	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_SPC_CON, 0x00);	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_ENA, 0xFF);	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_CON, 0x01);	hideep_pgm_w_reg(ts, HIDEEP_SYSCON_PWR_CON, 0x01);	hideep_pgm_w_reg(ts, HIDEEP_FLASH_TIM, 0x03);	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CACHE_CFG, 0x00);} static int hideep_pgm_get_pattern(struct hideep_ts *ts, u32 *pattern){	u16 p1 = 0xAF39;	u16 p2 = 0xDF9D;	int error; 	error = regmap_bulk_write(ts->reg, p1, &p2, 1);	if (error) {		dev_err(&ts->client->dev,			"%s: regmap_bulk_write() failed with %d\n",			__func__, error);		return error;	} 	usleep_range(1000, 1100); 	/* flush invalid Tx load register */	error = hideep_pgm_w_reg(ts, HIDEEP_ESI_TX_INVALID, 0x01);	if (error)		return error; 	error = hideep_pgm_r_reg(ts, HIDEEP_SYSCON_PGM_ID, pattern);	if (error)		return error; 	return 0;} static int hideep_enter_pgm(struct hideep_ts *ts){	int retry_count = 10;	u32 pattern;	int error; 	while (retry_count--) {		error = hideep_pgm_get_pattern(ts, &pattern);		if (error) {			dev_err(&ts->client->dev,				"hideep_pgm_get_pattern failed: %d\n", error);		} else if (pattern != 0x39AF9DDF) {			dev_err(&ts->client->dev, "%s: bad pattern: %#08x\n",				__func__, pattern);		} else {			dev_dbg(&ts->client->dev, "found magic code"); 			hideep_pgm_set(ts);			usleep_range(1000, 1100); 			return 0;		}	} 	dev_err(&ts->client->dev, "failed to  enter pgm mode\n");	SW_RESET_IN_PGM(1000);	return -EIO;} static void hideep_nvm_unlock(struct hideep_ts *ts){	u32 unmask_code; 	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_SFR_RPAGE);	hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code);	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_DEFAULT_PAGE); 	/* make it unprotected code */	unmask_code &= ~HIDEEP_PROT_MODE; 	/* compare unmask code */	if (unmask_code != ts->nvm_mask)		dev_warn(&ts->client->dev,			 "read mask code different %#08x vs %#08x",			 unmask_code, ts->nvm_mask); 	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_SFR_WPAGE);	SET_FLASH_PIO(0); 	NVM_W_SFR(HIDEEP_NVM_MASK_OFS, ts->nvm_mask);	SET_FLASH_HWCONTROL();	hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_DEFAULT_PAGE);} static int hideep_check_status(struct hideep_ts *ts){	int time_out = 100;	int status;	int error; 	while (time_out--) {		error = hideep_pgm_r_reg(ts, HIDEEP_FLASH_STA, &status);		if (!error && status)			return 0; 		usleep_range(1000, 1100);	} 	return -ETIMEDOUT;} static int hideep_program_page(struct hideep_ts *ts, u32 addr,			       const __be32 *ucode, size_t xfer_count){	u32 val;	int error; 	error = hideep_check_status(ts);	if (error)		return -EBUSY; 	addr &= ~(HIDEEP_NVM_PAGE_SIZE - 1); 	SET_FLASH_PIO(0);	SET_FLASH_PIO(1); 	/* erase page */	SET_PIO_SIG(HIDEEP_PERASE | addr, 0xFFFFFFFF); 	SET_FLASH_PIO(0); 	error = hideep_check_status(ts);	if (error)		return -EBUSY; 	/* write page */	SET_FLASH_PIO(1); 	val = be32_to_cpu(ucode[0]);	SET_PIO_SIG(HIDEEP_WRONLY | addr, val); 	hideep_pgm_w_mem(ts, HIDEEP_FLASH_PIO_SIG | HIDEEP_WRONLY,			 ucode, xfer_count); 	val = be32_to_cpu(ucode[xfer_count - 1]);	SET_PIO_SIG(124, val); 	SET_FLASH_PIO(0); 	usleep_range(1000, 1100); 	error = hideep_check_status(ts);	if (error)		return -EBUSY; 	SET_FLASH_HWCONTROL(); 	return 0;} static int hideep_program_nvm(struct hideep_ts *ts,			      const __be32 *ucode, size_t ucode_len){	struct pgm_packet *packet_r = (void *)ts->xfer_buf;	__be32 *current_ucode = packet_r->payload;	size_t xfer_len;	size_t xfer_count;	u32 addr = 0;	int error; 	hideep_nvm_unlock(ts); 	while (ucode_len > 0) {		xfer_len = min_t(size_t, ucode_len, HIDEEP_NVM_PAGE_SIZE);		xfer_count = xfer_len / sizeof(*ucode); 		error = hideep_pgm_r_mem(ts, 0x00000000 + addr,					 current_ucode, xfer_count);		if (error) {			dev_err(&ts->client->dev,				"%s: failed to read page at offset %#08x: %d\n",				__func__, addr, error);			return error;		} 		/* See if the page needs updating */		if (memcmp(ucode, current_ucode, xfer_len)) {			error = hideep_program_page(ts, addr,						    ucode, xfer_count);			if (error) {				dev_err(&ts->client->dev,					"%s: iwrite failure @%#08x: %d\n",					__func__, addr, error);				return error;			} 			usleep_range(1000, 1100);		} 		ucode += xfer_count;		addr += xfer_len;		ucode_len -= xfer_len;	}

Lines 1–500 of 1119.